Merge branch 'jk/terse-push' into aw/mirror-push

* jk/terse-push:
  send-pack: segfault fix on forced push
  send-pack: require --verbose to show update of tracking refs
  receive-pack: don't mention successful updates
  more terse push output
diff --git a/Makefile b/Makefile
index 3ec1876..470e54a 100644
--- a/Makefile
+++ b/Makefile
@@ -210,7 +210,6 @@
 SCRIPT_SH = \
 	git-bisect.sh git-checkout.sh \
 	git-clean.sh git-clone.sh git-commit.sh \
-	git-ls-remote.sh \
 	git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
 	git-pull.sh git-rebase.sh git-rebase--interactive.sh \
 	git-repack.sh git-request-pull.sh \
@@ -345,6 +344,7 @@
 	builtin-log.o \
 	builtin-ls-files.o \
 	builtin-ls-tree.o \
+	builtin-ls-remote.o \
 	builtin-mailinfo.o \
 	builtin-mailsplit.o \
 	builtin-merge-base.o \
@@ -352,7 +352,6 @@
 	builtin-mv.o \
 	builtin-name-rev.o \
 	builtin-pack-objects.o \
-	builtin-peek-remote.o \
 	builtin-prune.o \
 	builtin-prune-packed.o \
 	builtin-push.o \
diff --git a/builtin-peek-remote.c b/builtin-ls-remote.c
similarity index 71%
rename from builtin-peek-remote.c
rename to builtin-ls-remote.c
index b4106f5..56f3f88 100644
--- a/builtin-peek-remote.c
+++ b/builtin-ls-remote.c
@@ -3,10 +3,10 @@
 #include "transport.h"
 #include "remote.h"
 
-static const char peek_remote_usage[] =
-"git-peek-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
+static const char ls_remote_usage[] =
+"git-ls-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
 
-int cmd_peek_remote(int argc, const char **argv, const char *prefix)
+int cmd_ls_remote(int argc, const char **argv, const char *prefix)
 {
 	int i;
 	const char *dest = NULL;
@@ -14,6 +14,7 @@
 	unsigned flags = 0;
 	const char *uploadpack = NULL;
 
+	struct remote *remote;
 	struct transport *transport;
 	const struct ref *ref;
 
@@ -43,16 +44,19 @@
 				flags |= REF_NORMAL;
 				continue;
 			}
-			usage(peek_remote_usage);
+			usage(ls_remote_usage);
 		}
 		dest = arg;
 		break;
 	}
 
 	if (!dest || i != argc - 1)
-		usage(peek_remote_usage);
+		usage(ls_remote_usage);
 
-	transport = transport_get(NULL, dest);
+	remote = nongit ? NULL : remote_get(dest);
+	if (remote && !remote->url_nr)
+		die("remote %s has no configured URL", dest);
+	transport = transport_get(remote, remote ? remote->url[0] : dest);
 	if (uploadpack != NULL)
 		transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
 
diff --git a/builtin.h b/builtin.h
index 2335c01..525107f 100644
--- a/builtin.h
+++ b/builtin.h
@@ -48,6 +48,7 @@
 extern int cmd_log_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 extern int cmd_merge_base(int argc, const char **argv, const char *prefix);
@@ -55,7 +56,6 @@
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
-extern int cmd_peek_remote(int argc, const char **argv, const char *prefix);
 extern int cmd_pickaxe(int argc, const char **argv, const char *prefix);
 extern int cmd_prune(int argc, const char **argv, const char *prefix);
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
diff --git a/git-ls-remote.sh b/contrib/examples/git-ls-remote.sh
similarity index 100%
rename from git-ls-remote.sh
rename to contrib/examples/git-ls-remote.sh
diff --git a/git.c b/git.c
index 19a2172..b173f22 100644
--- a/git.c
+++ b/git.c
@@ -326,6 +326,7 @@
 		{ "log", cmd_log, RUN_SETUP | USE_PAGER },
 		{ "ls-files", cmd_ls_files, RUN_SETUP },
 		{ "ls-tree", cmd_ls_tree, RUN_SETUP },
+		{ "ls-remote", cmd_ls_remote },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
 		{ "merge-base", cmd_merge_base, RUN_SETUP },
