{"thread":{"id":"36387","subject":"[PATCH v2 0/9] Introduce publish tracking branch","startedAt":"2014-04-10T19:04:37Z","lastAt":"2014-04-18T23:29:53Z","messageCount":34,"participants":["Felipe Contreras","Junio C Hamano","Ramkumar Ramachandra","Matthieu Moy","Jeff King"],"isPatch":true,"patchVersion":2,"patchTotal":9},"messages":[{"id":"238642","messageId":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":null,"subject":"[PATCH v2 0/9] Introduce publish tracking branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:37Z","receivedAt":"2014-04-10T19:04:37Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"As it has been discussed before, our support for triangular workflows is\nlacking, and the following patch series aims to improve that situation.\n\nWe have the concept of upstream branch (e.g. 'origin/master') which is to where\nour topic branches eventually should be merged to, so it makes sense that\n'git rebase' uses that as the destination, but most people would not push to\nsuch upstream branch, they would push to a publish branch\n(e.g. 'github/feature-a'). We could set our upstream to the place we push, and\n'git push' would be able to use that as default, and 'git branch --vv' would\nshow how ahead/behind we are in comparisson to that branch, but then\n'git rebase' (or 'git merge') would be using the wrong branch.\n\nThis patch series adds:\n\n 1) git push --set-publish\n 2) git branch --set-publish\n 3) git branch -vv # uses and shows the publish branch when configured\n 4) @{publish} and @{p} marks\n 5) branch.$name.{push,pushremote} configurations\n\nAfter this, it becomes much easier to track branches in a triangular workflow.\n\nThe publish branch is used instead of the upstream branch for tracking\ninformation in 'git branch --vv' and 'git status' if present, otherwise there\nare no changes (upstream is used).\n\n  master          e230c56 [origin/master, gh/master] Git 1.8.4\n* fc/publish      0a105fd [master, gh/fc/publish: ahead 1] branch: display publish branch\n  fc/branch/fast  177dcad [master, gh/fc/branch/fast] branch: reorganize verbose options\n  fc/trivial      f289b9a [master: ahead 7] branch: trivial style fix\n  fc/leaks        d101af4 [master: ahead 2] read-cache: plug a possible leak\n  stable          e230c56 Git 1.8.4\n\nChanges since v1:\n\n * Added @{publish} and @{p} marks\n\nFelipe Contreras (9):\n  push: trivial reorganization\n  Add concept of 'publish' branch\n  branch: allow configuring the publish branch\n  t: branch add publish branch tests\n  push: add --set-publish option\n  branch: display publish branch\n  sha1_name: cleanup interpret_branch_name()\n  sha1_name: simplify track finding\n  sha1_name: add support for @{publish} marks\n\n Documentation/git-branch.txt | 11 +++++++\n Documentation/git-push.txt   |  9 +++++-\n Documentation/revisions.txt  |  4 +++\n branch.c                     | 43 +++++++++++++++++++++++++\n branch.h                     |  2 ++\n builtin/branch.c             | 74 ++++++++++++++++++++++++++++++++++++++----\n builtin/push.c               | 52 +++++++++++++++++++-----------\n remote.c                     | 34 ++++++++++++++++----\n remote.h                     |  4 +++\n sha1_name.c                  | 62 ++++++++++++++++++++++--------------\n t/t1508-at-combinations.sh   |  5 +++\n t/t3200-branch.sh            | 76 ++++++++++++++++++++++++++++++++++++++++++++\n t/t5529-push-publish.sh      | 70 ++++++++++++++++++++++++++++++++++++++++\n t/t6040-tracking-info.sh     |  5 +--\n transport.c                  | 28 ++++++++++------\n transport.h                  |  1 +\n 16 files changed, 415 insertions(+), 65 deletions(-)\n create mode 100755 t/t5529-push-publish.sh\n\n-- \n1.9.1+fc1\n"},{"id":"238643","messageId":"1397156686-31349-2-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 1/9] push: trivial reorganization","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:38Z","receivedAt":"2014-04-10T19:04:38Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n builtin/push.c | 35 +++++++++++++++++++----------------\n 1 file changed, 19 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 0e50ddb..d10aefc 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -155,20 +155,11 @@ static NORETURN int die_push_simple(struct branch *branch, struct remote *remote\n \t    remote->name, branch->name, advice_maybe);\n }\n \n-static const char message_detached_head_die[] =\n-\tN_(\"You are not currently on a branch.\\n\"\n-\t   \"To push the history leading to the current (detached HEAD)\\n\"\n-\t   \"state now, use\\n\"\n-\t   \"\\n\"\n-\t   \"    git push %s HEAD:<name-of-remote-branch>\\n\");\n-\n static void setup_push_upstream(struct remote *remote, struct branch *branch,\n \t\t\t\tint triangular)\n {\n \tstruct strbuf refspec = STRBUF_INIT;\n \n-\tif (!branch)\n-\t\tdie(_(message_detached_head_die), remote->name);\n \tif (!branch->merge_nr || !branch->merge || !branch->remote_name)\n \t\tdie(_(\"The current branch %s has no upstream branch.\\n\"\n \t\t    \"To push the current branch and set the remote as upstream, use\\n\"\n@@ -198,8 +189,6 @@ static void setup_push_upstream(struct remote *remote, struct branch *branch,\n \n static void setup_push_current(struct remote *remote, struct branch *branch)\n {\n-\tif (!branch)\n-\t\tdie(_(message_detached_head_die), remote->name);\n \tadd_refspec(branch->name);\n }\n \n@@ -240,9 +229,23 @@ static int is_workflow_triangular(struct remote *remote)\n \treturn (fetch_remote && fetch_remote != remote);\n }\n \n-static void setup_default_push_refspecs(struct remote *remote)\n+static const char message_detached_head_die[] =\n+\tN_(\"You are not currently on a branch.\\n\"\n+\t   \"To push the history leading to the current (detached HEAD)\\n\"\n+\t   \"state now, use\\n\"\n+\t   \"\\n\"\n+\t   \"    git push %s HEAD:<name-of-remote-branch>\\n\");\n+\n+static struct branch *get_current_branch(struct remote *remote)\n {\n \tstruct branch *branch = branch_get(NULL);\n+\tif (!branch)\n+\t\tdie(_(message_detached_head_die), remote->name);\n+\treturn branch;\n+}\n+\n+static void setup_default_push_refspecs(struct remote *remote)\n+{\n \tint triangular = is_workflow_triangular(remote);\n \n \tswitch (push_default) {\n@@ -257,17 +260,17 @@ static void setup_default_push_refspecs(struct remote *remote)\n \n \tcase PUSH_DEFAULT_SIMPLE:\n \t\tif (triangular)\n-\t\t\tsetup_push_current(remote, branch);\n+\t\t\tsetup_push_current(remote, get_current_branch(remote));\n \t\telse\n-\t\t\tsetup_push_upstream(remote, branch, triangular);\n+\t\t\tsetup_push_upstream(remote, get_current_branch(remote), triangular);\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_UPSTREAM:\n-\t\tsetup_push_upstream(remote, branch, triangular);\n+\t\tsetup_push_upstream(remote, get_current_branch(remote), triangular);\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_CURRENT:\n-\t\tsetup_push_current(remote, branch);\n+\t\tsetup_push_current(remote, get_current_branch(remote));\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_NOTHING:\n-- \n1.9.1+fc1\n"},{"id":"238644","messageId":"1397156686-31349-3-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 2/9] Add concept of 'publish' branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:39Z","receivedAt":"2014-04-10T19:04:39Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"The upstream branch is:\n\n  branch.$name.remote\n  branch.$name.merge\n\nThe publish branch is:\n\n  branch.$name.pushremote\n  branch.$name.push\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n builtin/push.c | 19 +++++++++++++++----\n remote.c       | 34 ++++++++++++++++++++++++++++------\n remote.h       |  4 ++++\n 3 files changed, 47 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/push.c b/builtin/push.c\nindex d10aefc..a1fdc49 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -192,6 +192,20 @@ static void setup_push_current(struct remote *remote, struct branch *branch)\n \tadd_refspec(branch->name);\n }\n \n+static void setup_push_simple(struct remote *remote, struct branch *branch,\n+\t\tint triangular)\n+{\n+\tif (branch->push_name) {\n+\t\tstruct strbuf refspec = STRBUF_INIT;\n+\t\tstrbuf_addf(&refspec, \"%s:%s\", branch->name, branch->push_name);\n+\t\tadd_refspec(refspec.buf);\n+\t} else if (triangular) {\n+\t\tsetup_push_current(remote, branch);\n+\t} else {\n+\t\tsetup_push_upstream(remote, branch, triangular);\n+\t}\n+}\n+\n static char warn_unspecified_push_default_msg[] =\n N_(\"push.default is unset; its implicit value is changing in\\n\"\n    \"Git 2.0 from 'matching' to 'simple'. To squelch this message\\n\"\n@@ -259,10 +273,7 @@ static void setup_default_push_refspecs(struct remote *remote)\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_SIMPLE:\n-\t\tif (triangular)\n-\t\t\tsetup_push_current(remote, get_current_branch(remote));\n-\t\telse\n-\t\t\tsetup_push_upstream(remote, get_current_branch(remote), triangular);\n+\t\tsetup_push_simple(remote, get_current_branch(remote), triangular);\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_UPSTREAM:\ndiff --git a/remote.c b/remote.c\nindex 5f63d55..3437d1f 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -352,13 +352,17 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\t\texplicit_default_remote_name = 1;\n \t\t\t}\n \t\t} else if (!strcmp(subkey, \".pushremote\")) {\n+\t\t\tif (git_config_string(&branch->pushremote_name, key, value))\n+\t\t\t\treturn -1;\n \t\t\tif (branch == current_branch)\n-\t\t\t\tif (git_config_string(&branch_pushremote_name, key, value))\n-\t\t\t\t\treturn -1;\n+\t\t\t\tbranch_pushremote_name = xstrdup(branch->pushremote_name);\n \t\t} else if (!strcmp(subkey, \".merge\")) {\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n \t\t\tadd_merge(branch, xstrdup(value));\n+\t\t} else if (!strcmp(subkey, \".push\")) {\n+\t\t\tif (git_config_string(&branch->push_name, key, value))\n+\t\t\t\treturn -1;\n \t\t}\n \t\treturn 0;\n \t}\n@@ -1562,6 +1566,14 @@ struct branch *branch_get(const char *name)\n \t\t\t}\n \t\t}\n \t}\n+\tif (ret && ret->pushremote_name) {\n+\t\tstruct remote *pushremote;\n+\t\tpushremote = pushremote_get(ret->pushremote_name);\n+\t\tret->push.src = xstrdup(ret->push_name);\n+\t\tif (remote_find_tracking(pushremote, &ret->push)\n+\t\t    && !strcmp(ret->pushremote_name, \".\"))\n+\t\t\tret->push.dst = xstrdup(ret->push_name);\n+\t}\n \treturn ret;\n }\n \n@@ -1771,6 +1783,15 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)\n \treturn found;\n }\n \n+static char *get_base(struct branch *branch)\n+{\n+\tif (branch->push.dst)\n+\t\treturn branch->push.dst;\n+\tif (branch->merge && branch->merge[0] && branch->merge[0]->dst)\n+\t\treturn branch->merge[0]->dst;\n+\treturn NULL;\n+}\n+\n /*\n  * Compare a branch with its upstream, and save their differences (number\n  * of commits) in *num_ours and *num_theirs.\n@@ -1788,12 +1809,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)\n \tint rev_argc;\n \n \t/* Cannot stat unless we are marked to build on top of somebody else. */\n-\tif (!branch ||\n-\t    !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)\n+\tif (!branch)\n+\t\treturn 0;\n+\tbase = get_base(branch);\n+\tif (!base)\n \t\treturn 0;\n \n \t/* Cannot stat if what we used to build on no longer exists */\n-\tbase = branch->merge[0]->dst;\n \tif (read_ref(base, sha1))\n \t\treturn -1;\n \ttheirs = lookup_commit_reference(sha1);\n@@ -1869,7 +1891,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)\n \t\tbreak;\n \t}\n \n-\tbase = branch->merge[0]->dst;\n+\tbase = get_base(branch);\n \tbase = shorten_unambiguous_ref(base, 0);\n \tif (upstream_is_gone) {\n \t\tstrbuf_addf(sb,\ndiff --git a/remote.h b/remote.h\nindex fb7647f..08e93c4 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -204,6 +204,10 @@ struct branch {\n \tstruct refspec **merge;\n \tint merge_nr;\n \tint merge_alloc;\n+\n+\tconst char *pushremote_name;\n+\tconst char *push_name;\n+\tstruct refspec push;\n };\n \n struct branch *branch_get(const char *name);\n-- \n1.9.1+fc1\n"},{"id":"238645","messageId":"1397156686-31349-4-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 3/9] branch: allow configuring the publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:40Z","receivedAt":"2014-04-10T19:04:40Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n Documentation/git-branch.txt | 11 +++++++++\n branch.c                     | 43 +++++++++++++++++++++++++++++++++\n branch.h                     |  2 ++\n builtin/branch.c             | 57 ++++++++++++++++++++++++++++++++++++++++----\n 4 files changed, 108 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex 311b336..914fd62 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -14,7 +14,9 @@ SYNOPSIS\n \t[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]\n 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]\n 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]\n+'git branch' (--set-publish-to=<publish> | -p <publish>) [<branchname>]\n 'git branch' --unset-upstream [<branchname>]\n+'git branch' --unset-publish [<branchname>]\n 'git branch' (-m | -M) [<oldbranch>] <newbranch>\n 'git branch' (-d | -D) [-r] <branchname>...\n 'git branch' --edit-description [<branchname>]\n@@ -191,6 +193,15 @@ start-point is either a local or remote-tracking branch.\n \tRemove the upstream information for <branchname>. If no branch\n \tis specified it defaults to the current branch.\n \n+-p <publish>::\n+--set-publish-to=<publish>::\n+\tSet up <branchname>'s publish tracking information. If no\n+\t<branchname> is specified, then it defaults to the current branch.\n+\n+--unset-publish::\n+\tRemove the publish information for <branchname>. If no branch\n+\tis specified it defaults to the current branch.\n+\n --edit-description::\n \tOpen an editor and edit the text to explain what the branch is\n \tfor, to be used by various other commands (e.g. `request-pull`).\ndiff --git a/branch.c b/branch.c\nindex 723a36b..6f4fe7f 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -144,6 +144,49 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,\n \treturn 0;\n }\n \n+void install_branch_publish(const char *name, const char *remote, const char *remote_ref)\n+{\n+\tstruct strbuf key = STRBUF_INIT;\n+\n+\tif (!remote && !strcmp(name, remote_ref + 11) && !prefixcmp(remote_ref, \"refs/heads\")) {\n+\t\twarning(_(\"Not setting branch %s as its own publish branch.\"), name);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_addf(&key, \"branch.%s.pushremote\", name);\n+\tgit_config_set(key.buf, remote ? remote : \".\");\n+\n+\tstrbuf_reset(&key);\n+\tstrbuf_addf(&key, \"branch.%s.push\", name);\n+\tgit_config_set(key.buf, remote_ref);\n+\n+\tstrbuf_release(&key);\n+}\n+\n+int setup_publish(const char *name, const char *ref)\n+{\n+\tstruct tracking tracking;\n+\tconst char *remote, *remote_ref;\n+\n+\tmemset(&tracking, 0, sizeof(tracking));\n+\ttracking.spec.dst = (char*)ref;\n+\tif (for_each_remote(find_tracked_branch, &tracking))\n+\t\treturn 1;\n+\n+\tif (tracking.matches > 1)\n+\t\treturn error(_(\"Not tracking: ambiguous information for ref %s\"),\n+\t\t\t\tref);\n+\n+\tremote = tracking.remote;\n+\tremote_ref = tracking.src ? tracking.src : ref;\n+\n+\tinstall_branch_publish(name, remote, remote_ref);\n+\n+\tfree(tracking.src);\n+\n+\treturn 0;\n+}\n+\n struct branch_desc_cb {\n \tconst char *config_name;\n \tconst char *value;\ndiff --git a/branch.h b/branch.h\nindex 64173ab..c9b6aa9 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -51,5 +51,7 @@ extern void install_branch_config(int flag, const char *local, const char *origi\n  * Read branch description\n  */\n extern int read_branch_desc(struct strbuf *, const char *branch_name);\n+extern int setup_publish(const char *name, const char *ref);\n+extern void install_branch_publish(const char *name, const char *remote, const char *remote_ref);\n \n #endif\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex b4d7716..17773d7 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -793,8 +793,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \tint delete = 0, rename = 0, force_create = 0, list = 0;\n \tint verbose = 0, abbrev = -1, detached = 0;\n \tint reflog = 0, edit_description = 0;\n-\tint quiet = 0, unset_upstream = 0;\n-\tconst char *new_upstream = NULL;\n+\tint quiet = 0, unset_upstream = 0, unset_publish = 0;\n+\tconst char *new_upstream = NULL, *publish = NULL;\n \tenum branch_track track;\n \tint kinds = REF_LOCAL_BRANCH;\n \tstruct commit_list *with_commit = NULL;\n@@ -809,7 +809,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tOPT_SET_INT( 0, \"set-upstream\",  &track, N_(\"change upstream info\"),\n \t\t\tBRANCH_TRACK_OVERRIDE),\n \t\tOPT_STRING('u', \"set-upstream-to\", &new_upstream, \"upstream\", \"change the upstream info\"),\n+\t\tOPT_STRING('p', \"set-publish-to\", &publish, \"publish\", \"change the publish info\"),\n \t\tOPT_BOOL(0, \"unset-upstream\", &unset_upstream, \"Unset the upstream info\"),\n+\t\tOPT_BOOL(0, \"unset-publish\", &unset_publish, \"Unset the publish info\"),\n \t\tOPT__COLOR(&branch_use_color, N_(\"use colored output\")),\n \t\tOPT_SET_INT('r', \"remotes\",     &kinds, N_(\"act on remote-tracking branches\"),\n \t\t\tREF_REMOTE_BRANCH),\n@@ -878,14 +880,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options, builtin_branch_usage,\n \t\t\t     0);\n \n-\tif (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)\n+\tif (!delete && !rename && !edit_description && !new_upstream && !publish &&\n+\t    !unset_upstream && !unset_publish && argc == 0)\n \t\tlist = 1;\n \n \tif (with_commit || merge_filter != NO_FILTER)\n \t\tlist = 1;\n \n-\tif (!!delete + !!rename + !!force_create + !!new_upstream +\n-\t    list + unset_upstream > 1)\n+\tif (!!delete + !!rename + !!force_create + !!new_upstream + !!publish +\n+\t    list + unset_upstream + unset_publish > 1)\n \t\tusage_with_options(builtin_branch_usage, options);\n \n \tif (abbrev == -1)\n@@ -990,6 +993,50 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addf(&buf, \"branch.%s.merge\", branch->name);\n \t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n \t\tstrbuf_release(&buf);\n+\t} else if (publish) {\n+\t\tstruct branch *branch = branch_get(argv[0]);\n+\t\tchar *real_ref = NULL;\n+\t\tunsigned char sha1[20];\n+\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"too many branches to set new publish branch\"));\n+\n+\t\tif (!branch) {\n+\t\t\tif (!argc || !strcmp(argv[0], \"HEAD\"))\n+\t\t\t\tdie(_(\"could not set publish branch of HEAD when \"\n+\t\t\t\t      \"it does not point to any branch.\"));\n+\t\t\tdie(_(\"no such branch '%s'\"), argv[0]);\n+\t\t}\n+\n+\t\tif (!ref_exists(branch->refname))\n+\t\t\tdie(_(\"branch '%s' does not exist\"), branch->name);\n+\n+\t\tif (dwim_ref(publish, strlen(publish), sha1, &real_ref) != 1 ||\n+\t\t\t\tsetup_publish(branch->name, real_ref))\n+\t\t\tdie(_(\"Cannot setup publish branch to '%s'.\"), publish);\n+\t} else if (unset_publish) {\n+\t\tstruct branch *branch = branch_get(argv[0]);\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\n+\t\tif (argc > 1)\n+\t\t\tdie(_(\"too many branches to unset publish branch\"));\n+\n+\t\tif (!branch) {\n+\t\t\tif (!argc || !strcmp(argv[0], \"HEAD\"))\n+\t\t\t\tdie(_(\"could not unset publish branch of HEAD when \"\n+\t\t\t\t      \"it does not point to any branch.\"));\n+\t\t\tdie(_(\"no such branch '%s'\"), argv[0]);\n+\t\t}\n+\n+\t\tif (!branch->push_name)\n+\t\t\tdie(_(\"Branch '%s' has no publish information\"), branch->name);\n+\n+\t\tstrbuf_addf(&buf, \"branch.%s.pushremote\", branch->name);\n+\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"branch.%s.push\", branch->name);\n+\t\tgit_config_set_multivar(buf.buf, NULL, NULL, 1);\n+\t\tstrbuf_release(&buf);\n \t} else if (argc > 0 && argc <= 2) {\n \t\tstruct branch *branch = branch_get(argv[0]);\n \t\tint branch_existed = 0, remote_tracking = 0;\n-- \n1.9.1+fc1\n"},{"id":"238646","messageId":"1397156686-31349-5-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 4/9] t: branch add publish branch tests","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:41Z","receivedAt":"2014-04-10T19:04:41Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n t/t3200-branch.sh | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 76 insertions(+)\n\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex fcdb867..8cd21d1 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -907,4 +907,80 @@ test_expect_success 'tracking with unexpected .fetch refspec' '\n \t)\n '\n \n+test_expect_success '--set-publish-to fails on multiple branches' '\n+\ttest_must_fail git branch --set-publish-to master a b c\n+'\n+\n+test_expect_success '--set-publish-to fails on detached HEAD' '\n+\ttest_when_finished \"git checkout master\" &&\n+\tgit checkout master^{} &&\n+\ttest_must_fail git branch --set-publish-to master\n+'\n+\n+test_expect_success '--set-publish-to fails on a missing dst branch' '\n+\ttest_must_fail git branch --set-publish-to master does-not-exist\n+'\n+\n+test_expect_success '--set-publish-to fails on a missing src branch' '\n+\ttest_must_fail git branch --set-publish-to does-not-exist master\n+'\n+\n+test_expect_success '--set-publish-to fails on a non-ref' '\n+\ttest_must_fail git branch --set-publish-to HEAD^{}\n+'\n+\n+test_expect_success 'use --set-publish-to modify HEAD' '\n+\tgit checkout master &&\n+\ttest_config branch.master.pushremote foo &&\n+\ttest_config branch.master.push foo &&\n+\tgit branch -f test &&\n+\tgit branch --set-publish-to test &&\n+\ttest \"$(git config branch.master.pushremote)\" = \".\" &&\n+\ttest \"$(git config branch.master.push)\" = \"refs/heads/test\"\n+'\n+\n+test_expect_success 'use --set-publish-to modify a particular branch' '\n+\tgit branch -f test &&\n+\tgit branch -f test2 &&\n+\tgit branch --set-publish-to test2 test &&\n+\ttest \"$(git config branch.test.pushremote)\" = \".\" &&\n+\ttest \"$(git config branch.test.push)\" = \"refs/heads/test2\"\n+'\n+\n+test_expect_success '--unset-publish should fail if given a non-existent branch' '\n+\ttest_must_fail git branch --unset-publish i-dont-exist\n+'\n+\n+test_expect_success 'test --unset-publish on HEAD' '\n+\tgit checkout master &&\n+\tgit branch -f test &&\n+\ttest_config branch.master.pushremote foo &&\n+\ttest_config branch.master.push foo &&\n+\tgit branch --set-publish-to test &&\n+\tgit branch --unset-publish &&\n+\ttest_must_fail git config branch.master.pushremote &&\n+\ttest_must_fail git config branch.master.push &&\n+\t# fail for a branch without publish set\n+\ttest_must_fail git branch --unset-publish\n+'\n+\n+test_expect_success '--unset-publish should fail on multiple branches' '\n+\ttest_must_fail git branch --unset-publish a b c\n+'\n+\n+test_expect_success '--unset-publish should fail on detached HEAD' '\n+\ttest_when_finished \"git checkout -\" &&\n+\tgit checkout HEAD^{} &&\n+\ttest_must_fail git branch --unset-publish\n+'\n+\n+test_expect_success 'test --unset-publish on a particular branch' '\n+\tgit branch -f test &&\n+\tgit branch -f test2 &&\n+\tgit branch --set-publish-to test2 test &&\n+\tgit branch --unset-publish test &&\n+\ttest_must_fail git config branch.test2.pushremote &&\n+\ttest_must_fail git config branch.test2.push\n+'\n+\n test_done\n-- \n1.9.1+fc1\n"},{"id":"238647","messageId":"1397156686-31349-6-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 5/9] push: add --set-publish option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:42Z","receivedAt":"2014-04-10T19:04:42Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"To setup publish tracking branch, like 'git branch --set-publish'.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n Documentation/git-push.txt |  9 +++++-\n builtin/push.c             |  2 ++\n t/t5529-push-publish.sh    | 70 ++++++++++++++++++++++++++++++++++++++++++++++\n transport.c                | 28 +++++++++++++------\n transport.h                |  1 +\n 5 files changed, 100 insertions(+), 10 deletions(-)\n create mode 100755 t/t5529-push-publish.sh\n\ndiff --git a/Documentation/git-push.txt b/Documentation/git-push.txt\nindex 2b7f4f9..bf6ff9c 100644\n--- a/Documentation/git-push.txt\n+++ b/Documentation/git-push.txt\n@@ -10,7 +10,8 @@ SYNOPSIS\n --------\n [verse]\n 'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]\n-\t   [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]\n+\t   [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]\n+\t   [-u | --set-upstream] [-p | --set-publish]\n \t   [--force-with-lease[=<refname>[:<expect>]]]\n \t   [--no-verify] [<repository> [<refspec>...]]\n \n@@ -231,6 +232,12 @@ useful if you write an alias or script around 'git push'.\n \tlinkgit:git-pull[1] and other commands. For more information,\n \tsee 'branch.<name>.merge' in linkgit:git-config[1].\n \n+-p::\n+--set-publish::\n+\tFor every branch that is up to date or successfully pushed, add\n+\tpublish branch tracking reference, used by argument-less\n+\tlinkgit:git-pull[1] and other commands.\n+\n --[no-]thin::\n \tThese options are passed to linkgit:git-send-pack[1]. A thin transfer\n \tsignificantly reduces the amount of sent data when the sender and\ndiff --git a/builtin/push.c b/builtin/push.c\nindex a1fdc49..9e61b8f 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -532,6 +532,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_STRING( 0 , \"exec\", &receivepack, \"receive-pack\", N_(\"receive pack program\")),\n \t\tOPT_BIT('u', \"set-upstream\", &flags, N_(\"set upstream for git pull/status\"),\n \t\t\tTRANSPORT_PUSH_SET_UPSTREAM),\n+\t\tOPT_BIT('p', \"set-publish\", &flags, N_(\"set publish for git pull/status\"),\n+\t\t\tTRANSPORT_PUSH_SET_PUBLISH),\n \t\tOPT_BOOL(0, \"progress\", &progress, N_(\"force progress reporting\")),\n \t\tOPT_BIT(0, \"prune\", &flags, N_(\"prune locally removed refs\"),\n \t\t\tTRANSPORT_PUSH_PRUNE),\ndiff --git a/t/t5529-push-publish.sh b/t/t5529-push-publish.sh\nnew file mode 100755\nindex 0000000..2037026\n--- /dev/null\n+++ b/t/t5529-push-publish.sh\n@@ -0,0 +1,70 @@\n+#!/bin/sh\n+\n+test_description='push with --set-publish'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup bare parent' '\n+\tgit init --bare parent &&\n+\tgit remote add publish parent\n+'\n+\n+test_expect_success 'setup local commit' '\n+\techo content >file &&\n+\tgit add file &&\n+\tgit commit -m one\n+'\n+\n+check_config() {\n+\t(echo $2; echo $3) >expect.$1\n+\t(git config branch.$1.pushremote\n+\t git config branch.$1.push) >actual.$1\n+\ttest_cmp expect.$1 actual.$1\n+}\n+\n+test_expect_success 'push -p master:master' '\n+\tgit push -p publish master:master &&\n+\tcheck_config master publish refs/heads/master\n+'\n+\n+test_expect_success 'push -u master:other' '\n+\tgit push -p publish master:other &&\n+\tcheck_config master publish refs/heads/other\n+'\n+\n+test_expect_success 'push -p --dry-run master:otherX' '\n+\tgit push -p --dry-run publish master:otherX &&\n+\tcheck_config master publish refs/heads/other\n+'\n+\n+test_expect_success 'push -p master2:master2' '\n+\tgit branch master2 &&\n+\tgit push -p publish master2:master2 &&\n+\tcheck_config master2 publish refs/heads/master2\n+'\n+\n+test_expect_success 'push -p master2:other2' '\n+\tgit push -p publish master2:other2 &&\n+\tcheck_config master2 publish refs/heads/other2\n+'\n+\n+test_expect_success 'push -p :master2' '\n+\tgit push -p publish :master2 &&\n+\tcheck_config master2 publish refs/heads/other2\n+'\n+\n+test_expect_success 'push -u --all' '\n+\tgit branch all1 &&\n+\tgit branch all2 &&\n+\tgit push -p --all &&\n+\tcheck_config all1 publish refs/heads/all1 &&\n+\tcheck_config all2 publish refs/heads/all2\n+'\n+\n+test_expect_success 'push -p HEAD' '\n+\tgit checkout -b headbranch &&\n+\tgit push -p publish HEAD &&\n+\tcheck_config headbranch publish refs/heads/headbranch\n+'\n+\n+test_done\ndiff --git a/transport.c b/transport.c\nindex ca7bb44..dfcddfe 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -143,8 +143,8 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)\n \t}\n }\n \n-static void set_upstreams(struct transport *transport, struct ref *refs,\n-\tint pretend)\n+static void set_tracking(struct transport *transport, struct ref *refs,\n+\tint pretend, int publish)\n {\n \tstruct ref *ref;\n \tfor (ref = refs; ref; ref = ref->next) {\n@@ -179,12 +179,18 @@ static void set_upstreams(struct transport *transport, struct ref *refs,\n \t\tif (!remotename || !starts_with(remotename, \"refs/heads/\"))\n \t\t\tcontinue;\n \n-\t\tif (!pretend)\n-\t\t\tinstall_branch_config(BRANCH_CONFIG_VERBOSE,\n-\t\t\t\tlocalname + 11, transport->remote->name,\n-\t\t\t\tremotename);\n-\t\telse\n-\t\t\tprintf(\"Would set upstream of '%s' to '%s' of '%s'\\n\",\n+\t\tif (!pretend) {\n+\t\t\tif (publish)\n+\t\t\t\tinstall_branch_publish(localname + 11,\n+\t\t\t\t\t\ttransport->remote->name,\n+\t\t\t\t\t\tremotename);\n+\t\t\telse\n+\t\t\t\tinstall_branch_config(BRANCH_CONFIG_VERBOSE,\n+\t\t\t\t\t\tlocalname + 11, transport->remote->name,\n+\t\t\t\t\t\tremotename);\n+\t\t} else\n+\t\t\tprintf(\"Would set %s of '%s' to '%s' of '%s'\\n\",\n+\t\t\t\tpublish ? \"publish\" : \"upstream\",\n \t\t\t\tlocalname + 11, remotename + 11,\n \t\t\t\ttransport->remote->name);\n \t}\n@@ -1129,6 +1135,8 @@ int transport_push(struct transport *transport,\n \t\t/* Maybe FIXME. But no important transport uses this case. */\n \t\tif (flags & TRANSPORT_PUSH_SET_UPSTREAM)\n \t\t\tdie(\"This transport does not support using --set-upstream\");\n+\t\tif (flags & TRANSPORT_PUSH_SET_PUBLISH)\n+\t\t\tdie(\"This transport does not support using --set-publish\");\n \n \t\treturn transport->push(transport, refspec_nr, refspec, flags);\n \t} else if (transport->push_refs) {\n@@ -1203,7 +1211,9 @@ int transport_push(struct transport *transport,\n \t\t\t\t\treject_reasons);\n \n \t\tif (flags & TRANSPORT_PUSH_SET_UPSTREAM)\n-\t\t\tset_upstreams(transport, remote_refs, pretend);\n+\t\t\tset_tracking(transport, remote_refs, pretend, 0);\n+\t\tif (flags & TRANSPORT_PUSH_SET_PUBLISH)\n+\t\t\tset_tracking(transport, remote_refs, pretend, 1);\n \n \t\tif (!(flags & TRANSPORT_PUSH_DRY_RUN)) {\n \t\t\tstruct ref *ref;\ndiff --git a/transport.h b/transport.h\nindex 02ea248..324997c 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -123,6 +123,7 @@ struct transport {\n #define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256\n #define TRANSPORT_PUSH_NO_HOOK 512\n #define TRANSPORT_PUSH_FOLLOW_TAGS 1024\n+#define TRANSPORT_PUSH_SET_PUBLISH 2048\n \n #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)\n #define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)\n-- \n1.9.1+fc1\n"},{"id":"238648","messageId":"1397156686-31349-7-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 6/9] branch: display publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:43Z","receivedAt":"2014-04-10T19:04:43Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n builtin/branch.c         | 17 ++++++++++++++++-\n t/t6040-tracking-info.sh |  5 +++--\n 2 files changed, 19 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 17773d7..e0a8d0a 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -42,6 +42,7 @@ static char branch_colors[][COLOR_MAXLEN] = {\n \tGIT_COLOR_NORMAL,\t/* LOCAL */\n \tGIT_COLOR_GREEN,\t/* CURRENT */\n \tGIT_COLOR_BLUE,\t\t/* UPSTREAM */\n+\tGIT_COLOR_YELLOW,\t/* PUBLISH */\n };\n enum color_branch {\n \tBRANCH_COLOR_RESET = 0,\n@@ -49,7 +50,8 @@ enum color_branch {\n \tBRANCH_COLOR_REMOTE = 2,\n \tBRANCH_COLOR_LOCAL = 3,\n \tBRANCH_COLOR_CURRENT = 4,\n-\tBRANCH_COLOR_UPSTREAM = 5\n+\tBRANCH_COLOR_UPSTREAM = 5,\n+\tBRANCH_COLOR_PUBLISH = 6\n };\n \n static enum merge_filter {\n@@ -76,6 +78,8 @@ static int parse_branch_color_slot(const char *var, int ofs)\n \t\treturn BRANCH_COLOR_CURRENT;\n \tif (!strcasecmp(var+ofs, \"upstream\"))\n \t\treturn BRANCH_COLOR_UPSTREAM;\n+\tif (!strcasecmp(var+ofs, \"publish\"))\n+\t\treturn BRANCH_COLOR_PUBLISH;\n \treturn -1;\n }\n \n@@ -448,6 +452,17 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,\n \t\telse\n \t\t\tstrbuf_addstr(&fancy, ref);\n \t}\n+\tif (branch->push.dst) {\n+\t\tref = shorten_unambiguous_ref(branch->push.dst, 0);\n+\t\tif (fancy.len)\n+\t\t\tstrbuf_addstr(&fancy, \", \");\n+\t\tif (want_color(branch_use_color))\n+\t\t\tstrbuf_addf(&fancy, \"%s%s%s\",\n+\t\t\t\t\tbranch_get_color(BRANCH_COLOR_PUBLISH),\n+\t\t\t\t\tref, branch_get_color(BRANCH_COLOR_RESET));\n+\t\telse\n+\t\t\tstrbuf_addstr(&fancy, ref);\n+\t}\n \n \tif (upstream_is_gone) {\n \t\tif (show_upstream_ref)\ndiff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\nindex 7ac8fd0..8b9ef63 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -33,7 +33,8 @@ test_expect_success setup '\n \t\tgit checkout -b b5 --track brokenbase &&\n \t\tadvance g &&\n \t\tgit branch -d brokenbase &&\n-\t\tgit checkout -b b6 origin\n+\t\tgit checkout -b b6 origin &&\n+\t\tgit branch --set-publish origin/master b6\n \t) &&\n \tgit checkout -b follower --track master &&\n \tadvance h\n@@ -64,7 +65,7 @@ b2 [origin/master: ahead 1, behind 1] d\n b3 [origin/master: behind 1] b\n b4 [origin/master: ahead 2] f\n b5 [brokenbase: gone] g\n-b6 [origin/master] c\n+b6 [origin/master, origin/master] c\n EOF\n \n test_expect_success 'branch -vv' '\n-- \n1.9.1+fc1\n"},{"id":"238649","messageId":"1397156686-31349-8-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 7/9] sha1_name: cleanup interpret_branch_name()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:44Z","receivedAt":"2014-04-10T19:04:44Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"The 'upstream' variable doesn't hold an \"upstream\", but a branch, so\nmake it clearer.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n sha1_name.c | 24 ++++++++++++------------\n 1 file changed, 12 insertions(+), 12 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 6fca869..906f09d 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -1057,31 +1057,31 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref)\n \tfree(s);\n }\n \n-static const char *get_upstream_branch(const char *branch_buf, int len)\n+static const char *get_upstream_branch(const char *name_buf, int len)\n {\n-\tchar *branch = xstrndup(branch_buf, len);\n-\tstruct branch *upstream = branch_get(*branch ? branch : NULL);\n+\tchar *name = xstrndup(name_buf, len);\n+\tstruct branch *branch = branch_get(*name ? name : NULL);\n \n \t/*\n \t * Upstream can be NULL only if branch refers to HEAD and HEAD\n \t * points to something different than a branch.\n \t */\n-\tif (!upstream)\n+\tif (!branch)\n \t\tdie(_(\"HEAD does not point to a branch\"));\n-\tif (!upstream->merge || !upstream->merge[0]->dst) {\n-\t\tif (!ref_exists(upstream->refname))\n-\t\t\tdie(_(\"No such branch: '%s'\"), branch);\n-\t\tif (!upstream->merge) {\n+\tif (!branch->merge || !branch->merge[0]->dst) {\n+\t\tif (!ref_exists(branch->refname))\n+\t\t\tdie(_(\"No such branch: '%s'\"), name);\n+\t\tif (!branch->merge) {\n \t\t\tdie(_(\"No upstream configured for branch '%s'\"),\n-\t\t\t\tupstream->name);\n+\t\t\t\tbranch->name);\n \t\t}\n \t\tdie(\n \t\t\t_(\"Upstream branch '%s' not stored as a remote-tracking branch\"),\n-\t\t\tupstream->merge[0]->src);\n+\t\t\tbranch->merge[0]->src);\n \t}\n-\tfree(branch);\n+\tfree(name);\n \n-\treturn upstream->merge[0]->dst;\n+\treturn branch->merge[0]->dst;\n }\n \n static int interpret_upstream_mark(const char *name, int namelen,\n-- \n1.9.1+fc1\n"},{"id":"238650","messageId":"1397156686-31349-9-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 8/9] sha1_name: simplify track finding","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:45Z","receivedAt":"2014-04-10T19:04:45Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"It's more efficient to check for the braces first.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n sha1_name.c | 11 +++++++----\n 1 file changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 906f09d..aa3f3e0 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -417,7 +417,7 @@ static int ambiguous_path(const char *path, int len)\n \n static inline int upstream_mark(const char *string, int len)\n {\n-\tconst char *suffix[] = { \"@{upstream}\", \"@{u}\" };\n+\tconst char *suffix[] = { \"upstream\", \"u\" };\n \tint i;\n \n \tfor (i = 0; i < ARRAY_SIZE(suffix); i++) {\n@@ -475,7 +475,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)\n \t\t\t\t\tnth_prior = 1;\n \t\t\t\t\tcontinue;\n \t\t\t\t}\n-\t\t\t\tif (!upstream_mark(str + at, len - at)) {\n+\t\t\t\tif (!upstream_mark(str + at + 2, len - at - 3)) {\n \t\t\t\t\treflog_len = (len-1) - (at+2);\n \t\t\t\t\tlen = at;\n \t\t\t\t}\n@@ -1089,7 +1089,10 @@ static int interpret_upstream_mark(const char *name, int namelen,\n {\n \tint len;\n \n-\tlen = upstream_mark(name + at, namelen - at);\n+\tif (name[at + 1] != '{' || name[namelen - 1] != '}')\n+\t\treturn -1;\n+\n+\tlen = upstream_mark(name + at + 2, namelen - at - 3);\n \tif (!len)\n \t\treturn -1;\n \n@@ -1097,7 +1100,7 @@ static int interpret_upstream_mark(const char *name, int namelen,\n \t\treturn -1;\n \n \tset_shortened_ref(buf, get_upstream_branch(name, at));\n-\treturn len + at;\n+\treturn len + at + 3;\n }\n \n /*\n-- \n1.9.1+fc1\n"},{"id":"238651","messageId":"1397156686-31349-10-git-send-email-felipe.contreras@gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v2 9/9] sha1_name: add support for @{publish} marks","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T19:04:46Z","receivedAt":"2014-04-10T19:04:46Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n Documentation/revisions.txt |  4 ++++\n sha1_name.c                 | 49 ++++++++++++++++++++++++++++-----------------\n t/t1508-at-combinations.sh  |  5 +++++\n 3 files changed, 40 insertions(+), 18 deletions(-)\n\ndiff --git a/Documentation/revisions.txt b/Documentation/revisions.txt\nindex 5a286d0..fd01cb4 100644\n--- a/Documentation/revisions.txt\n+++ b/Documentation/revisions.txt\n@@ -96,6 +96,10 @@ some output processing may assume ref names in UTF-8.\n   refers to the branch that the branch specified by branchname is set to build on\n   top of.  A missing branchname defaults to the current one.\n \n+'<branchname>@\\{publish\\}', e.g. 'master@\\{publish\\}', '@\\{p\\}'::\n+  The suffix '@\\{publish\\}' to a branchname refers to the remote branch to\n+  push to. A missing branchname defaults to the current one.\n+\n '<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::\n   A suffix '{caret}' to a revision parameter means the first parent of\n   that commit object.  '{caret}<n>' means the <n>th parent (i.e.\ndiff --git a/sha1_name.c b/sha1_name.c\nindex aa3f3e0..a36852d 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -415,9 +415,9 @@ static int ambiguous_path(const char *path, int len)\n \treturn slash;\n }\n \n-static inline int upstream_mark(const char *string, int len)\n+static inline int tracking_mark(const char *string, int len)\n {\n-\tconst char *suffix[] = { \"upstream\", \"u\" };\n+\tconst char *suffix[] = { \"upstream\", \"u\", \"publish\", \"p\" };\n \tint i;\n \n \tfor (i = 0; i < ARRAY_SIZE(suffix); i++) {\n@@ -475,7 +475,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)\n \t\t\t\t\tnth_prior = 1;\n \t\t\t\t\tcontinue;\n \t\t\t\t}\n-\t\t\t\tif (!upstream_mark(str + at + 2, len - at - 3)) {\n+\t\t\t\tif (!tracking_mark(str + at + 2, len - at - 3)) {\n \t\t\t\t\treflog_len = (len-1) - (at+2);\n \t\t\t\t\tlen = at;\n \t\t\t\t}\n@@ -1057,10 +1057,11 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref)\n \tfree(s);\n }\n \n-static const char *get_upstream_branch(const char *name_buf, int len)\n+static const char *get_tracking_branch(const char *name_buf, int len, char type)\n {\n \tchar *name = xstrndup(name_buf, len);\n \tstruct branch *branch = branch_get(*name ? name : NULL);\n+\tchar *tracking = NULL;\n \n \t/*\n \t * Upstream can be NULL only if branch refers to HEAD and HEAD\n@@ -1068,23 +1069,35 @@ static const char *get_upstream_branch(const char *name_buf, int len)\n \t */\n \tif (!branch)\n \t\tdie(_(\"HEAD does not point to a branch\"));\n-\tif (!branch->merge || !branch->merge[0]->dst) {\n-\t\tif (!ref_exists(branch->refname))\n-\t\t\tdie(_(\"No such branch: '%s'\"), name);\n-\t\tif (!branch->merge) {\n-\t\t\tdie(_(\"No upstream configured for branch '%s'\"),\n-\t\t\t\tbranch->name);\n+\tswitch (type) {\n+\tcase 'u':\n+\t\tif (!branch->merge || !branch->merge[0]->dst) {\n+\t\t\tif (!ref_exists(branch->refname))\n+\t\t\t\tdie(_(\"No such branch: '%s'\"), name);\n+\t\t\tif (!branch->merge) {\n+\t\t\t\tdie(_(\"No upstream configured for branch '%s'\"),\n+\t\t\t\t\tbranch->name);\n+\t\t\t}\n+\t\t\tdie(\n+\t\t\t\t_(\"Upstream branch '%s' not stored as a remote-tracking branch\"),\n+\t\t\t\tbranch->merge[0]->src);\n+\t\t}\n+\t\ttracking = branch->merge[0]->dst;\n+\t\tbreak;\n+\tcase 'p':\n+\t\tif (!branch->push.dst) {\n+\t\t\tdie(_(\"No publish configured for branch '%s'\"),\n+\t\t\t\t\tbranch->name);\n \t\t}\n-\t\tdie(\n-\t\t\t_(\"Upstream branch '%s' not stored as a remote-tracking branch\"),\n-\t\t\tbranch->merge[0]->src);\n+\t\ttracking = branch->push.dst;\n+\t\tbreak;\n \t}\n \tfree(name);\n \n-\treturn branch->merge[0]->dst;\n+\treturn tracking;\n }\n \n-static int interpret_upstream_mark(const char *name, int namelen,\n+static int interpret_tracking_mark(const char *name, int namelen,\n \t\t\t\t   int at, struct strbuf *buf)\n {\n \tint len;\n@@ -1092,14 +1105,14 @@ static int interpret_upstream_mark(const char *name, int namelen,\n \tif (name[at + 1] != '{' || name[namelen - 1] != '}')\n \t\treturn -1;\n \n-\tlen = upstream_mark(name + at + 2, namelen - at - 3);\n+\tlen = tracking_mark(name + at + 2, namelen - at - 3);\n \tif (!len)\n \t\treturn -1;\n \n \tif (memchr(name, ':', at))\n \t\treturn -1;\n \n-\tset_shortened_ref(buf, get_upstream_branch(name, at));\n+\tset_shortened_ref(buf, get_tracking_branch(name, at, name[at + 2]));\n \treturn len + at + 3;\n }\n \n@@ -1150,7 +1163,7 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)\n \t\tif (len > 0)\n \t\t\treturn reinterpret(name, namelen, len, buf);\n \n-\t\tlen = interpret_upstream_mark(name, namelen, at - name, buf);\n+\t\tlen = interpret_tracking_mark(name, namelen, at - name, buf);\n \t\tif (len > 0)\n \t\t\treturn len;\n \t}\ndiff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh\nindex 078e119..f67aab3 100755\n--- a/t/t1508-at-combinations.sh\n+++ b/t/t1508-at-combinations.sh\n@@ -32,6 +32,7 @@ fail() {\n test_expect_success 'setup' '\n \ttest_commit master-one &&\n \ttest_commit master-two &&\n+\tgit checkout -b publish-branch &&\n \tgit checkout -b upstream-branch &&\n \ttest_commit upstream-one &&\n \ttest_commit upstream-two &&\n@@ -46,6 +47,7 @@ test_expect_success 'setup' '\n \ttest_commit new-two &&\n \tgit branch -u master old-branch &&\n \tgit branch -u upstream-branch new-branch\n+\tgit branch -p publish-branch new-branch\n '\n \n check HEAD ref refs/heads/new-branch\n@@ -61,8 +63,11 @@ check \"HEAD@{u}\" ref refs/heads/upstream-branch\n check \"@{u}@{1}\" commit upstream-one\n check \"@{-1}@{u}\" ref refs/heads/master\n check \"@{-1}@{u}@{1}\" commit master-one\n+check \"@{p}\" ref refs/heads/publish-branch\n+check \"HEAD@{p}\" ref refs/heads/publish-branch\n check \"@\" commit new-two\n check \"@@{u}\" ref refs/heads/upstream-branch\n+check \"@@{p}\" ref refs/heads/publish-branch\n check \"@@/at-test\" ref refs/heads/@@/at-test\n check \"@/at-test\" ref refs/heads/@/at-test\n check \"@at-test\" ref refs/heads/@at-test\n-- \n1.9.1+fc1\n"},{"id":"238658","messageId":"xmqq38hkx4lr.fsf@gitster.dls.corp.google.com","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 0/9] Introduce publish tracking branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-10T21:21:52Z","receivedAt":"2014-04-10T21:21:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"A handful of minimum tweaks [*1*] here and there were necessary in\norder to queue the series on 'pu', but to me, the feature looked\nlike quite a straight-forward addition.\n\nI'd be dropping the jk/branch-at-publish-rebased from 'pu', at least\ntentatively, as that one was primarily Peff giving Ram a base to\nbuild on top to achieve essentially the same thing as this series\ndoes.  I didn't bother to check if this series could have reused\nsome from that series (primarily because I was short of time, had to\ntake the work laptop to service, etc. etc.) before doing so.\n\nI didn't think too deeply about the workflow ramifications of this\nseries brings in, either---that is left for the reviewers Cc'ed on\nthe patches.\n\n\n[Footnote]\n\n*1* Things like these:\n\n - the context in builtin/push.c has already changed at the tip of\n   'master' (we already pretend to be Git 2.0) and the patch text\n   needed to be adjusted.\n\n - an instance of cast to \"(char*)\" fixed to \"(char *)\". \n\n - t/t5529 is already used by other topics, renaming the new test to\n   t/t5534-push-publish.sh.\n\n - !prefixcmp() is already removed for Git 2.0, replacing its use\n   with starts_with().\n"},{"id":"238659","messageId":"CALkWK0nC7Ai_LbJNpy-oK-qfWZELx6NVWTNS-5iWzhn8t9JoTw@mail.gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-10-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 9/9] sha1_name: add support for @{publish} marks","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-04-10T21:40:20Z","receivedAt":"2014-04-10T21:40:20Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n> @@ -1068,23 +1069,35 @@ static const char *get_upstream_branch(const char *name_buf, int len)\n>          */\n>         if (!branch)\n>                 die(_(\"HEAD does not point to a branch\"));\n> -       if (!branch->merge || !branch->merge[0]->dst) {\n> -               if (!ref_exists(branch->refname))\n> -                       die(_(\"No such branch: '%s'\"), name);\n> -               if (!branch->merge) {\n> -                       die(_(\"No upstream configured for branch '%s'\"),\n> -                               branch->name);\n> +       switch (type) {\n> +       case 'u':\n> +               if (!branch->merge || !branch->merge[0]->dst) {\n> +                       if (!ref_exists(branch->refname))\n> +                               die(_(\"No such branch: '%s'\"), name);\n> +                       if (!branch->merge) {\n> +                               die(_(\"No upstream configured for branch '%s'\"),\n> +                                       branch->name);\n> +                       }\n> +                       die(\n> +                               _(\"Upstream branch '%s' not stored as a remote-tracking branch\"),\n> +                               branch->merge[0]->src);\n> +               }\n> +               tracking = branch->merge[0]->dst;\n> +               break;\n> +       case 'p':\n> +               if (!branch->push.dst) {\n> +                       die(_(\"No publish configured for branch '%s'\"),\n> +                                       branch->name);\n\nThis assumes a push.default value of 'current' or 'matching'. What\nhappens if push.default is set to 'nothing' or 'upstream', for\ninstance?\n\np.s- Good to see you back on the list :)\n"},{"id":"238660","messageId":"CALkWK0kRAYpZxDkZ6nVPcfo4pqJuTLNFekpQ+tLPPRFJZKUMuw@mail.gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-9-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 8/9] sha1_name: simplify track finding","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-04-10T21:44:21Z","receivedAt":"2014-04-10T21:44:21Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n> It's more efficient to check for the braces first.\n\nWhy is it more efficient? So you can error out quickly in the case of\na malformed string? I'm personally not thrilled about this change.\n"},{"id":"238661","messageId":"CALkWK0mWW+RrkEyuBw6c7yLwR8qpiTTJYR_cO33=tW716w94qg@mail.gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-8-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 7/9] sha1_name: cleanup interpret_branch_name()","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-04-10T21:45:51Z","receivedAt":"2014-04-10T21:45:51Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n>  sha1_name.c | 24 ++++++++++++------------\n>  1 file changed, 12 insertions(+), 12 deletions(-)\n\nI like this variable rename. This instance has annoyed me in the past.\n"},{"id":"238662","messageId":"CALkWK0nU_Esy6QoQG-vv3nKE=P=gWDiFgRp4RKvq4DbxOntkaA@mail.gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-10-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 9/9] sha1_name: add support for @{publish} marks","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-04-10T21:49:03Z","receivedAt":"2014-04-10T21:49:03Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n> diff --git a/sha1_name.c b/sha1_name.c\n> index aa3f3e0..a36852d 100644\n> --- a/sha1_name.c\n> +++ b/sha1_name.c\n> @@ -415,9 +415,9 @@ static int ambiguous_path(const char *path, int len)\n>         return slash;\n>  }\n>\n> -static inline int upstream_mark(const char *string, int len)\n> +static inline int tracking_mark(const char *string, int len)\n>  {\n> -       const char *suffix[] = { \"upstream\", \"u\" };\n> +       const char *suffix[] = { \"upstream\", \"u\", \"publish\", \"p\" };\n\nOh, another thing: on some threads, people decided that \"@{push}\"\nwould be a more apt name (+ alias @{u} to @{pull} for symmetry).\n"},{"id":"238663","messageId":"CALkWK0mSfsUrSMPMmZ+PNtZ2WBUqN4Swk_e6S1fMGuUoocJSEg@mail.gmail.com","threadId":"36387","inReplyTo":"1397156686-31349-7-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-04-10T22:03:37Z","receivedAt":"2014-04-10T22:03:37Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Felipe Contreras wrote:\n> Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n\nPlease write a commit message, preferably showing the new git-branch output.\n\nI noticed that this only picks up a publish-branch if\nbranch.*.pushremote is configured. What happened to the case when\nremote.pushdefault is configured?\n"},{"id":"238664","messageId":"53471a55767f1_d696b12f01c@nysa.notmuch","threadId":"36387","inReplyTo":"CALkWK0nC7Ai_LbJNpy-oK-qfWZELx6NVWTNS-5iWzhn8t9JoTw@mail.gmail.com","subject":"Re: [PATCH v2 9/9] sha1_name: add support for @{publish} marks","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T22:25:25Z","receivedAt":"2014-04-10T22:25:25Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Felipe Contreras wrote:\n> > @@ -1068,23 +1069,35 @@ static const char *get_upstream_branch(const char *name_buf, int len)\n> >          */\n> >         if (!branch)\n> >                 die(_(\"HEAD does not point to a branch\"));\n> > -       if (!branch->merge || !branch->merge[0]->dst) {\n> > -               if (!ref_exists(branch->refname))\n> > -                       die(_(\"No such branch: '%s'\"), name);\n> > -               if (!branch->merge) {\n> > -                       die(_(\"No upstream configured for branch '%s'\"),\n> > -                               branch->name);\n> > +       switch (type) {\n> > +       case 'u':\n> > +               if (!branch->merge || !branch->merge[0]->dst) {\n> > +                       if (!ref_exists(branch->refname))\n> > +                               die(_(\"No such branch: '%s'\"), name);\n> > +                       if (!branch->merge) {\n> > +                               die(_(\"No upstream configured for branch '%s'\"),\n> > +                                       branch->name);\n> > +                       }\n> > +                       die(\n> > +                               _(\"Upstream branch '%s' not stored as a remote-tracking branch\"),\n> > +                               branch->merge[0]->src);\n> > +               }\n> > +               tracking = branch->merge[0]->dst;\n> > +               break;\n> > +       case 'p':\n> > +               if (!branch->push.dst) {\n> > +                       die(_(\"No publish configured for branch '%s'\"),\n> > +                                       branch->name);\n> \n> This assumes a push.default value of 'current' or 'matching'. What\n> happens if push.default is set to 'nothing' or 'upstream', for\n> instance?\n\nWhy would that matter? @{upstream} doesn't depend on this, neither does\n@{publish}; @{upstream} is .remote+.merge, @{publish} is .pushremote+.push.\n\nIf the user hasn't configured a publish branch, @{publish} fails.\n\n-- \nFelipe Contreras\n"},{"id":"238665","messageId":"53471abb6fa76_d696b12f0b0@nysa.notmuch","threadId":"36387","inReplyTo":"CALkWK0kRAYpZxDkZ6nVPcfo4pqJuTLNFekpQ+tLPPRFJZKUMuw@mail.gmail.com","subject":"Re: [PATCH v2 8/9] sha1_name: simplify track finding","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T22:27:07Z","receivedAt":"2014-04-10T22:27:07Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Felipe Contreras wrote:\n> > It's more efficient to check for the braces first.\n> \n> Why is it more efficient? So you can error out quickly in the case of\n> a malformed string?\n\nThat's one reason. The other is that get_sha1_basic() calls upstream_mark()\nwhen we _already_ know there's a @{foo}.\n\n-- \nFelipe Contreras\n"},{"id":"238666","messageId":"53471b02305be_d696b12f099@nysa.notmuch","threadId":"36387","inReplyTo":"CALkWK0nU_Esy6QoQG-vv3nKE=P=gWDiFgRp4RKvq4DbxOntkaA@mail.gmail.com","subject":"Re: [PATCH v2 9/9] sha1_name: add support for @{publish} marks","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T22:28:18Z","receivedAt":"2014-04-10T22:28:18Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Felipe Contreras wrote:\n> > diff --git a/sha1_name.c b/sha1_name.c\n> > index aa3f3e0..a36852d 100644\n> > --- a/sha1_name.c\n> > +++ b/sha1_name.c\n> > @@ -415,9 +415,9 @@ static int ambiguous_path(const char *path, int len)\n> >         return slash;\n> >  }\n> >\n> > -static inline int upstream_mark(const char *string, int len)\n> > +static inline int tracking_mark(const char *string, int len)\n> >  {\n> > -       const char *suffix[] = { \"upstream\", \"u\" };\n> > +       const char *suffix[] = { \"upstream\", \"u\", \"publish\", \"p\" };\n> \n> Oh, another thing: on some threads, people decided that \"@{push}\"\n> would be a more apt name (+ alias @{u} to @{pull} for symmetry).\n\n@{push} is the name I originally suggested, but it's weird to talk about\n\"the push branch\", so I decided on \"the publish branch\", which is more natural.\n\n-- \nFelipe Contreras\n"},{"id":"238667","messageId":"53471d0b4c8dc_d696b12f08c@nysa.notmuch","threadId":"36387","inReplyTo":"CALkWK0mSfsUrSMPMmZ+PNtZ2WBUqN4Swk_e6S1fMGuUoocJSEg@mail.gmail.com","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-10T22:36:59Z","receivedAt":"2014-04-10T22:36:59Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Ramkumar Ramachandra wrote:\n> Felipe Contreras wrote:\n> > Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n> \n> Please write a commit message, preferably showing the new git-branch output.\n\nYeah... this has been sitting in git-fc for quite a while, I wasn't expecting\nto send this patch series again given that nobody commented on v1.\n\n> I noticed that this only picks up a publish-branch if\n> branch.*.pushremote is configured. What happened to the case when\n> remote.pushdefault is configured?\n\nWhat happens when branch.*.remote is not configured for @{upstream}? The same\nthing.\n\nIt might be useful to visualize what would be the name of the branch when\npushing it (without a refspec) even if the publish branch hasn't been\nconfigured, but I think the code would be much more coplicated, and it would\nbreak symetry with @{upstream}, besides, the user can just do 'git push -p\nbranch', and from that moment on it will be visible.\n\n-- \nFelipe Contreras\n"},{"id":"238677","messageId":"vpqtxa0jkfo.fsf@anie.imag.fr","threadId":"36387","inReplyTo":"1397156686-31349-1-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v2 0/9] Introduce publish tracking branch","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2014-04-11T09:15:55Z","receivedAt":"2014-04-11T09:15:55Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Felipe Contreras <felipe.contreras@gmail.com> writes:\n\n> As it has been discussed before, our support for triangular workflows is\n> lacking, and the following patch series aims to improve that situation.\n\nI'm not a heavy user of triangular workflow, so I'm not in the best\nposition to comment (and I have no time for a real review, sorry).\n\nOn overall, I do like the change. I played a bit with it, and do not\nunderstand what \"git push\" does:\n\n  $ git status\n  On branch master\n  Your branch is ahead of 'origin/new' by 4 commits.\n    (use \"git push\" to publish your local commits)\n\n=> OK, it's using the publish branch to tell me whether I should push.\n\n  $ git push -v\n  Pushing to /tmp/git\n  To /tmp/git\n   = [up to date]      master -> master\n  updating local tracking ref 'refs/remotes/origin/master'\n  Everything up-to-date\n\n=> Err, it still pushes to the upstream branch ... Wasn't that the point\nof the change to push to publish? Did I do something wrong?\n\n\nYour series lacks documentation of branch.<name>.* in\nDocumentation/config.txt.\n\nIt seems strange to me to name the config variables \"branch.<name>.push\"\nand \"branch.<name>.pushremote\" and call the same thing \"@{publish}\"\nelsewhere. We're already not consistant with @{upstream} corresponding\nto branch.<name>.merge, but I do not consider it as a good reason to\nintroduce one more inconsistancy.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"238680","messageId":"20140411111750.GA28858@sigill.intra.peff.net","threadId":"36387","inReplyTo":"53471d0b4c8dc_d696b12f08c@nysa.notmuch","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-04-11T11:17:51Z","receivedAt":"2014-04-11T11:17:51Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Apr 10, 2014 at 05:36:59PM -0500, Felipe Contreras wrote:\n\n> > I noticed that this only picks up a publish-branch if\n> > branch.*.pushremote is configured. What happened to the case when\n> > remote.pushdefault is configured?\n> \n> What happens when branch.*.remote is not configured for @{upstream}? The same\n> thing.\n\nI don't know if that is a good comparison.\n\nIn other threads, the discussed meaning of @{publish} was something like\n\"the tracking branch of the ref you would push to if you ran 'git push'\nwithout arguments\".\n\nThat is consistent with @{upstream} being \"the tracking branch of the\nref you would pull from with 'git pull'\". But \"git pull\" without a\nbranch.*.remote will do nothing, so \"what pull would do\" is the same as\n\"what you have configured in your branch.*.remote\".\n\nWhereas \"git push\" does not depend on having branch.*.pushremote\nconfigured. Its behavior is based on push.default and push refspecs, so\n\"what push would do\" must take that into account.\n\n> It might be useful to visualize what would be the name of the branch when\n> pushing it (without a refspec) even if the publish branch hasn't been\n> configured, but I think the code would be much more coplicated, and it would\n> break symetry with @{upstream}, besides, the user can just do 'git push -p\n> branch', and from that moment on it will be visible.\n\nIt is more complicated (see the patches that Junio had at\njk/branch-at-publish), but I think it is more likely to do what the user\nexpects.\n\nFor instance, it looks like your @{publish} requires config like:\n\n  [branch \"master\"]\n  pushremote = foo\n  push = refs/heads/bar\n\nto operate. Setting \"pushremote\" affects what \"git push\" does; it will\ngo to the \"foo\" remote. But the branch.master.push setting does not do\nanything to \"git push\". Only a push refspec (or push.default setting)\nwill change that. So the \"branch.*.push\" must be kept in sync manually\n(perhaps by running \"git push -p\").\n\nWhereas if @{publish} means \"where you would push to\", then\n\"branch.*.push\" does not need to exist at all. The values can be taken\nautomatically from the other push settings.\n\n-Peff\n\nPS I first tried just setting \"branch.master.pushremote\" without setting\n   \"branch.master.push\". This results in a segfault, as branch_get()\n   assumes that push_name is always set and tries to xstrdup() it.\n"},{"id":"238695","messageId":"20140411134801.GB5871@nysa.casa.local","threadId":"36387","inReplyTo":"20140411111750.GA28858@sigill.intra.peff.net","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T13:48:01Z","receivedAt":"2014-04-11T13:48:01Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Jeff King wrote:\n> On Thu, Apr 10, 2014 at 05:36:59PM -0500, Felipe Contreras wrote:\n> \n> > > I noticed that this only picks up a publish-branch if\n> > > branch.*.pushremote is configured. What happened to the case when\n> > > remote.pushdefault is configured?\n> > \n> > What happens when branch.*.remote is not configured for @{upstream}? The same\n> > thing.\n> \n> I don't know if that is a good comparison.\n\nI think it is. @{publish} is like @{upstream}. Period.\n\n> In other threads, the discussed meaning of @{publish} was something like\n> \"the tracking branch of the ref you would push to if you ran 'git push'\n> without arguments\".\n\nAnd I disagree.\n\n> That is consistent with @{upstream} being \"the tracking branch of the\n> ref you would pull from with 'git pull'\". But \"git pull\" without a\n> branch.*.remote will do nothing, so \"what pull would do\" is the same as\n> \"what you have configured in your branch.*.remote\".\n> \n> Whereas \"git push\" does not depend on having branch.*.pushremote\n> configured. Its behavior is based on push.default and push refspecs, so\n> \"what push would do\" must take that into account.\n\nYes, but we are not talking about 'git push', we are talking about\n@{publish}.\n\nI think of @{publish} as \"the branch the user has configured to push\nto\"; it overrides all other configurations (push.default and push\nrefspecs). I wouldn't mind having a @{push} *in addition* to @{publish}\nthat would have the behavior you mention, but for @{publish} I'm pretty\ncertain the behavior I want is that it maps *directly* to what the user\nhas configured.\n\nSimilarly, I don't want 'git branch -vv' to show @{push}; it would be a\nmess to show something on all the branches, probably origin/$branch, and\nprobably all \"ahead/behind\". I want it to show @{publish}, so only the\nbranches the user has *explicitly* configured.\n\n> > It might be useful to visualize what would be the name of the branch when\n> > pushing it (without a refspec) even if the publish branch hasn't been\n> > configured, but I think the code would be much more coplicated, and it would\n> > break symetry with @{upstream}, besides, the user can just do 'git push -p\n> > branch', and from that moment on it will be visible.\n> \n> It is more complicated (see the patches that Junio had at\n> jk/branch-at-publish), but I think it is more likely to do what the user\n> expects.\n> \n> For instance, it looks like your @{publish} requires config like:\n> \n>   [branch \"master\"]\n>   pushremote = foo\n>   push = refs/heads/bar\n> \n> to operate. Setting \"pushremote\" affects what \"git push\" does; it will\n> go to the \"foo\" remote. But the branch.master.push setting does not do\n> anything to \"git push\". Only a push refspec (or push.default setting)\n> will change that. So the \"branch.*.push\" must be kept in sync manually\n> (perhaps by running \"git push -p\").\n> \n> Whereas if @{publish} means \"where you would push to\"\n\nIt doesn't mean that to me.\n\nFor the record, I've been thinking about this for a long long time, and\nI argued for @{push} and @{publish} long before you discussed this in\nJanuary (which apparently you forgot). I implemented this more than half\na year ago, and have been using it since; it works great. The problem of\ntriangular workflows is pretty much solved for me.\n\n-- \nFelipe Contreras\n"},{"id":"238698","messageId":"20140411142504.GA25376@nysa.casa.local","threadId":"36387","inReplyTo":"vpqtxa0jkfo.fsf@anie.imag.fr","subject":"Re: [PATCH v2 0/9] Introduce publish tracking branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T14:25:04Z","receivedAt":"2014-04-11T14:25:04Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Matthieu Moy wrote:\n> Felipe Contreras <felipe.contreras@gmail.com> writes:\n> \n> > As it has been discussed before, our support for triangular workflows is\n> > lacking, and the following patch series aims to improve that situation.\n> \n> I'm not a heavy user of triangular workflow, so I'm not in the best\n> position to comment (and I have no time for a real review, sorry).\n> \n> On overall, I do like the change. I played a bit with it, and do not\n> understand what \"git push\" does:\n> \n>   $ git status\n>   On branch master\n>   Your branch is ahead of 'origin/new' by 4 commits.\n>     (use \"git push\" to publish your local commits)\n> \n> => OK, it's using the publish branch to tell me whether I should push.\n> \n>   $ git push -v\n>   Pushing to /tmp/git\n>   To /tmp/git\n>    = [up to date]      master -> master\n>   updating local tracking ref 'refs/remotes/origin/master'\n>   Everything up-to-date\n> \n> => Err, it still pushes to the upstream branch ... Wasn't that the point\n> of the change to push to publish? Did I do something wrong?\n\nMy patch series only affects push.default=simple, perhaps you have a\ndifferent configuration.\n\nMaybe we want the publish branch to override any push.default, so:\n\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -195,11 +195,7 @@ static void setup_push_current(struct remote *remote, struct branch *branch)\n static void setup_push_simple(struct remote *remote, struct branch *branch,\n                int triangular)\n {\n-       if (branch->push_name) {\n-               struct strbuf refspec = STRBUF_INIT;\n-               strbuf_addf(&refspec, \"%s:%s\", branch->name, branch->push_name);\n-               add_refspec(refspec.buf);\n-       } else if (triangular) {\n+       if (triangular) {\n                setup_push_current(remote, branch);\n        } else {\n                setup_push_upstream(remote, branch, triangular);\n@@ -260,8 +256,16 @@ static struct branch *get_current_branch(struct remote *remote)\n \n static void setup_default_push_refspecs(struct remote *remote)\n {\n+       struct branch *branch = branch_get(NULL);\n        int triangular = is_workflow_triangular(remote);\n \n+       if (branch && branch->push_name) {\n+               struct strbuf refspec = STRBUF_INIT;\n+               strbuf_addf(&refspec, \"%s:%s\", branch->name, branch->push_name);\n+               add_refspec(refspec.buf);\n+               return;\n+       }\n+\n        switch (push_default) {\n        default:\n        case PUSH_DEFAULT_UNSPECIFIED:\n\n-- \nFelipe Contreras\n"},{"id":"238713","messageId":"vpqd2gng4nl.fsf@anie.imag.fr","threadId":"36387","inReplyTo":"20140411142504.GA25376@nysa.casa.local","subject":"Re: [PATCH v2 0/9] Introduce publish tracking branch","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2014-04-11T17:25:02Z","receivedAt":"2014-04-11T17:25:02Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Felipe Contreras <felipe.contreras@gmail.com> writes:\n\n> My patch series only affects push.default=simple, perhaps you have a\n> different configuration.\n\nGood catch. I have push.default=upstream (essentially for compatibility\nwith old Git versions, I'd prefer simple actually).\n\n> Maybe we want the publish branch to override any push.default, so:\n\nNot sure actually. If a user says \"push.default=upstream\", it seems\nweird to push to something other than upstream indeed. What's clear to\nme is that your patch in its current form clearly makes \"simple\" a much\nbetter default than \"upstream\" (good news, it it the default!).\n\nThat said, the advice given by \"git status\" is clearly wrong:\n\n> >   $ git status\n> >   On branch master\n> >   Your branch is ahead of 'origin/new' by 4 commits.\n> >     (use \"git push\" to publish your local commits)\n\nIt should say (use \"git push origin new\" to publish your local commits)\nwith push.default=upstream and the current behavior of the patch.\n\nPerhaps argumentless \"git push\" could warn when push.default=upstream\nand branch.<name>.publish is configured, I'm not sure.\n\nSorry, more questions and \"I'm not sure\" than actual suggestion :-(.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"238736","messageId":"53483f7984cb8_2c1f6e72ecc0@nysa.notmuch","threadId":"36387","inReplyTo":"vpqd2gng4nl.fsf@anie.imag.fr","subject":"Re: [PATCH v2 0/9] Introduce publish tracking branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T19:16:09Z","receivedAt":"2014-04-11T19:16:09Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Matthieu Moy wrote:\n> Felipe Contreras <felipe.contreras@gmail.com> writes:\n> \n> > My patch series only affects push.default=simple, perhaps you have a\n> > different configuration.\n> \n> Good catch. I have push.default=upstream (essentially for compatibility\n> with old Git versions, I'd prefer simple actually).\n> \n> > Maybe we want the publish branch to override any push.default, so:\n> \n> Not sure actually. If a user says \"push.default=upstream\", it seems\n> weird to push to something other than upstream indeed. What's clear to\n> me is that your patch in its current form clearly makes \"simple\" a much\n> better default than \"upstream\" (good news, it it the default!).\n\nAs you said in another email; that's just the default. If the user explicitely\ntold Git to use certain branch (git push -p), Git should use that branch.\n\n> That said, the advice given by \"git status\" is clearly wrong:\n> \n> > >   $ git status\n> > >   On branch master\n> > >   Your branch is ahead of 'origin/new' by 4 commits.\n> > >     (use \"git push\" to publish your local commits)\n> \n> It should say (use \"git push origin new\" to publish your local commits)\n> with push.default=upstream and the current behavior of the patch.\n> \n> Perhaps argumentless \"git push\" could warn when push.default=upstream\n> and branch.<name>.publish is configured, I'm not sure.\n> \n> Sorry, more questions and \"I'm not sure\" than actual suggestion :-(.\n\nI believe in v3 of the patch series \"git push\" will actually do it correctly.\n\nCheers.\n\n-- \nFelipe Contreras\n"},{"id":"238734","messageId":"xmqqsipjsm8c.fsf@gitster.dls.corp.google.com","threadId":"36387","inReplyTo":"20140411111750.GA28858@sigill.intra.peff.net","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-11T19:24:35Z","receivedAt":"2014-04-11T19:24:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> For instance, it looks like your @{publish} requires config like:\n>\n>   [branch \"master\"]\n>   pushremote = foo\n>   push = refs/heads/bar\n>\n> to operate. Setting \"pushremote\" affects what \"git push\" does; it will\n> go to the \"foo\" remote.\n\nOK, and the same thing would happen if branch.*.pushremote is not\nset for any branch, but remote.pushdefault is set to 'foo', right?\n\n> But the branch.master.push setting does not do\n> anything to \"git push\".\n\nI am not sure I understand this.  I thought that the desire behind\nthe branch.*.push is to allow something like:\n\n\t... other things in the config ...\n\t[remote]\n        \tpushdefault = foo\n\t[remote \"foo\"]\n\t\turl = ...\n        \tpush = +refs/heads/*:refs/remotes/satellite/*\n                fetch = +refs/heads/*:refs/remotes/foo/*\n\t[branch \"master\"]\n\t\t; pushremote = foo\n        \tpush = refs/heads/bar\n\nso that \"git push\" on 'master' will override the more generic \"all\nlocal branches here will go to their remote-tracking hierarchy for\nthis satellite\" refspec.  And in that sense branch.master.push would\ndo something to \"git push\".\n\nI personally think that kind of override adds any more values than\nit causes confusion, so I think it is OK not to support such uses of\nbranch.*.push at all.  A configuration without branch.master.push\nmay cause \"git push\" on 'master' to update refs/heads/master at the\nremote, and if we fetched from 'foo' immediately after pushing\nthere, it would come back as refs/remotes/foo/master, and it would\nbe natural for users to expect that master@{publish} would be\nrefs/remotes/foo/master in such a case.\n\nIf we supported setting branch.master.push to refs/heads/bar, then\nmaster would be pushed as refs/heads/bar over there and it would\ncome back as refs/remotes/foo/bar here, so I would expect that to be\nthe value of master@{publish}.\n\n> Only a push refspec (or push.default setting)\n> will change that. So the \"branch.*.push\" must be kept in sync manually\n> (perhaps by running \"git push -p\").\n>\n> Whereas if @{publish} means \"where you would push to\", then\n> \"branch.*.push\" does not need to exist at all. The values can be taken\n> automatically from the other push settings.\n>\n> -Peff\n>\n> PS I first tried just setting \"branch.master.pushremote\" without setting\n>    \"branch.master.push\". This results in a segfault, as branch_get()\n>    assumes that push_name is always set and tries to xstrdup() it.\n"},{"id":"238741","messageId":"5348478e1bc46_2c1f6e72ecbb@nysa.notmuch","threadId":"36387","inReplyTo":"xmqqsipjsm8c.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T19:50:38Z","receivedAt":"2014-04-11T19:50:38Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Junio C Hamano wrote:\n> Jeff King <peff@peff.net> writes:\n> \n> > For instance, it looks like your @{publish} requires config like:\n> >\n> >   [branch \"master\"]\n> >   pushremote = foo\n> >   push = refs/heads/bar\n> >\n> > to operate. Setting \"pushremote\" affects what \"git push\" does; it will\n> > go to the \"foo\" remote.\n> \n> OK, and the same thing would happen if branch.*.pushremote is not\n> set for any branch, but remote.pushdefault is set to 'foo', right?\n> \n> > But the branch.master.push setting does not do\n> > anything to \"git push\".\n> \n> I am not sure I understand this.  I thought that the desire behind\n> the branch.*.push is to allow something like:\n> \n> \t... other things in the config ...\n> \t[remote]\n>         \tpushdefault = foo\n> \t[remote \"foo\"]\n> \t\turl = ...\n>         \tpush = +refs/heads/*:refs/remotes/satellite/*\n>                 fetch = +refs/heads/*:refs/remotes/foo/*\n> \t[branch \"master\"]\n> \t\t; pushremote = foo\n>         \tpush = refs/heads/bar\n> \n> so that \"git push\" on 'master' will override the more generic \"all\n> local branches here will go to their remote-tracking hierarchy for\n> this satellite\" refspec.  And in that sense branch.master.push would\n> do something to \"git push\".\n\nIn my patches the above doesn't work; branch.master.push doesn't do anything if\n.pushremote isn't there.\n\nI'm always thinking from the common user's point of view, and the common user\ndoesn't know what branch.master.push is, but he knows he did\n`git branch -p foo/bar master` (or something like that), and\n`git branch -v` would corroborate that.\n\nSo you would have something like this:\n\n\t[remote \"foo\"]\n\t\turl = ...\n\t\tpush = +refs/heads/*:refs/remotes/satellite/*\n\t\tfetch = +refs/heads/*:refs/remotes/foo/*\n\t[branch \"master\"]\n\t\tpushremote = foo\n\t\tpush = refs/heads/bar\n\n> I personally think that kind of override adds any more values than\n> it causes confusion, so I think it is OK not to support such uses of\n> branch.*.push at all.  A configuration without branch.master.push\n> may cause \"git push\" on 'master' to update refs/heads/master at the\n> remote,\n\nHow? If branch.master.push is not configured, then \"git push\" would push\n'master' to refs/remotes/satellite/master on the remote.\n\n-- \nFelipe Contreras\n"},{"id":"238767","messageId":"20140412112344.GA14820@sigill.intra.peff.net","threadId":"36387","inReplyTo":"20140411134801.GB5871@nysa.casa.local","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-04-12T11:23:44Z","receivedAt":"2014-04-12T11:23:44Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Apr 11, 2014 at 08:48:01AM -0500, Felipe Contreras wrote:\n\n> I think of @{publish} as \"the branch the user has configured to push\n> to\"; it overrides all other configurations (push.default and push\n> refspecs). I wouldn't mind having a @{push} *in addition* to @{publish}\n> that would have the behavior you mention, but for @{publish} I'm pretty\n> certain the behavior I want is that it maps *directly* to what the user\n> has configured.\n\nI guess I don't understand why we need \"branch.*.push\" when we already\nhave push refspecs that do the same thing (and are more powerful, as\nthey can glob).\n\nThe behavior you describe is not useful to me, as it would mean having\nto manage branch.*.push as a duplicate of the information that is\nalready found in \"remote.$(git config remote.pushdefault).push\".  I do\nnot mind if the two co-exist, but please do not take the short @{p} as\nyour series does (not only because of @{push}, but also because @{pull}\nhas been mentioned elsewhere as a possible other name for @{upstream}).\n\n-Peff\n"},{"id":"238769","messageId":"20140412114212.GB14820@sigill.intra.peff.net","threadId":"36387","inReplyTo":"xmqqsipjsm8c.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-04-12T11:42:12Z","receivedAt":"2014-04-12T11:42:12Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Apr 11, 2014 at 12:24:35PM -0700, Junio C Hamano wrote:\n\n> > But the branch.master.push setting does not do\n> > anything to \"git push\".\n> \n> I am not sure I understand this.  I thought that the desire behind\n> the branch.*.push is to allow something like:\n> \n> \t... other things in the config ...\n> \t[remote]\n>         \tpushdefault = foo\n> \t[remote \"foo\"]\n> \t\turl = ...\n>         \tpush = +refs/heads/*:refs/remotes/satellite/*\n>                 fetch = +refs/heads/*:refs/remotes/foo/*\n> \t[branch \"master\"]\n> \t\t; pushremote = foo\n>         \tpush = refs/heads/bar\n> \n> so that \"git push\" on 'master' will override the more generic \"all\n> local branches here will go to their remote-tracking hierarchy for\n> this satellite\" refspec.  And in that sense branch.master.push would\n> do something to \"git push\".\n\nAh, I see. If I set \"push.default\" to \"upstream\", then the config I\nshowed before _does_ affect \"git push\". But I do not usually do that. I\nhave push.default set to \"current\", and sometimes override it using push\nrefspecs on certain repositories.\n\nAnd that is why I find branch.*.push and Felipe's @{publish} useless for\nmy workflow. Pushes already go where I want them to, and I just want a\nway to ask git to perform that config resolution for me. Whereas\nFelipe's workflow is (I think) something like:\n\n  # make a new branch...\n  git checkout -b topic origin/master\n\n  # now publish our branch, and remember our publishing point\n  git push -p my-repo topic\n\n  # and now further pushes automatically go to my-repo/topic\n  git push\n\nI can see there is some value in that override if you do things like:\n\n  git push -p my-repo topic:some-other-name\n\nbecause the \"-p\" means \"remember this other name I gave\".\n\nI would think in such a workflow that most of your branches would end up\nwith publish config, though. And therefore @{publish} would become\nequivalent to \"where you would push\". But Felipe indicated that he would\nnot want \"branch -vv\" to match where all branches would be pushed, but\nrather only those that were specifically configured. So maybe I do not\nunderstand his workflow after all.\n\n-Peff\n"},{"id":"238774","messageId":"53494f09531cc_c9914c7308a@nysa.notmuch","threadId":"36387","inReplyTo":"20140412112344.GA14820@sigill.intra.peff.net","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-12T14:34:49Z","receivedAt":"2014-04-12T14:34:49Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Jeff King wrote:\n> On Fri, Apr 11, 2014 at 08:48:01AM -0500, Felipe Contreras wrote:\n> \n> > I think of @{publish} as \"the branch the user has configured to push\n> > to\"; it overrides all other configurations (push.default and push\n> > refspecs). I wouldn't mind having a @{push} *in addition* to @{publish}\n> > that would have the behavior you mention, but for @{publish} I'm pretty\n> > certain the behavior I want is that it maps *directly* to what the user\n> > has configured.\n> \n> I guess I don't understand why we need \"branch.*.push\" when we already\n> have push refspecs that do the same thing (and are more powerful, as\n> they can glob).\n\nReally? I think it's pretty simple.\n\nI have a configuration similar to the following\n\n  [remote \"gh\"]\n  \turl = git@github.com:felipec/git.git\n  [branch \"master\"]\n  \tpushremote = gh\n  \tpush = refs/heads/master\n  [branch \"fc/master\"]\n  \tpushremote = gh\n  \tpush = refs/heads/fc/master\n  [branch \"up/publish\"]\n  \tpushremote = gh\n  \tpush = refs/heads/fc/publish\n\nNow, if I didn't, I could create it easily with something like this:\n\n  % git push -p gh master\n  % git push -p gh fc/master\n  % git push -p gh up/publish:fc/publish\n\nHow would that translate to push refspecs?\n\n  [remote \"gh\"]\n  \turl = git@github.com:felipec/git.git\n  \tpush = refs/heads/master:refs/heads/master\n  \tpush = refs/heads/fc/master:refs/heads/fc/master\n  \tpush = refs/heads/up/master:refs/heads/fc/publish\n  [branch \"master\"]\n  \tpushremote = gh\n  [branch \"fc/master\"]\n  \tpushremote = gh\n  [branch \"up/publish\"]\n  \tpushremote = gh\n\nSo in theory it could be possible to make it work without branch.x.push.\n\nIf I run `git branch -v`, it would show the publish branch correctly.\n\n  master cee0c27 [origin/master, gh/master] Git 1.9.1\n\nHowever, if I have a configuration like this:\n\n  [remote \"gh\"]\n  \turl = git@github.com:felipec/git.git\n  \tpush = refs/heads/*:refs/heads/*\n\nGit would still show the same publish branch, even though the user never\nexplicitely told it do to so (e.g. with `git push -p`). To me this is not OK;\nif I haven't specifically set a publish branch, it shouldn't be shown.\n\nAnd then, what happens when I do:\n\n  % git branch --unset-upstream up/publish\n\nOr:\n\n  % git branch -d up/publish\n\nShould Git remove the appropriate push refspec? What if that refspec was\nmanually added by the user before the concept of the publish branch even\nexisted?\n\nTo me using the refspec just adds more complications, and it's not symetric\nwith @{upstream}. With branch.x.push, the symetry is very much there; the code\nis similar to @{upstream}, the configuration as well, and what the user expects\ntoo; it's much simpler. Also, the user can clearly see what are his manually\nconfigured refspecs, as opposed to the ones added by `git push -p`.\n\nIt's just neat.\n\n-- \nFelipe Contreras\n"},{"id":"238775","messageId":"5349562bb7ae4_c9914c7308f9@nysa.notmuch","threadId":"36387","inReplyTo":"20140412114212.GB14820@sigill.intra.peff.net","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-12T15:05:15Z","receivedAt":"2014-04-12T15:05:15Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Jeff King wrote:\n> On Fri, Apr 11, 2014 at 12:24:35PM -0700, Junio C Hamano wrote:\n> \n> > > But the branch.master.push setting does not do\n> > > anything to \"git push\".\n> > \n> > I am not sure I understand this.  I thought that the desire behind\n> > the branch.*.push is to allow something like:\n> > \n> > \t... other things in the config ...\n> > \t[remote]\n> >         \tpushdefault = foo\n> > \t[remote \"foo\"]\n> > \t\turl = ...\n> >         \tpush = +refs/heads/*:refs/remotes/satellite/*\n> >                 fetch = +refs/heads/*:refs/remotes/foo/*\n> > \t[branch \"master\"]\n> > \t\t; pushremote = foo\n> >         \tpush = refs/heads/bar\n> > \n> > so that \"git push\" on 'master' will override the more generic \"all\n> > local branches here will go to their remote-tracking hierarchy for\n> > this satellite\" refspec.  And in that sense branch.master.push would\n> > do something to \"git push\".\n> \n> Ah, I see. If I set \"push.default\" to \"upstream\", then the config I\n> showed before _does_ affect \"git push\". But I do not usually do that. I\n> have push.default set to \"current\", and sometimes override it using push\n> refspecs on certain repositories.\n> \n> And that is why I find branch.*.push and Felipe's @{publish} useless for\n> my workflow. Pushes already go where I want them to, and I just want a\n> way to ask git to perform that config resolution for me. Whereas\n> Felipe's workflow is (I think) something like:\n> \n>   # make a new branch...\n>   git checkout -b topic origin/master\n> \n>   # now publish our branch, and remember our publishing point\n>   git push -p my-repo topic\n> \n>   # and now further pushes automatically go to my-repo/topic\n>   git push\n> \n> I can see there is some value in that override if you do things like:\n> \n>   git push -p my-repo topic:some-other-name\n> \n> because the \"-p\" means \"remember this other name I gave\".\n> \n> I would think in such a workflow that most of your branches would end up with\n> publish config, though. And therefore @{publish} would become equivalent to\n> \"where you would push\".\n\n> But Felipe indicated that he would not want \"branch -vv\" to match where all\n> branches would be pushed, but rather only those that were specifically\n> configured. So maybe I do not understand his workflow after all.\n\nIt's a pretty typical triangular workflow, with a touch of fork maintainership.\n\nHere are some types of branches I have:\n\n* master [origin/master, gh/master] Git 1.9.1\n\nMy main master branch, I use it as a base point for many other branches. I\ndon't use origin/master because that's a moving target.\n\n* dev/remote/hg-extra [master] remote-hg: store extra hg information in notes\n\nA development branch. I don't publish those, therefore no @{publish}.\n\n* fc/publish [fc/branch/nice-verbose, gh/fc/publish] sha1_name: add support for @{publish} marks\n\nA branch that is all good. I publish those, and use them for git-fc (my fork).\nI think they should be in Git's core, but haven't been merged for some reason\nor another.\n\nNotice that the upstream branch is another local branch, not master. Strictly\nspeaking it's not an \"upstream\" branch, but I want 'git rebase' to use that as\nthe base point. Another @{base} concept might be more appropriate, but those\npatches are a different story.\n\n* up/publish [master] sha1_name: add support for @{publish} marks\n\nA branch that should be sent upstream. I don't publish those.\n\nNotice up/publish is different from fc/publish because the later depends on\nanother fc/* branch, which wasn't accepted upstream.\n\n* fc/master [gh/fc/master] prompt: fix missing file errors in zsh\n\nMy main branch, used for git-fc. I merge Git's master, and cherry-pick various\nfixes, so it has always the latest and greatest stuff.\n\nNotice that 'gh/fc/master' is the publish branch, there is no upstream.\n\n* pu [] Merge branch 'travis-ci' into pu\n\nSimilar to Junio's pu, I use `git reintegrate` to generate this branch using\n'master' as the baseline, and merging all the fc/* branches. The result should\nbe identical to fc/master, if not, we are missing something from upstream, or\nthere's something missing in the fc/* branches.\n\nIt's not published, and has no upstream.\n\n\nAs you can see; some branches are published, others are not. The ones that are\nnot published don't have a @{publish}, and `git branch -v` doesn't show them.\nWhy is that hard to understand?\n\n-- \nFelipe Contreras\n"},{"id":"238875","messageId":"20140415054356.GA4772@sigill.intra.peff.net","threadId":"36387","inReplyTo":"5349562bb7ae4_c9914c7308f9@nysa.notmuch","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-04-15T05:43:57Z","receivedAt":"2014-04-15T05:43:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Apr 12, 2014 at 10:05:15AM -0500, Felipe Contreras wrote:\n\n> As you can see; some branches are published, others are not. The ones that are\n> not published don't have a @{publish}, and `git branch -v` doesn't show them.\n> Why is that hard to understand?\n\nDo you ever push the unpublished branches anywhere at all? If not, then\nyou would not have a tracking branch. E.g., git _would_ push to remote\n\"gh\", branch \"refs/heads/topic\", but there is no remote tracking branch\n\"refs/remotes/gh/topic\", because you have never actually pushed there.\nSo there is no @{publish} branch.\n\nOr do you have some branches in a state where they are \"pushed, but not\npublished\"? It wasn't clear to me from your example if your \"pu\" or\n\"dev/remote/hg-extra\" ever get pushed.\n\nI do not use \"git branch -v\" myself, so I don't personally care that\nmuch how it behaves. But I do use a separate script that does the same\nthing, and I would want it to show the ahead/behind relationship between\neach branch and where it would be pushed to (and as I said, I define\nmine with refspecs). Right now it uses nasty hackery to guess at where\nthings will be pushed, but ideally it would ask git via @{push} or some\nsimilar mechanism.\n\nIf the former (you do not actually push them), then I think the\nsemantics I am looking for and the ones you want would coincide. If not,\nthen I think we are really talking about two different things.\n\n-Peff\n"},{"id":"239086","messageId":"5351b571421e4_3497cdf30c78@nysa.notmuch","threadId":"36387","inReplyTo":"20140415054356.GA4772@sigill.intra.peff.net","subject":"Re: [PATCH v2 6/9] branch: display publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-18T23:29:53Z","receivedAt":"2014-04-18T23:29:53Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Jeff King wrote:\n> On Sat, Apr 12, 2014 at 10:05:15AM -0500, Felipe Contreras wrote:\n> \n> > As you can see; some branches are published, others are not. The ones that are\n> > not published don't have a @{publish}, and `git branch -v` doesn't show them.\n> > Why is that hard to understand?\n> \n> Do you ever push the unpublished branches anywhere at all? If not, then\n> you would not have a tracking branch. E.g., git _would_ push to remote\n> \"gh\", branch \"refs/heads/topic\", but there is no remote tracking branch\n> \"refs/remotes/gh/topic\", because you have never actually pushed there.\n> So there is no @{publish} branch.\n> \n> Or do you have some branches in a state where they are \"pushed, but not\n> published\"? It wasn't clear to me from your example if your \"pu\" or\n> \"dev/remote/hg-extra\" ever get pushed.\n\nSometimes I do push these branches, but I don't understand what you mean by\n\"pushed state\". When you push something no states are changed. Say I do:\n\n % git push tmp wip-feature\n % git push backup wip-feature\n\nSo I pushed a branch to two different repositories, the former one might not\neven exist any more. Who cares? No states have changed. The fact that this\nbranch was pushed doesn't change anything about the nature of the branch.\n\n> I do not use \"git branch -v\" myself,\n\nMe neither (at least not the upstream version).\n\n> so I don't personally care that much how it behaves. But I do use a separate\n> script that does the same thing, and I would want it to show the ahead/behind\n> relationship between each branch and where it would be pushed to (and as I\n> said, I define mine with refspecs). Right now it uses nasty hackery to guess\n> at where things will be pushed, but ideally it would ask git via @{push} or\n> some similar mechanism.\n\nYes, but you can push a branch to many locations, to which one should the\nscript show the tracking information? IMO it should be the one location you\nexplicitly configured, and in the case of \"wip-feature\" is \"no location\".\n\n> If the former (you do not actually push them), then I think the semantics I\n> am looking for and the ones you want would coincide. If not, then I think we\n> are really talking about two different things.\n\nI ask again what is so difficult about the notion that there are two kinds of branches?\n\nA)\n\n% git checkout ready-feature\n% git push tmp ready-feature\n% git push -p github ready-feature\n% git push backup ready-feature\n\nB)\n\n% git checkout wip-feature\n% git push tmp wip-feature\n% git push backup wip-feature\n\nIn a haste these branches might not look very different, but conceptually they\nare. One has a location where it is publicly visible, and where you wish to\npush regularly, the other one doesn't.\n\nWhether you push a branch or not is not really important, it's whether or not\nthe branch has a *special* place where you push to.\n\n-- \nFelipe Contreras\n"}]}