{"thread":{"id":"36393","subject":"[PATCH v3 0/8] Introduce publish tracking branch","startedAt":"2014-04-11T17:59:03Z","lastAt":"2014-04-11T19:48:39Z","messageCount":11,"participants":["Felipe Contreras","Matthieu Moy","Junio C Hamano"],"isPatch":true,"patchVersion":3,"patchTotal":8},"messages":[{"id":"238718","messageId":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","threadId":"36393","inReplyTo":null,"subject":"[PATCH v3 0/8] Introduce publish tracking branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T17:59:03Z","receivedAt":"2014-04-11T17:59:03Z","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\nSo, let's introduce the concept of a publish branch (similar to upstream),\nwhich is used by push to determine the branch to push to, if the user hasn't\nspecified any manually, and overrides other configurations, such as\n(push.default, and remote.$name.push).\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 v2:\n\n * Added documentation for branch.$name.push\n * Override other configurations (such as push.default)\n * Fix crash when branch.$name.pushremote is not present\n * Added push tests\n * Remove unnecessary cleanups\n * Other test fixes\n * Fixes from Junio\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5f4d793..1569353 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -764,6 +764,13 @@ branch.<name>.mergeoptions::\n \toption values containing whitespace characters are currently not\n \tsupported.\n \n+branch.<name>.push::\n+\tDefines, together with branch.<name>.pushremote, the publish branch for\n+\tthe given branch. It tells 'git push' which branch to push to, and\n+\toverrides any other configurations, such as push.default. It also tells\n+\tcommands such as 'git status' and 'git branch' which remote branch to\n+\tuse for tracking information (commits ahead and behind).\n+\n branch.<name>.rebase::\n \tWhen true, rebase the branch <name> on top of the fetched branch,\n \tinstead of merging the default branch from the default remote when\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 9e61b8f..172e843 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -155,11 +155,20 @@ 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@@ -189,23 +198,11 @@ 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-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@@ -243,23 +240,9 @@ static int is_workflow_triangular(struct remote *remote)\n \treturn (fetch_remote && fetch_remote != remote);\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 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+\tstruct branch *branch = branch_get(NULL);\n \tint triangular = is_workflow_triangular(remote);\n \n \tswitch (push_default) {\n@@ -273,15 +256,18 @@ static void setup_default_push_refspecs(struct remote *remote)\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_SIMPLE:\n-\t\tsetup_push_simple(remote, get_current_branch(remote), triangular);\n+\t\tif (triangular)\n+\t\t\tsetup_push_current(remote, branch);\n+\t\telse\n+\t\t\tsetup_push_upstream(remote, branch, triangular);\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_UPSTREAM:\n-\t\tsetup_push_upstream(remote, get_current_branch(remote), triangular);\n+\t\tsetup_push_upstream(remote, branch, triangular);\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_CURRENT:\n-\t\tsetup_push_current(remote, get_current_branch(remote));\n+\t\tsetup_push_current(remote, branch);\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_NOTHING:\n@@ -455,7 +441,14 @@ static int do_push(const char *repo, int flags)\n \t}\n \n \tif (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {\n-\t\tif (remote->push_refspec_nr) {\n+\t\tstruct branch *branch = branch_get(NULL);\n+\t\t/* Is there a publish branch */\n+\t\tif (branch->pushremote_name && !strcmp(remote->name, branch->pushremote_name) &&\n+\t\t\t\tbranch && branch->push_name) {\n+\t\t\tstruct strbuf refspec = STRBUF_INIT;\n+\t\t\tstrbuf_addf(&refspec, \"%s:%s\", branch->name, branch->push_name);\n+\t\t\tadd_refspec(refspec.buf);\n+\t\t} else if (remote->push_refspec_nr) {\n \t\t\trefspec = remote->push_refspec;\n \t\t\trefspec_nr = remote->push_refspec_nr;\n \t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR))\ndiff --git a/remote.c b/remote.c\nindex 3437d1f..eda6192 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -1566,7 +1566,7 @@ struct branch *branch_get(const char *name)\n \t\t\t}\n \t\t}\n \t}\n-\tif (ret && ret->pushremote_name) {\n+\tif (ret && ret->pushremote_name && ret->push_name) {\n \t\tstruct remote *pushremote;\n \t\tpushremote = pushremote_get(ret->pushremote_name);\n \t\tret->push.src = xstrdup(ret->push_name);\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 67e0ab3..f9c528d 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1261,6 +1261,8 @@ test_expect_success 'push --follow-tag only pushes relevant tags' '\n '\n \n test_expect_success 'push --no-thin must produce non-thin pack' '\n+\ttest_when_finished \"git reset --hard v1.0\" &&\n+\n \tcat >>path1 <<\\EOF &&\n keep base version of path1 big enough, compared to the new changes\n later, in order to pass size heuristics in\n@@ -1277,4 +1279,66 @@ EOF\n \tgit push --no-thin --receive-pack=\"$rcvpck\" no-thin/.git refs/heads/master:refs/heads/foo\n '\n \n+test_expect_success 'push with publish branch' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push &&\n+\tcheck_push_result up_repo $the_first_commit heads/master &&\n+\tcheck_push_result down_repo $the_commit heads/for-john\n+'\n+\n+test_expect_success 'push with publish branch for different remote' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push up &&\n+\tcheck_push_result up_repo $the_commit heads/master &&\n+\tcheck_push_result down_repo $the_first_commit heads/master\n+'\n+\n+test_expect_success 'push with publish branch with pushdefault' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config remote.pushdefault up &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push &&\n+\tcheck_push_result up_repo $the_first_commit heads/master &&\n+\tcheck_push_result down_repo $the_commit heads/for-john\n+'\n+\n+test_expect_success 'push with publish branch with remote refspec' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config remote.down.push refs/heads/master:refs/heads/bad &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push &&\n+\tcheck_push_result up_repo $the_first_commit heads/master &&\n+\tcheck_push_result down_repo $the_commit heads/for-john\n+'\n+\n+test_expect_success 'push with publish branch with manual refspec' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push down master:good &&\n+\tcheck_push_result up_repo $the_first_commit heads/master &&\n+\tcheck_push_result down_repo $the_commit heads/good\n+'\n+\n test_done\n\n\nFelipe Contreras (8):\n  t5516 (fetch-push): fix test restoration\n  Add concept of 'publish' branch\n  branch: add --set-publish-to option\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/config.txt     |  7 ++++\n Documentation/git-branch.txt | 11 +++++++\n Documentation/git-push.txt   |  9 +++++-\n Documentation/revisions.txt  |  4 +++\n branch.c                     | 44 +++++++++++++++++++++++++\n branch.h                     |  2 ++\n builtin/branch.c             | 74 ++++++++++++++++++++++++++++++++++++++----\n builtin/push.c               | 11 ++++++-\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/t5516-fetch-push.sh        | 64 +++++++++++++++++++++++++++++++++++++\n t/t5534-push-publish.sh      | 70 ++++++++++++++++++++++++++++++++++++++++\n t/t6040-tracking-info.sh     |  5 +--\n transport.c                  | 28 ++++++++++------\n transport.h                  |  1 +\n 18 files changed, 463 insertions(+), 48 deletions(-)\n create mode 100755 t/t5534-push-publish.sh\n\n-- \n1.9.1+fc1\n"},{"id":"238719","messageId":"1397239151-2391-2-git-send-email-felipe.contreras@gmail.com","threadId":"36393","inReplyTo":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 1/8] t5516 (fetch-push): fix test restoration","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T17:59:04Z","receivedAt":"2014-04-11T17:59:04Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"We shouldn't modify the commits; it screws the following tests.\n\nSigned-off-by: Felipe Contreras <felipe.contreras@gmail.com>\n---\n t/t5516-fetch-push.sh | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex 67e0ab3..f4cf0db 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1261,6 +1261,8 @@ test_expect_success 'push --follow-tag only pushes relevant tags' '\n '\n \n test_expect_success 'push --no-thin must produce non-thin pack' '\n+\ttest_when_finished \"git reset --hard v1.0\" &&\n+\n \tcat >>path1 <<\\EOF &&\n keep base version of path1 big enough, compared to the new changes\n later, in order to pass size heuristics in\n-- \n1.9.1+fc1\n"},{"id":"238720","messageId":"1397239151-2391-3-git-send-email-felipe.contreras@gmail.com","threadId":"36393","inReplyTo":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 2/8] Add concept of 'publish' branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T17:59:05Z","receivedAt":"2014-04-11T17:59:05Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"The publish branch is the branch the user wants to push to, akin to the\nupstream branch, which is the branch the user wants to use as a\nbaseline. It overrides other configurations, such as push.default, and\nremote.<name>.push.\n\nThe 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 Documentation/config.txt |  7 ++++++\n builtin/push.c           |  9 ++++++-\n remote.c                 | 34 +++++++++++++++++++++-----\n remote.h                 |  4 ++++\n t/t5516-fetch-push.sh    | 62 ++++++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 109 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 5f4d793..1569353 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -764,6 +764,13 @@ branch.<name>.mergeoptions::\n \toption values containing whitespace characters are currently not\n \tsupported.\n \n+branch.<name>.push::\n+\tDefines, together with branch.<name>.pushremote, the publish branch for\n+\tthe given branch. It tells 'git push' which branch to push to, and\n+\toverrides any other configurations, such as push.default. It also tells\n+\tcommands such as 'git status' and 'git branch' which remote branch to\n+\tuse for tracking information (commits ahead and behind).\n+\n branch.<name>.rebase::\n \tWhen true, rebase the branch <name> on top of the fetched branch,\n \tinstead of merging the default branch from the default remote when\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 0e50ddb..2a78c2b 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -441,7 +441,14 @@ static int do_push(const char *repo, int flags)\n \t}\n \n \tif (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {\n-\t\tif (remote->push_refspec_nr) {\n+\t\tstruct branch *branch = branch_get(NULL);\n+\t\t/* Is there a publish branch */\n+\t\tif (branch->pushremote_name && !strcmp(remote->name, branch->pushremote_name) &&\n+\t\t\t\tbranch && branch->push_name) {\n+\t\t\tstruct strbuf refspec = STRBUF_INIT;\n+\t\t\tstrbuf_addf(&refspec, \"%s:%s\", branch->name, branch->push_name);\n+\t\t\tadd_refspec(refspec.buf);\n+\t\t} else if (remote->push_refspec_nr) {\n \t\t\trefspec = remote->push_refspec;\n \t\t\trefspec_nr = remote->push_refspec_nr;\n \t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR))\ndiff --git a/remote.c b/remote.c\nindex 5f63d55..eda6192 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 && ret->push_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);\ndiff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh\nindex f4cf0db..f9c528d 100755\n--- a/t/t5516-fetch-push.sh\n+++ b/t/t5516-fetch-push.sh\n@@ -1279,4 +1279,66 @@ EOF\n \tgit push --no-thin --receive-pack=\"$rcvpck\" no-thin/.git refs/heads/master:refs/heads/foo\n '\n \n+test_expect_success 'push with publish branch' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push &&\n+\tcheck_push_result up_repo $the_first_commit heads/master &&\n+\tcheck_push_result down_repo $the_commit heads/for-john\n+'\n+\n+test_expect_success 'push with publish branch for different remote' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push up &&\n+\tcheck_push_result up_repo $the_commit heads/master &&\n+\tcheck_push_result down_repo $the_first_commit heads/master\n+'\n+\n+test_expect_success 'push with publish branch with pushdefault' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config remote.pushdefault up &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push &&\n+\tcheck_push_result up_repo $the_first_commit heads/master &&\n+\tcheck_push_result down_repo $the_commit heads/for-john\n+'\n+\n+test_expect_success 'push with publish branch with remote refspec' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config remote.down.push refs/heads/master:refs/heads/bad &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push &&\n+\tcheck_push_result up_repo $the_first_commit heads/master &&\n+\tcheck_push_result down_repo $the_commit heads/for-john\n+'\n+\n+test_expect_success 'push with publish branch with manual refspec' '\n+\tmk_test up_repo heads/master &&\n+\tmk_test down_repo heads/master &&\n+\ttest_config remote.up.url up_repo &&\n+\ttest_config remote.down.url down_repo &&\n+\ttest_config branch.master.pushremote down &&\n+\ttest_config branch.master.push for-john &&\n+\tgit push down master:good &&\n+\tcheck_push_result up_repo $the_first_commit heads/master &&\n+\tcheck_push_result down_repo $the_commit heads/good\n+'\n+\n test_done\n-- \n1.9.1+fc1\n"},{"id":"238726","messageId":"1397239151-2391-4-git-send-email-felipe.contreras@gmail.com","threadId":"36393","inReplyTo":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 3/8] branch: add --set-publish-to option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T17:59:06Z","receivedAt":"2014-04-11T17:59:06Z","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                     | 44 +++++++++++++++++++++++++\n branch.h                     |  2 ++\n builtin/branch.c             | 57 ++++++++++++++++++++++++++++++---\n t/t3200-branch.sh            | 76 ++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 185 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..090b3d1 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -144,6 +144,50 @@ 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) &&\n+\t\t\tstarts_with(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;\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":"238721","messageId":"1397239151-2391-5-git-send-email-felipe.contreras@gmail.com","threadId":"36393","inReplyTo":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 4/8] push: add --set-publish option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T17:59:07Z","receivedAt":"2014-04-11T17:59:07Z","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/t5534-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/t5534-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 2a78c2b..172e843 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -525,6 +525,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/t5534-push-publish.sh b/t/t5534-push-publish.sh\nnew file mode 100755\nindex 0000000..2037026\n--- /dev/null\n+++ b/t/t5534-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":"238722","messageId":"1397239151-2391-6-git-send-email-felipe.contreras@gmail.com","threadId":"36393","inReplyTo":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 5/8] branch: display publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T17:59:08Z","receivedAt":"2014-04-11T17:59:08Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"It does it along the upstream branch, if any.\n\n* publish ... [master, gh/publish: ahead 1] ...\n  master  ... [master, gh/master] ...\n\nSigned-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":"238725","messageId":"1397239151-2391-7-git-send-email-felipe.contreras@gmail.com","threadId":"36393","inReplyTo":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 6/8] sha1_name: cleanup interpret_branch_name()","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T17:59:09Z","receivedAt":"2014-04-11T17:59:09Z","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":"238723","messageId":"1397239151-2391-8-git-send-email-felipe.contreras@gmail.com","threadId":"36393","inReplyTo":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 7/8] sha1_name: simplify track finding","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T17:59:10Z","receivedAt":"2014-04-11T17:59:10Z","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":"238724","messageId":"1397239151-2391-9-git-send-email-felipe.contreras@gmail.com","threadId":"36393","inReplyTo":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH v3 8/8] sha1_name: add support for @{publish} marks","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2014-04-11T17:59:11Z","receivedAt":"2014-04-11T17:59:11Z","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":"238729","messageId":"vpqeh13aegs.fsf@anie.imag.fr","threadId":"36393","inReplyTo":"1397239151-2391-1-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v3 0/8] Introduce publish tracking branch","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2014-04-11T18:49:39Z","receivedAt":"2014-04-11T18:49:39Z","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> * Override other configurations (such as push.default)\n\nI think I convinced myself that this is the right way to go since my\nlast message. After all, \"push.default\" is, by definition, just a\ndefault.\n\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -764,6 +764,13 @@ branch.<name>.mergeoptions::\n>  \toption values containing whitespace characters are currently not\n>  \tsupported.\n>  \n> +branch.<name>.push::\n> +\tDefines, together with branch.<name>.pushremote, the publish branch for\n> +\tthe given branch. It tells 'git push' which branch to push to, and\n> +\toverrides any other configurations, such as push.default. It also tells\n> +\tcommands such as 'git status' and 'git branch' which remote branch to\n> +\tuse for tracking information (commits ahead and behind).\n> +\n\nGood.\n\nI think this text should mention the @{publish} shorthand too. Stg like\n\"This branch can be refered to as @{publish} when specifying revision\n(see linkgit:gitrevisions[7]).\"\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"238739","messageId":"xmqqr453r6js.fsf@gitster.dls.corp.google.com","threadId":"36393","inReplyTo":"1397239151-2391-3-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH v3 2/8] Add concept of 'publish' branch","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-04-11T19:48:39Z","receivedAt":"2014-04-11T19:48:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Felipe Contreras <felipe.contreras@gmail.com> writes:\n\n> The publish branch is the branch the user wants to push to, akin to the\n> upstream branch, which is the branch the user wants to use as a\n> baseline. It overrides other configurations, such as push.default, and\n> remote.<name>.push.\n>\n> The upstream branch is:\n>\n>   branch.$name.remote\n>   branch.$name.merge\n\nThese two configuration variables are *NOT* the \"upstream branch\";\nit is more like \"the upstream branch of $name is _defined_ with\nthese two variables\".\n\nFor example, with:\n\n\t[remote \"foo\"]\n        \tfetch = refs/heads/*:refs/remotes/foo/*\n\t[branch \"bar\"]\n        \tremote = foo\n                merge = baz\n\nThe upstream branch of 'bar' is refs/remotes/foo/baz, and that is\ndetermined by consulting these two variables, branch.bar.remote and\nbranch.bar.merge to learn that we look at refs/heads/baz from foo\nand also refs/remotes/foo/baz is where we keep a copy of that.\n\n> The publish branch is:\n>\n>   branch.$name.pushremote\n>   branch.$name.push\n\nCan you give a description for \"the publish branch for $name\" here\nin a way similar to \"the upstream\" I gave an example above, so that\nanybody reading this log message can answer questions like...\n\nWhen the end users say master@{publish}, what conceptually are they\nnaming?  Can they use master@{publish} in a way similar to:\n\n $ git log master@{upstream}..master\n\nthat gives us what we did since we forked, because master@{upstream}\nnames the remote-tracking branch we locally have for the branch our\n'master' integrates with?\n\nThanks.\n"}]}