@@ -333,7 +334,7 @@
 		{ "mv", cmd_mv, RUN_SETUP | NEED_WORK_TREE },
 		{ "name-rev", cmd_name_rev, RUN_SETUP },
 		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
-		{ "peek-remote", cmd_peek_remote },
+		{ "peek-remote", cmd_ls_remote },
 		{ "pickaxe", cmd_blame, RUN_SETUP },
 		{ "prune", cmd_prune, RUN_SETUP },
 		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
new file mode 100755
index 0000000..6ec5f7c
--- /dev/null
+++ b/t/t5512-ls-remote.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='git ls-remote'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	>file &&
+	git add file &&
+	test_tick &&
+	git commit -m initial &&
+	git tag mark &&
+	git show-ref --tags -d | sed -e "s/ /	/" >expected.tag &&
+	(
+		echo "$(git rev-parse HEAD)	HEAD"
+		git show-ref -d	| sed -e "s/ /	/"
+	) >expected.all &&
+
+	git remote add self $(pwd)/.git
+
+'
+
+test_expect_success 'ls-remote --tags .git' '
+
+	git ls-remote --tags .git >actual &&
+	diff -u expected.tag actual
+
+'
+
+test_expect_success 'ls-remote .git' '
+
+	git ls-remote .git >actual &&
+	diff -u expected.all actual
+
+'
+
+test_expect_success 'ls-remote --tags self' '
+
+	git ls-remote --tags self >actual &&
+	diff -u expected.tag actual
+
+'
+
+test_expect_success 'ls-remote self' '
+
+	git ls-remote self >actual &&
+	diff -u expected.all actual
+
+'
+
+test_done
diff --git a/transport.c b/transport.c
index ab79f0c..f4577b7 100644
--- a/transport.c
+++ b/transport.c
@@ -6,6 +6,7 @@
 #endif
 #include "pkt-line.h"
 #include "fetch-pack.h"
+#include "send-pack.h"
 #include "walker.h"
 #include "bundle.h"
 #include "dir.h"
@@ -648,48 +649,16 @@
 
 static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
 	struct git_transport_data *data = transport->data;
-	const char **argv;
-	char *rem;
-	int argc;
-	int err;
+	struct send_pack_args args;
 
-	argv = xmalloc((refspec_nr + 11) * sizeof(char *));
-	argv[0] = "send-pack";
-	argc = 1;
-	if (flags & TRANSPORT_PUSH_ALL)
-		argv[argc++] = "--all";
-	if (flags & TRANSPORT_PUSH_FORCE)
-		argv[argc++] = "--force";
-	if (flags & TRANSPORT_PUSH_DRY_RUN)
-		argv[argc++] = "--dry-run";
-	if (data->receivepack) {
-		char *rp = xmalloc(strlen(data->receivepack) + 16);
-		sprintf(rp, "--receive-pack=%s", data->receivepack);
-		argv[argc++] = rp;
-	}
-	if (data->thin)
-		argv[argc++] = "--thin";
-	rem = xmalloc(strlen(transport->remote->name) + 10);
-	sprintf(rem, "--remote=%s", transport->remote->name);
-	argv[argc++] = rem;
-	argv[argc++] = transport->url;
-	while (refspec_nr--)
-		argv[argc++] = *refspec++;
-	argv[argc] = NULL;
-	err = run_command_v_opt(argv, RUN_GIT_CMD);
-	switch (err) {
-	case -ERR_RUN_COMMAND_FORK:
-		error("unable to fork for %s", argv[0]);
-	case -ERR_RUN_COMMAND_EXEC:
-		error("unable to exec %s", argv[0]);
-		break;
-	case -ERR_RUN_COMMAND_WAITPID:
-	case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
-	case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
-	case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
-		error("%s died with strange error", argv[0]);
-	}
-	return !!err;
+	args.receivepack = data->receivepack;
+	args.send_all = !!(flags & TRANSPORT_PUSH_ALL);
+	args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
+	args.use_thin_pack = data->thin;
+	args.verbose = transport->verbose;
+	args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
+
+	return send_pack(&args, transport->url, transport->remote, refspec_nr, refspec);
 }
 
 static int disconnect_git(struct transport *transport)