{"thread":{"id":"20103","subject":"[PATCH 0/3]: add git push --current and remote.*.pushHeadOnly","startedAt":"2009-07-13T23:07:39Z","lastAt":"2009-07-19T21:46:38Z","messageCount":6,"participants":["Paolo Bonzini","Nanako Shiraishi"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"117922","messageId":"1247526462-17584-1-git-send-email-bonzini@gnu.org","threadId":"20103","inReplyTo":null,"subject":"[PATCH 0/3]: add git push --current and remote.*.pushHeadOnly","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-13T23:07:39Z","receivedAt":"2009-07-13T23:07:39Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This second series is gets rid of the most annoying part\n(IMHO) of push.default = tracking, i.e. the fact that its\nbehavior cannot be achieved using git's ordinary tools.\nWhile autosetuppush is enough to set the refspecs correctly,\npush.tracking does not push _all_ tracked branches, but\nonly the current one (because it implicitly adds only\none refspec, while autosetuppush places them all in the\nconfiguration).\n\nWhat I introduce here is \"git push --current\" and a companion\nremote.*.pushHeadOnly option to make it the default.  The\ndifference between \"git push HEAD\" and \"git push --current\"\nis that the latter will still walk the remote.*.push refspecs,\nbut honor only the one matching HEAD.\n\nTogether with autosetuppush, this more or less achieves the\nsame result as push.tracking, at least for newly created\nremotes.  A subsequent series will handle the transition.\n\nPatch 1 is the meat of the implementation.  Patch 2 touches\none detail about how to handle \"git push --current\" when\nthe remote does not have a corresponding push refspec.\nPatch 3 adds remote.*.pushHeadOnly.\n\nPaolo Bonzini (3):\n push: add --current\n change default push refspec when --current is given\n push: add remote.*.pushHeadOnly configuration\n\n Documentation/config.txt   |    6 ++++\n Documentation/git-push.txt |   15 +++++++++-\n builtin-push.c             |   15 ++++++++--\n http-push.c                |   27 ++++++++++++++----\n remote.c                   |   42 ++++++++++++++++++++++++-----\n remote.h                   |    3 ++\n t/t5516-fetch-push.sh      |   64 ++++++++++++++++++++++++++++++++++++++++++++\n transport.c                |   22 ++++++++++++++-\n transport.h                |    1 +\n 9 files changed, 177 insertions(+), 18 deletions(-)\n"},{"id":"117924","messageId":"1247526462-17584-2-git-send-email-bonzini@gnu.org","threadId":"20103","inReplyTo":"1247526462-17584-1-git-send-email-bonzini@gnu.org","subject":"[PATCH 1/3] push: add --current","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-13T23:07:40Z","receivedAt":"2009-07-13T23:07:40Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This patch adds the --current option to git-push.  The option restricts\npushing to the current HEAD, even in the presence of wildcard refspecs\nin the configuration.  This achieves an effect similar to the \"tracking\"\nvalue of push.default, in that git push only pushes a subset of the\nentire push possibilities (the difference, of course, is that these\nare implicitly taken from remote.*.merge in the case of push.default =\ntracking).\n\nThe option does not make sense, and is thus disabled, if explicit refspecs\nare given on the command line.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n\tIf some of the tests seem dubious, please consider patch 2\n\tfirst.  Patch 2 changes some of the semantics to something\n\tthat I consider more intuitive, and adjusts the tests\n\tcorrespondingly.\n\n Documentation/git-push.txt |   11 ++++++++-\n builtin-push.c             |    5 +++-\n http-push.c                |   27 +++++++++++++++++-----\n remote.c                   |   40 +++++++++++++++++++++++++++------\n remote.h                   |    2 +\n t/t5516-fetch-push.sh      |   52 ++++++++++++++++++++++++++++++++++++++++++++\n transport.c                |   22 +++++++++++++++++-\n transport.h                |    1 +\n 8 files changed, 144 insertions(+), 16 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 2653388..0d6fcaa 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects\n SYNOPSIS\n --------\n [verse]\n-'git push' [--all | --mirror | --tags] [--dry-run] [--receive-pack=<git-receive-pack>]\n+'git push' [--all | --mirror | --tags] [--current] [--dry-run] [--receive-pack=<git-receive-pack>]\n \t   [--repo=<repository>] [-f | --force] [-v | --verbose]\n \t   [<repository> <refspec>...]\n \n@@ -71,6 +71,15 @@ nor in any Push line of the corresponding remotes file---see below).\n \tInstead of naming each ref to push, specifies that all\n \trefs under `$GIT_DIR/refs/heads/` be pushed.\n \n+--current::\n+\tIndependent of the other options, restrict pushing to the current\n+\tHEAD.\n+\n+\tRefspecs given in the configuration is still used to find the\n+\tdestination name of the current branch.  However, this option\n+\tcannot be specified if an explicit refspec is given on the\n+\tcommand line, because it would be useless and possibly confusing.\n+\n --mirror::\n \tInstead of naming each ref to push, specifies that all\n \trefs under `$GIT_DIR/refs/` (which includes but is not\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 0a0297f..8921d53 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -10,7 +10,7 @@\n #include \"parse-options.h\"\n \n static const char * const push_usage[] = {\n-\t\"git push [--all | --mirror] [--dry-run] [--porcelain] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]\",\n+\t\"git push [--all | --mirror] [--current] [--dry-run] [--porcelain] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]\",\n \tNULL,\n };\n \n@@ -199,6 +199,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_BIT( 0 , \"mirror\", &flags, \"mirror all refs\",\n \t\t\t    (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),\n \t\tOPT_BOOLEAN( 0 , \"tags\", &tags, \"push tags\"),\n+\t\tOPT_BIT( 0 , \"current\", &flags, \"push current HEAD only\", TRANSPORT_PUSH_CURRENT),\n \t\tOPT_BIT( 0 , \"dry-run\", &flags, \"dry run\", TRANSPORT_PUSH_DRY_RUN),\n \t\tOPT_BIT( 0,  \"porcelain\", &flags, \"machine-readable output\", TRANSPORT_PUSH_PORCELAIN),\n \t\tOPT_BIT('f', \"force\", &flags, \"force updates\", TRANSPORT_PUSH_FORCE),\n@@ -210,6 +211,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, options, push_usage, 0);\n \n+\tif ((argc > 1 || tags) && (flags & TRANSPORT_PUSH_CURRENT))\n+\t\treturn error (\"Cannot give --current together with --tags or a refspec.\");\n \tif (tags)\n \t\tadd_refspec(\"refs/tags/*\");\n \ndiff --git a/http-push.c b/http-push.c\nindex 00e83dc..9c93e91 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -14,7 +14,7 @@\n #include <expat.h>\n \n static const char http_push_usage[] =\n-\"git http-push [--all] [--dry-run] [--force] [--verbose] <remote> [<head>...]\\n\";\n+\"git http-push [--all] [--current] [--dry-run] [--force] [--verbose] <remote> [<head>...]\\n\";\n \n #ifndef XML_STATUS_OK\n enum XML_Status {\n@@ -75,7 +75,7 @@ static int aborted;\n static signed char remote_dir_exists[256];\n \n static int push_verbosely;\n-static int push_all = MATCH_REFS_NONE;\n+static int match_flags = MATCH_REFS_NONE;\n static int force_all;\n static int dry_run;\n \n@@ -1802,7 +1802,11 @@ int main(int argc, char **argv)\n \n \t\tif (*arg == '-') {\n \t\t\tif (!strcmp(arg, \"--all\")) {\n-\t\t\t\tpush_all = MATCH_REFS_ALL;\n+\t\t\t\tmatch_flags |= MATCH_REFS_ALL;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strcmp(arg, \"--current\")) {\n+\t\t\t\tmatch_flags |= MATCH_REFS_HEAD_ONLY;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--force\")) {\n@@ -1904,7 +1908,17 @@ int main(int argc, char **argv)\n \t\tfetch_indices();\n \n \t/* Get a list of all local and remote heads to validate refspecs */\n-\tlocal_refs = get_local_heads();\n+\tif (match_flags && MATCH_REFS_HEAD_ONLY) {\n+\t\tlocal_refs = get_current_head();\n+\t\tif (!local_refs) {\n+\t\t\tfprintf(stderr, \"--current specified with no current branch.\\n\");\n+\t\t\trc = -1;\n+\t\t\tgoto cleanup;\n+\t\t}\n+\t}\n+\telse\n+\t\tlocal_refs = get_local_heads();\n+\n \tfprintf(stderr, \"Fetching remote heads...\\n\");\n \tget_dav_remote_heads();\n \trun_request_queue();\n@@ -1919,7 +1933,7 @@ int main(int argc, char **argv)\n \n \t/* match them up */\n \tif (match_refs(local_refs, &remote_refs,\n-\t\t       nr_refspec, (const char **) refspec, push_all)) {\n+\t\t       nr_refspec, (const char **) refspec, match_flags)) {\n \t\trc = -1;\n \t\tgoto cleanup;\n \t}\n@@ -2005,7 +2019,8 @@ int main(int argc, char **argv)\n \t\told_sha1_hex = NULL;\n \t\tcommit_argv[1] = \"--objects\";\n \t\tcommit_argv[2] = new_sha1_hex;\n-\t\tif (!push_all && !is_null_sha1(ref->old_sha1)) {\n+\t\tif (!(match_flags & MATCH_REFS_ALL)\n+\t\t    && !is_null_sha1(ref->old_sha1)) {\n \t\t\told_sha1_hex = xmalloc(42);\n \t\t\tsprintf(old_sha1_hex, \"^%s\",\n \t\t\t\tsha1_to_hex(ref->old_sha1));\ndiff --git a/remote.c b/remote.c\nindex c3ada2d..b5bf9a6 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -990,7 +990,7 @@ static char *guess_ref(const char *name, struct ref *peer)\n \n static int match_explicit(struct ref *src, struct ref *dst,\n \t\t\t  struct ref ***dst_tail,\n-\t\t\t  struct refspec *rs)\n+\t\t\t  struct refspec *rs, int head_only)\n {\n \tstruct ref *matched_src, *matched_dst;\n \tint copy_src;\n@@ -1007,14 +1007,26 @@ static int match_explicit(struct ref *src, struct ref *dst,\n \t\tcopy_src = 1;\n \t\tbreak;\n \tcase 0:\n-\t\t/* The source could be in the get_sha1() format\n+\t\t/*\n+\t\t * The source could be in the get_sha1() format\n \t\t * not a reference name.  :refs/other is a\n \t\t * way to delete 'other' ref at the remote end.\n+\t\t * This case however could cause unwanted references\n+\t\t * to be stored in matched_dst->peer_ref for --current.\n+\t\t * In that case, all we can/want handle is HEAD.\n \t\t */\n-\t\tmatched_src = try_explicit_object_name(rs->src);\n+\t\tif (head_only) {\n+\t\t\tassert (!src->next);\n+\t\t\tif (strcmp(rs->src, \"HEAD\"))\n+\t\t\t\treturn 0;\n+\t\t\tmatched_src = src;\n+\t\t\tcopy_src = 1;\n+\t\t} else {\n+\t\t\tmatched_src = try_explicit_object_name(rs->src);\n+\t\t\tcopy_src = 0;\n+\t\t}\n \t\tif (!matched_src)\n \t\t\treturn error(\"src refspec %s does not match any.\", rs->src);\n-\t\tcopy_src = 0;\n \t\tbreak;\n \tdefault:\n \t\treturn error(\"src refspec %s matches more than one.\", rs->src);\n@@ -1068,11 +1080,11 @@ static int match_explicit(struct ref *src, struct ref *dst,\n \n static int match_explicit_refs(struct ref *src, struct ref *dst,\n \t\t\t       struct ref ***dst_tail, struct refspec *rs,\n-\t\t\t       int rs_nr)\n+\t\t\t       int rs_nr, int head_only)\n {\n \tint i, errs;\n \tfor (i = errs = 0; i < rs_nr; i++)\n-\t\terrs += match_explicit(src, dst, dst_tail, &rs[i]);\n+\t\terrs += match_explicit(src, dst, dst_tail, &rs[i], head_only);\n \treturn errs;\n }\n \n@@ -1118,6 +1130,7 @@ int match_refs(struct ref *src, struct ref **dst,\n \tstruct refspec *rs;\n \tint send_all = flags & MATCH_REFS_ALL;\n \tint send_mirror = flags & MATCH_REFS_MIRROR;\n+\tint head_only = flags & MATCH_REFS_HEAD_ONLY;\n \tint errs;\n \tstatic const char *default_refspec[] = { \":\", NULL };\n \tstruct ref **dst_tail = tail_ref(dst);\n@@ -1127,7 +1140,8 @@ int match_refs(struct ref *src, struct ref **dst,\n \t\trefspec = default_refspec;\n \t}\n \trs = parse_push_refspec(nr_refspec, (const char **) refspec);\n-\terrs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec);\n+\terrs = match_explicit_refs(src, *dst, &dst_tail, rs, nr_refspec,\n+\t\t\t\t   head_only);\n \n \t/* pick the remainder */\n \tfor ( ; src; src = src->next) {\n@@ -1527,6 +1541,18 @@ struct ref *get_local_heads(void)\n \treturn local_refs;\n }\n \n+struct ref *get_current_head(void)\n+{\n+\tstruct ref *local_refs = NULL, **local_tail = &local_refs;\n+\tstruct branch *branch = branch_get(NULL);\n+\tunsigned char sha1[20];\n+\tif (branch) {\n+\t\tget_sha1(branch->refname, sha1);\n+\t\tone_local_ref(branch->refname, sha1, 0, &local_tail);\n+\t}\n+\treturn local_refs;\n+}\n+\n struct ref *guess_remote_head(const struct ref *head,\n \t\t\t      const struct ref *refs,\n \t\t\t      int all)\ndiff --git a/remote.h b/remote.h\nindex 5db8420..8e5d5b4 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -137,6 +137,7 @@ enum match_refs_flags {\n \tMATCH_REFS_NONE\t\t= 0,\n \tMATCH_REFS_ALL \t\t= (1 << 0),\n \tMATCH_REFS_MIRROR\t= (1 << 1),\n+\tMATCH_REFS_HEAD_ONLY\t= (1 << 2),\n };\n \n /* Reporting of tracking info */\n@@ -144,6 +145,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs);\n int format_tracking_info(struct branch *branch, struct strbuf *sb);\n \n struct ref *get_local_heads(void);\n+struct ref *get_current_head(void);\n /*\n  * Find refs from a list which are likely to be pointed to by the given HEAD\n  * ref. If 'all' is false, returns the most likely ref; otherwise, returns a\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 2d2633f..3333ce9 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -586,4 +586,56 @@ test_expect_success 'push with branches containing #' '\n \tgit checkout master\n '\n \n+test_expect_success 'push --current fails on empty repository' '\n+\tgit init &&\n+\tmkdir b.git &&\n+\t(cd b.git && git init --bare) &&\n+\techo a > b &&\n+\tgit add b &&\n+\tgit commit -m a &&\n+\tgit checkout -b branch &&\n+\techo bb > b &&\n+\tgit add b &&\n+\tgit commit -m branch &&\n+\tgit checkout master &&\n+\t! git push --current b.git\n+'\n+\n+test_expect_success 'push --current succeeds when push is configured' '\n+\tgit config remote.bremote.url b.git &&\n+\tgit config remote.bremote.push refs/heads/master:refs/heads/master &&\n+\tgit push --current bremote &&\n+\ttest `git rev-parse master` = `cd b.git && git rev-parse master`\n+'\n+\n+test_expect_success 'push --current does nothing when current branch does not exist' '\n+\tgit checkout branch &&\n+\tgit push --current b.git 2>&1 | grep \"Everything up-to-date\" &&\n+\tgit push b.git branch\n+'\n+\n+test_expect_success 'push --current does not push other branches' '\n+\tgit checkout master &&\n+\techo aa > b &&\n+\tgit commit -m master2 b &&\n+\tgit checkout branch &&\n+\tgit push --current b.git 2>&1 | grep \"Everything up-to-date\" &&\n+\ttest `git rev-parse master^` = `cd b.git && git rev-parse master`\n+'\n+\n+test_expect_success 'push --current does update the current branches' '\n+\techo cc > b &&\n+\tgit commit -m branch2 b &&\n+\tgit checkout master &&\n+\tgit push --current b.git &&\n+\ttest `git rev-parse master` = `cd b.git && git rev-parse master` &&\n+\ttest `git rev-parse branch^` = `cd b.git && git rev-parse branch`\n+'\n+\n+test_expect_success 'push --current respects configuration' '\n+\tgit checkout branch &&\n+\tgit push --current bremote 2>&1 | grep \"Everything up-to-date\" &&\n+\ttest `git rev-parse branch^` = `cd b.git && git rev-parse branch`\n+'\n+\n test_done\ndiff --git a/transport.c b/transport.c\nindex de0d587..c7b6aaa 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -327,6 +327,11 @@ static int rsync_transport_push(struct transport *transport,\n \tif (flags & TRANSPORT_PUSH_ALL) {\n \t\tif (for_each_ref(write_one_ref, &temp_dir))\n \t\t\treturn -1;\n+\t} else if (flags & TRANSPORT_PUSH_CURRENT) {\n+\t\tstruct branch *branch = branch_get(NULL);\n+\t\tunsigned char sha1[20];\n+\t\tget_sha1(branch->name, sha1);\n+\t\twrite_one_ref(branch->name, sha1, 0, &temp_dir);\n \t} else if (write_refs_to_temp_dir(&temp_dir, refspec_nr, refspec))\n \t\treturn -1;\n \n@@ -406,6 +411,8 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons\n \targc = 1;\n \tif (flags & TRANSPORT_PUSH_ALL)\n \t\targv[argc++] = \"--all\";\n+\tif (flags & TRANSPORT_PUSH_CURRENT)\n+\t\targv[argc++] = \"--current\";\n \tif (flags & TRANSPORT_PUSH_FORCE)\n \t\targv[argc++] = \"--force\";\n \tif (flags & TRANSPORT_PUSH_DRY_RUN)\n@@ -1001,12 +1008,19 @@ int transport_push(struct transport *transport,\n {\n \tverify_remote_names(refspec_nr, refspec);\n \n+\tif (flags & TRANSPORT_PUSH_CURRENT) {\n+\t\tstruct branch *branch = branch_get(NULL);\n+\t\tif (!branch)\n+\t\t\treturn error(\"Tried to push current branch, but there \"\n+\t\t\t\t     \"is no current branch!\");\n+\t}\n+\n \tif (transport->push)\n \t\treturn transport->push(transport, refspec_nr, refspec, flags);\n \tif (transport->push_refs) {\n \t\tstruct ref *remote_refs =\n \t\t\ttransport->get_refs_list(transport, 1);\n-\t\tstruct ref *local_refs = get_local_heads();\n+\t\tstruct ref *local_refs;\n \t\tint match_flags = MATCH_REFS_NONE;\n \t\tint verbose = flags & TRANSPORT_PUSH_VERBOSE;\n \t\tint porcelain = flags & TRANSPORT_PUSH_PORCELAIN;\n@@ -1017,6 +1031,12 @@ int transport_push(struct transport *transport,\n \t\tif (flags & TRANSPORT_PUSH_MIRROR)\n \t\t\tmatch_flags |= MATCH_REFS_MIRROR;\n \n+\t\tif (flags & TRANSPORT_PUSH_CURRENT) {\n+\t\t\tlocal_refs = get_current_head();\n+\t\t\tmatch_flags |= MATCH_REFS_HEAD_ONLY;\n+\t\t} else\n+\t\t\tlocal_refs = get_local_heads();\n+\n \t\tif (match_refs(local_refs, &remote_refs,\n \t\t\t       refspec_nr, refspec, match_flags)) {\n \t\t\treturn -1;\ndiff --git a/transport.h b/transport.h\nindex 51b5397..62aa243 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -36,6 +36,7 @@ struct transport {\n #define TRANSPORT_PUSH_MIRROR 8\n #define TRANSPORT_PUSH_VERBOSE 16\n #define TRANSPORT_PUSH_PORCELAIN 32\n+#define TRANSPORT_PUSH_CURRENT 64\n \n /* Returns a transport suitable for the url */\n struct transport *transport_get(struct remote *, const char *);\n-- \n1.6.2.5\n"},{"id":"117923","messageId":"1247526462-17584-3-git-send-email-bonzini@gnu.org","threadId":"20103","inReplyTo":"1247526462-17584-1-git-send-email-bonzini@gnu.org","subject":"[PATCH 2/3] change default push refspec when --current is given","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-13T23:07:41Z","receivedAt":"2009-07-13T23:07:41Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This patch is separate because I find it a little more controversial.\nActually, after writing the tests I am pretty sure that it results\nin a more intuitive behavior, but keeping it separate also makes it\nclearer to see the effect of the patch (from the changes to the\ntestsuite).\n\nWith this patch, if there is no push.default specified I make the\ndefault push refspec \"HEAD\".  This conforms to the idea of pushing\nthe current branch only.\n\nThe main effect of the patch is that, in a normal configuration,\n\"git push --current FOO\" will *not* give an error when pushing to\nan empty destination.  Instead, it will obviously push the current\nbranch to the destination, with the same name.  I find this quite\nintuitive.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n\tActually, I'm pretty sure that the behavior after patch 1 is\n\tmore or less \"wrong\".\n\n\tThe reason why I split this patch is that I'm undecided whether\n\tto do something even more Draconian: when --current is used,\n\t*always* use a default refspec of HEAD, even when push.default\n\tis set.  I somehow felt this was more correct, yet I didn't do it\n\tfor three reasons.  Together, these were enough to discourage\n\tme:\n\n\t1) it would have been more complicated to document, even though\n\teasier to code;\n\n\t2) this only affects one case, i.e. pushing a new branch;\n\n\t3) I want to implement \"git remote add\n\t--push={matching,current,tracking}\" as the next step; this will\n\tautomatically a remote.*.push=HEAD line when appropriate.\n\n\tThis means that in practice nobody will see the difference\n\tassuming I get to (3) before 1.6.5 or something like that.\n\n Documentation/git-push.txt |    6 +++++-\n builtin-push.c             |    8 ++++++--\n t/t5516-fetch-push.sh      |   20 +++++++++-----------\n 3 files changed, 20 insertions(+), 14 deletions(-)\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 0d6fcaa..ce6133e 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -75,11 +75,15 @@ nor in any Push line of the corresponding remotes file---see below).\n \tIndependent of the other options, restrict pushing to the current\n \tHEAD.\n \n-\tRefspecs given in the configuration is still used to find the\n+\tRefspecs given in the configuration are still used to find the\n \tdestination name of the current branch.  However, this option\n \tcannot be specified if an explicit refspec is given on the\n \tcommand line, because it would be useless and possibly confusing.\n \n+\tAdditionally, if there is no refspec in the configuration and no\n+\t`push.default` configuration either, with this option git will\n+\tuse a default refspec of `HEAD` rather than `:`.\n+\n --mirror::\n \tInstead of naming each ref to push, specifies that all\n \trefs under `$GIT_DIR/refs/` (which includes but is not\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 8921d53..2911513 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -88,7 +88,6 @@ static void warn_unconfigured_push(void)\n \n static void setup_default_push_refspecs(void)\n {\n-\tgit_config(git_default_config, NULL);\n \tswitch (push_default) {\n \tcase PUSH_DEFAULT_UNSPECIFIED:\n \t\twarn_unconfigured_push();\n@@ -150,8 +149,13 @@ static int do_push(const char *repo, int flags)\n \t\tif (remote->push_refspec_nr) {\n \t\t\trefspec = remote->push_refspec;\n \t\t\trefspec_nr = remote->push_refspec_nr;\n-\t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR))\n+\t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR)) {\n+\t\t\tpush_default = (flags & TRANSPORT_PUSH_CURRENT\n+\t\t\t\t\t? PUSH_DEFAULT_CURRENT\n+\t\t\t\t\t: PUSH_DEFAULT_UNSPECIFIED);\n+\t\t\tgit_config(git_default_config, NULL);\n \t\t\tsetup_default_push_refspecs();\n+\t\t}\n \t}\n \terrs = 0;\n \tif (remote->pushurl_nr) {\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 3333ce9..bdc98ec 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -586,7 +586,7 @@ test_expect_success 'push with branches containing #' '\n \tgit checkout master\n '\n \n-test_expect_success 'push --current fails on empty repository' '\n+test_expect_success 'push --current succeeds on empty repository' '\n \tgit init &&\n \tmkdir b.git &&\n \t(cd b.git && git init --bare) &&\n@@ -598,20 +598,14 @@ test_expect_success 'push --current succeeds on empty repository' '\n \tgit add b &&\n \tgit commit -m branch &&\n \tgit checkout master &&\n-\t! git push --current b.git\n-'\n-\n-test_expect_success 'push --current succeeds when push is configured' '\n-\tgit config remote.bremote.url b.git &&\n-\tgit config remote.bremote.push refs/heads/master:refs/heads/master &&\n-\tgit push --current bremote &&\n+\tgit push --current b.git\n \ttest `git rev-parse master` = `cd b.git && git rev-parse master`\n '\n \n-test_expect_success 'push --current does nothing when current branch does not exist' '\n+test_expect_success 'push --current always creates current branch' '\n \tgit checkout branch &&\n-\tgit push --current b.git 2>&1 | grep \"Everything up-to-date\" &&\n-\tgit push b.git branch\n+\tgit push --current b.git &&\n+\ttest `git rev-parse branch` = `cd b.git && git rev-parse branch`\n '\n \n test_expect_success 'push --current does not push other branches' '\n@@ -633,6 +627,10 @@ test_expect_success 'push --current does update the current branches' '\n '\n \n test_expect_success 'push --current respects configuration' '\n+\tgit config remote.bremote.url b.git &&\n+\tgit config remote.bremote.push refs/heads/master:refs/heads/master2 &&\n+\tgit push --current bremote &&\n+\ttest `git rev-parse master` = `cd b.git && git rev-parse master2`\n \tgit checkout branch &&\n \tgit push --current bremote 2>&1 | grep \"Everything up-to-date\" &&\n \ttest `git rev-parse branch^` = `cd b.git && git rev-parse branch`\n-- \n1.6.2.5\n"},{"id":"117925","messageId":"1247526462-17584-4-git-send-email-bonzini@gnu.org","threadId":"20103","inReplyTo":"1247526462-17584-1-git-send-email-bonzini@gnu.org","subject":"[PATCH 3/3] push: add remote.*.pushHeadOnly configuration","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-13T23:07:42Z","receivedAt":"2009-07-13T23:07:42Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This patch adds a remote.*.pushHeadOnly configuration that automatically\nenables (when possible) the --current option to git push.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n Documentation/config.txt |    6 ++++++\n builtin-push.c           |    2 ++\n remote.c                 |    2 ++\n remote.h                 |    1 +\n t/t5516-fetch-push.sh    |   16 +++++++++++++++-\n 5 files changed, 26 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex cb6832b..4ab5593 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1359,6 +1359,12 @@ remote.<name>.uploadpack::\n \tThe default program to execute on the remote side when fetching.  See\n \toption \\--upload-pack of linkgit:git-fetch-pack[1].\n \n+remote.<name>.pushHeadOnly::\n+\tIf true, whenever `git push` is invoked without a refspec and\n+\tit will try pushing to this remote, `git push` will automatically\n+\tbehave as if the `\\--current` option was given on the command line.\n+\tIn other words, only the current branch is pushed to the remote.\n+\n remote.<name>.tagopt::\n \tSetting this value to \\--no-tags disables automatic tag following when\n \tfetching from remote <name>\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 2911513..fcecd14 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -127,6 +127,8 @@ static int do_push(const char *repo, int flags)\n \n \tif (remote->mirror)\n \t\tflags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);\n+\tif (remote->push_head_only && !refspec_nr)\n+\t\tflags |= TRANSPORT_PUSH_CURRENT;\n \n \tif ((flags & TRANSPORT_PUSH_ALL) && refspec) {\n \t\tif (!strcmp(*refspec, \"refs/tags/*\"))\ndiff --git a/remote.c b/remote.c\nindex b5bf9a6..d46dc0d 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -379,6 +379,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\tremote->mirror = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \".skipdefaultupdate\"))\n \t\tremote->skip_default_update = git_config_bool(key, value);\n+\telse if (!strcmp(subkey, \".pushheadonly\"))\n+\t\tremote->push_head_only = git_config_bool(key, value);\n \n \telse if (!strcmp(subkey, \".url\")) {\n \t\tconst char *v;\ndiff --git a/remote.h b/remote.h\nindex 8e5d5b4..b1e3e99 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -36,6 +36,7 @@ struct remote {\n \t * 2 to always fetch tags\n \t */\n \tint fetch_tags;\n+\tint push_head_only;\n \tint skip_default_update;\n \tint mirror;\n \ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex bdc98ec..eee9e00 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -631,9 +631,23 @@ test_expect_success 'push --current respects configuration' '\n \tgit config remote.bremote.push refs/heads/master:refs/heads/master2 &&\n \tgit push --current bremote &&\n \ttest `git rev-parse master` = `cd b.git && git rev-parse master2`\n+'\n+\n+test_expect_success 'remote.*.pushHeadOnly respects configuration' '\n+\techo xx > b &&\n+\tgit commit -mmaster3 b &&\n+\tgit config remote.bremote.pushHeadOnly true &&\n \tgit checkout branch &&\n-\tgit push --current bremote 2>&1 | grep \"Everything up-to-date\" &&\n+\tgit push bremote &&\n+\ttest `git rev-parse master^` = `cd b.git && git rev-parse master` &&\n \ttest `git rev-parse branch^` = `cd b.git && git rev-parse branch`\n '\n \n+test_expect_success 'remote.*.pushHeadOnly works' '\n+\tgit config --unset remote.bremote.push &&\n+\tgit push bremote &&\n+\ttest `git rev-parse master^` = `cd b.git && git rev-parse master` &&\n+\ttest `git rev-parse branch` = `cd b.git && git rev-parse branch`\n+'\n+\n test_done\n-- \n1.6.2.5\n"},{"id":"118271","messageId":"20090720063244.6117@nanako3.lavabit.com","threadId":"20103","inReplyTo":"1247526462-17584-2-git-send-email-bonzini@gnu.org","subject":"Re: [PATCH 1/3] push: add --current","fromName":"Nanako Shiraishi","fromEmail":"nanako3@lavabit.com","sentAt":"2009-07-19T21:32:44Z","receivedAt":"2009-07-19T21:32:44Z","isPatch":true,"sender":{"key":"nanako3@lavabit.com","avatar":"https://gravatar.com/avatar/3777b9e201c5883a62b1a6fdf7c53f2d712d1d80989146063ea861e33aad72a8?d=mp&s=160"},"body":"Quoting Paolo Bonzini <bonzini@gnu.org>:\n\n> diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\n> index 2653388..0d6fcaa 100644\n> --- a/Documentation/git-push.txt\n> +++ b/Documentation/git-push.txt\n> @@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects\n>  SYNOPSIS\n>  --------\n>  [verse]\n> -'git push' [--all | --mirror | --tags] [--dry-run] [--receive-pack=<git-receive-pack>]\n> +'git push' [--all | --mirror | --tags] [--current] [--dry-run] [--receive-pack=<git-receive-pack>]\n\nShouldn't this be \"[--all | --mirror | --tags | --current]\" instead? In other words, does it make sense to say \"git push --all --current\"?\n\n> @@ -71,6 +71,15 @@ nor in any Push line of the corresponding remotes file---see below).\n>  \tInstead of naming each ref to push, specifies that all\n>  \trefs under `$GIT_DIR/refs/heads/` be pushed.\n>  \n> +--current::\n> +\tIndependent of the other options, restrict pushing to the current\n> +\tHEAD.\n> +\n> +\tRefspecs given in the configuration is still used to find the\n> +\tdestination name of the current branch.  However, this option\n> +\tcannot be specified if an explicit refspec is given on the\n> +\tcommand line, because it would be useless and possibly confusing.\n> +\n\nThe second and subsequent paragraphs must be dedented, and the blank line between paragraphs should be replaced by a single line with '+' on it. You can find examples in the same file (e.g. description of <refspec>).\n\nInstead of correcting a grammatical error you made here in patch 1/3 with a later patch 2/3, please fix it here.\n\n> diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\n> index 2d2633f..3333ce9 100755\n> --- a/t/t5516-fetch-push.sh\n> +++ b/t/t5516-fetch-push.sh\n> @@ -586,4 +586,56 @@ test_expect_success 'push with branches containing #' '\n>  \tgit checkout master\n>  '\n>  \n> +test_expect_success 'push --current fails on empty repository' '\n> +\tgit init &&\n> +\tmkdir b.git &&\n> +\t(cd b.git && git init --bare) &&\n> +\techo a > b &&\n> +\tgit add b &&\n> +\tgit commit -m a &&\n> +\tgit checkout -b branch &&\n> +\techo bb > b &&\n> +\tgit add b &&\n> +\tgit commit -m branch &&\n> +\tgit checkout master &&\n> +\t! git push --current b.git\n> +'\n\nIf your final belief (which I happen to agree) is that\n\n% git push --current\n% git push --current origin\n% git push --current over.there.example.com:project.git\n\nshould work as expected (that is, they should push the current branch to the same name) from a repository without any special configuration, don't say \"push --current fails\" on the title as if you think it is the right thing to do.\n\nInstead, mark clearly that the code after this patch is still broken, like this:\n\n\ttest_expect_failure 'push --current into an empty repository' '\n\t\t...\n                git push --current b.git\n\t'\n\nand change expect_failure to expect_success in the later patch that fixes the breakage.\n\n> +test_expect_success 'push --current succeeds when push is configured' '\n> +\tgit config remote.bremote.url b.git &&\n> +\tgit config remote.bremote.push refs/heads/master:refs/heads/master &&\n> +\tgit push --current bremote &&\n> +\ttest `git rev-parse master` = `cd b.git && git rev-parse master`\n> +'\n\nThe style of the existing tests in the script isn't\n\n\ttest `git rev-parse master` = `cd b.git && git rev-parse master`\n\nbut is\n\n\ttest \"$(git rev-parse master)\" = \"$(cd b.git && git rev-parse master)\"\n\n-- \nNanako Shiraishi\nhttp://ivory.ap.teacup.com/nanako3/\n"},{"id":"118272","messageId":"4A63943E.80209@gnu.org","threadId":"20103","inReplyTo":"20090720063244.6117@nanako3.lavabit.com","subject":"Re: [PATCH 1/3] push: add --current","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-19T21:46:38Z","receivedAt":"2009-07-19T21:46:38Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"Thanks for the review!\n\n> If your final belief (which I happen to agree) is that\n> \n> % git push --current\n> % git push --current origin\n> % git push --current over.there.example.com:project.git\n> \n> should work as expected [...] mark clearly that the code after this patch is still broken\n\nOkay, will do.  I just wanted to present both possibilities and thought\nthat (succeeding) tests were the best way to illustrate the difference.\n For the final submission, now that I had a second opinion, I'll\nprobably just squash 1/3 and 2/3 together.\n\n> The style of the existing tests in the script isn't\n> \n> \ttest `git rev-parse master` = `cd b.git && git rev-parse master`\n> \n> but is\n> \n> \ttest \"$(git rev-parse master)\" = \"$(cd b.git && git rev-parse master)\"\n\nWill do.\n\nPaolo\n"}]}