{"thread":{"id":"34823","subject":"[PATCH 0/6] Introduce publish tracking branch","startedAt":"2013-09-01T08:26:36Z","lastAt":"2013-09-02T07:38:21Z","messageCount":11,"participants":["Felipe Contreras","Eric Sunshine","Matthieu Moy"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"226470","messageId":"1378024002-26190-1-git-send-email-felipe.contreras@gmail.com","threadId":"34823","inReplyTo":null,"subject":"[PATCH 0/6] Introduce publish tracking branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-01T08:26:36Z","receivedAt":"2013-09-01T08:26:36Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"Hi,\n\nAs 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 'git\nrebase' (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\nAfter this, it becomes much easier to track branches in a triangular workflow.\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\nFelipe Contreras (6):\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\n Documentation/git-branch.txt |  11 +++++\n Documentation/git-push.txt   |   9 +++-\n branch.c                     |  43 ++++++++++++++++++\n branch.h                     |   2 +\n builtin/branch.c             | 101 +++++++++++++++++++++++++++++++++++++++----\n builtin/push.c               |  52 ++++++++++++++--------\n remote.c                     |  34 ++++++++++++---\n remote.h                     |   4 ++\n t/t3200-branch.sh            |  76 ++++++++++++++++++++++++++++++++\n t/t5529-push-publish.sh      |  70 ++++++++++++++++++++++++++++++\n transport.c                  |  28 ++++++++----\n transport.h                  |   1 +\n 12 files changed, 388 insertions(+), 43 deletions(-)\n create mode 100755 t/t5529-push-publish.sh\n\n-- \n1.8.4-337-g7358a66-dirty\n"},{"id":"226476","messageId":"1378024002-26190-2-git-send-email-felipe.contreras@gmail.com","threadId":"34823","inReplyTo":"1378024002-26190-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH 1/6] push: trivial reorganization","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-01T08:26:37Z","receivedAt":"2013-09-01T08:26:37Z","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 04f0eaf..5dc06a3 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -113,20 +113,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@@ -156,8 +147,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@@ -191,9 +180,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@@ -208,17 +211,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.8.4-337-g7358a66-dirty\n"},{"id":"226475","messageId":"1378024002-26190-3-git-send-email-felipe.contreras@gmail.com","threadId":"34823","inReplyTo":"1378024002-26190-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH 2/6] Add concept of 'publish' branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-01T08:26:38Z","receivedAt":"2013-09-01T08:26:38Z","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 5dc06a3..f2deddf 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -150,6 +150,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\t\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@@ -210,10 +224,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 efcba93..04c7ed9 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -350,13 +350,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(&pushremote_name, key, value))\n-\t\t\t\t\treturn -1;\n+\t\t\t\tpushremote_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@@ -1492,6 +1496,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@@ -1694,6 +1706,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  * Return true if there is anything to report, otherwise false.\n  */\n@@ -1710,15 +1731,16 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)\n \t * Nothing to report unless we are marked to build on top of\n \t * somebody else.\n \t */\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/*\n \t * If what we used to build on no longer exists, there is\n \t * nothing to report.\n \t */\n-\tbase = branch->merge[0]->dst;\n \tif (read_ref(base, sha1))\n \t\treturn 0;\n \ttheirs = lookup_commit_reference(sha1);\n@@ -1781,7 +1803,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)\n \tif (!stat_tracking_info(branch, &num_ours, &num_theirs))\n \t\treturn 0;\n \n-\tbase = branch->merge[0]->dst;\n+\tbase = get_base(branch);\n \tbase = shorten_unambiguous_ref(base, 0);\n \tif (!num_theirs) {\n \t\tstrbuf_addf(sb,\ndiff --git a/remote.h b/remote.h\nindex cf56724..79e5adf 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -138,6 +138,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.8.4-337-g7358a66-dirty\n"},{"id":"226471","messageId":"1378024002-26190-4-git-send-email-felipe.contreras@gmail.com","threadId":"34823","inReplyTo":"1378024002-26190-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH 3/6] branch: allow configuring the publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-01T08:26:39Z","receivedAt":"2013-09-01T08:26:39Z","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             | 56 ++++++++++++++++++++++++++++++++++++++++----\n 4 files changed, 108 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex b7cb625..1bf9b2a 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@@ -189,6 +191,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 c5c6984..02495e3 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 0836890..48af999 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -781,8 +781,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@@ -797,7 +797,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_BOOLEAN(0, \"unset-upstream\", &unset_upstream, \"Unset the upstream info\"),\n+\t\tOPT_BOOLEAN(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@@ -866,13 +868,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\t\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 + !!list + !!new_upstream + !!unset_upstream > 1)\n+\tif (!!delete + !!rename + !!force_create + !!list + !!new_upstream + !!publish +\n+\t\t\t!!unset_upstream + !! unset_publish > 1)\n \t\tusage_with_options(builtin_branch_usage, options);\n \n \tif (abbrev == -1)\n@@ -978,6 +982,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.8.4-337-g7358a66-dirty\n"},{"id":"226474","messageId":"1378024002-26190-5-git-send-email-felipe.contreras@gmail.com","threadId":"34823","inReplyTo":"1378024002-26190-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH 4/6] t: branch add publish branch tests","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-01T08:26:40Z","receivedAt":"2013-09-01T08:26: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 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 44ec6a4..cd0b8e9 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -870,4 +870,80 @@ test_expect_success '--merged catches invalid object names' '\n \ttest_must_fail git branch --merged 0000000000000000000000000000000000000000\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.8.4-337-g7358a66-dirty\n"},{"id":"226473","messageId":"1378024002-26190-6-git-send-email-felipe.contreras@gmail.com","threadId":"34823","inReplyTo":"1378024002-26190-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH 5/6] push: add --set-publish option","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-01T08:26:41Z","receivedAt":"2013-09-01T08:26:41Z","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 f7dfe48..6f3885d 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   [--no-verify] [<repository> [<refspec>...]]\n \n DESCRIPTION\n@@ -171,6 +172,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 f2deddf..1c184b3 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -473,6 +473,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 e15db98..d796732 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -140,8 +140,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@@ -176,12 +176,18 @@ static void set_upstreams(struct transport *transport, struct ref *refs,\n \t\tif (!remotename || prefixcmp(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\tlocalname + 11, transport->remote->name,\n+\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@@ -1113,6 +1119,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@@ -1181,7 +1189,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 ea70ea7..715e6d5 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -108,6 +108,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.8.4-337-g7358a66-dirty\n"},{"id":"226472","messageId":"1378024002-26190-7-git-send-email-felipe.contreras@gmail.com","threadId":"34823","inReplyTo":"1378024002-26190-1-git-send-email-felipe.contreras@gmail.com","subject":"[PATCH 6/6] branch: display publish branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-01T08:26:42Z","receivedAt":"2013-09-01T08:26:42Z","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 | 45 ++++++++++++++++++++++++++++++++++++++++-----\n 1 file changed, 40 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex 48af999..47644ad 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@@ -424,17 +428,37 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,\n \tstruct branch *branch = branch_get(branch_name);\n \tstruct strbuf fancy = STRBUF_INIT;\n \n+\tif (!branch)\n+\t\treturn;\n+\n \tif (!stat_tracking_info(branch, &ours, &theirs)) {\n-\t\tif (branch && branch->merge && branch->merge[0]->dst &&\n-\t\t    show_upstream_ref) {\n+\t\tif (!show_upstream_ref)\n+\t\t\treturn;\n+\t\tif (branch->merge && branch->merge[0]->dst) {\n \t\t\tref = shorten_unambiguous_ref(branch->merge[0]->dst, 0);\n \t\t\tif (want_color(branch_use_color))\n-\t\t\t\tstrbuf_addf(stat, \"[%s%s%s] \",\n+\t\t\t\tstrbuf_addf(&fancy, \"%s%s%s\",\n \t\t\t\t\t\tbranch_get_color(BRANCH_COLOR_UPSTREAM),\n \t\t\t\t\t\tref, branch_get_color(BRANCH_COLOR_RESET));\n \t\t\telse\n-\t\t\t\tstrbuf_addf(stat, \"[%s] \", ref);\n+\t\t\t\tstrbuf_addstr(&fancy, ref);\n+\t\t}\n+\t\tif (branch->push.dst) {\n+\t\t\tref = shorten_unambiguous_ref(branch->push.dst, 0);\n+\t\t\tif (fancy.len)\n+\t\t\t\tstrbuf_addstr(&fancy, \", \");\n+\t\t\tif (want_color(branch_use_color))\n+\t\t\t\tstrbuf_addf(&fancy, \"%s%s%s\",\n+\t\t\t\t\t\tbranch_get_color(BRANCH_COLOR_PUBLISH),\n+\t\t\t\t\t\tref, branch_get_color(BRANCH_COLOR_RESET));\n+\t\t\telse\n+\t\t\t\tstrbuf_addstr(&fancy, ref);\n \t\t}\n+\t\tif (!fancy.len)\n+\t\t\treturn;\n+\t\tstrbuf_addf(stat, _(\"[%s]\"), fancy.buf);\n+\t\tstrbuf_release(&fancy);\n+\t\tstrbuf_addch(stat, ' ');\n \t\treturn;\n \t}\n \n@@ -446,6 +470,17 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,\n \t\t\t\t\tref, branch_get_color(BRANCH_COLOR_RESET));\n \t\telse\n \t\t\tstrbuf_addstr(&fancy, ref);\n+\t\tif (branch->push.dst) {\n+\t\t\tref = shorten_unambiguous_ref(branch->push.dst, 0);\n+\t\t\tif (fancy.len)\n+\t\t\t\tstrbuf_addstr(&fancy, \", \");\n+\t\t\tif (want_color(branch_use_color))\n+\t\t\t\tstrbuf_addf(&fancy, \"%s%s%s\",\n+\t\t\t\t\t\tbranch_get_color(BRANCH_COLOR_PUBLISH),\n+\t\t\t\t\t\tref, branch_get_color(BRANCH_COLOR_RESET));\n+\t\t\telse\n+\t\t\t\tstrbuf_addstr(&fancy, ref);\n+\t\t}\n \t}\n \n \tif (!ours) {\n-- \n1.8.4-337-g7358a66-dirty\n"},{"id":"226477","messageId":"CAPig+cQUiSC4r01nSqRnrDj230x=tUK4dGUXhWrUitSAVKZ-Wg@mail.gmail.com","threadId":"34823","inReplyTo":"1378024002-26190-3-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH 2/6] Add concept of 'publish' branch","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-09-01T08:41:28Z","receivedAt":"2013-09-01T08:41:28Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sun, Sep 1, 2013 at 4:26 AM, Felipe Contreras\n<felipe.contreras@gmail.com> wrote:\n> The upstream branch is:\n>\n>   branch.$name.remote\n>   branch.$name.merge\n>\n> The publish branch is:\n>\n>   branch.$name.pushremote\n>   branch.$name.push\n>\n> Signed-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>\n> diff --git a/builtin/push.c b/builtin/push.c\n> index 5dc06a3..f2deddf 100644\n> --- a/builtin/push.c\n> +++ b/builtin/push.c\n> @@ -150,6 +150,20 @@ static void setup_push_current(struct remote *remote, struct branch *branch)\n>         add_refspec(branch->name);\n>  }\n>\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\nstrbuf_release(&refspec);\n\n> +       } else if (triangular) {\n> +               setup_push_current(remote, branch);\n> +       } else {\n> +               setup_push_upstream(remote, branch, triangular);\n> +       }\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> @@ -210,10 +224,7 @@ static void setup_default_push_refspecs(struct remote *remote)\n>                 break;\n>\n>         case PUSH_DEFAULT_SIMPLE:\n> -               if (triangular)\n> -                       setup_push_current(remote, get_current_branch(remote));\n> -               else\n> -                       setup_push_upstream(remote, get_current_branch(remote), triangular);\n> +               setup_push_simple(remote, get_current_branch(remote), triangular);\n>                 break;\n>\n>         case PUSH_DEFAULT_UPSTREAM:\n> diff --git a/remote.c b/remote.c\n> index efcba93..04c7ed9 100644\n> --- a/remote.c\n> +++ b/remote.c\n> @@ -350,13 +350,17 @@ static int handle_config(const char *key, const char *value, void *cb)\n>                                 explicit_default_remote_name = 1;\n>                         }\n>                 } else if (!strcmp(subkey, \".pushremote\")) {\n> +                       if (git_config_string(&branch->pushremote_name, key, value))\n> +                               return -1;\n>                         if (branch == current_branch)\n> -                               if (git_config_string(&pushremote_name, key, value))\n> -                                       return -1;\n> +                               pushremote_name = xstrdup(branch->pushremote_name);\n>                 } else if (!strcmp(subkey, \".merge\")) {\n>                         if (!value)\n>                                 return config_error_nonbool(key);\n>                         add_merge(branch, xstrdup(value));\n> +               } else if (!strcmp(subkey, \".push\")) {\n> +                       if (git_config_string(&branch->push_name, key, value))\n> +                               return -1;\n>                 }\n>                 return 0;\n>         }\n> @@ -1492,6 +1496,14 @@ struct branch *branch_get(const char *name)\n>                         }\n>                 }\n>         }\n> +       if (ret && ret->pushremote_name) {\n> +               struct remote *pushremote;\n> +               pushremote = pushremote_get(ret->pushremote_name);\n> +               ret->push.src = xstrdup(ret->push_name);\n> +               if (remote_find_tracking(pushremote, &ret->push)\n> +                   && !strcmp(ret->pushremote_name, \".\"))\n> +                       ret->push.dst = xstrdup(ret->push_name);\n> +       }\n>         return ret;\n>  }\n>\n> @@ -1694,6 +1706,15 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)\n>         return found;\n>  }\n>\n> +static char *get_base(struct branch *branch)\n> +{\n> +       if (branch->push.dst)\n> +               return branch->push.dst;\n> +       if (branch->merge && branch->merge[0] && branch->merge[0]->dst)\n> +               return branch->merge[0]->dst;\n> +       return NULL;\n> +}\n> +\n>  /*\n>   * Return true if there is anything to report, otherwise false.\n>   */\n> @@ -1710,15 +1731,16 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs)\n>          * Nothing to report unless we are marked to build on top of\n>          * somebody else.\n>          */\n> -       if (!branch ||\n> -           !branch->merge || !branch->merge[0] || !branch->merge[0]->dst)\n> +       if (!branch)\n> +               return 0;\n> +       base = get_base(branch);\n> +       if (!base)\n>                 return 0;\n>\n>         /*\n>          * If what we used to build on no longer exists, there is\n>          * nothing to report.\n>          */\n> -       base = branch->merge[0]->dst;\n>         if (read_ref(base, sha1))\n>                 return 0;\n>         theirs = lookup_commit_reference(sha1);\n> @@ -1781,7 +1803,7 @@ int format_tracking_info(struct branch *branch, struct strbuf *sb)\n>         if (!stat_tracking_info(branch, &num_ours, &num_theirs))\n>                 return 0;\n>\n> -       base = branch->merge[0]->dst;\n> +       base = get_base(branch);\n>         base = shorten_unambiguous_ref(base, 0);\n>         if (!num_theirs) {\n>                 strbuf_addf(sb,\n> diff --git a/remote.h b/remote.h\n> index cf56724..79e5adf 100644\n> --- a/remote.h\n> +++ b/remote.h\n> @@ -138,6 +138,10 @@ struct branch {\n>         struct refspec **merge;\n>         int merge_nr;\n>         int merge_alloc;\n> +\n> +       const char *pushremote_name;\n> +       const char *push_name;\n> +       struct refspec push;\n>  };\n>\n>  struct branch *branch_get(const char *name);\n> --\n> 1.8.4-337-g7358a66-dirty\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n"},{"id":"226482","messageId":"CAMP44s1x=7k3XTPSzHFk4gtJBY301mVbsQWammsp3HpdSzp2CA@mail.gmail.com","threadId":"34823","inReplyTo":"CAPig+cQUiSC4r01nSqRnrDj230x=tUK4dGUXhWrUitSAVKZ-Wg@mail.gmail.com","subject":"Re: [PATCH 2/6] Add concept of 'publish' branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-01T11:20:18Z","receivedAt":"2013-09-01T11:20:18Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Sun, Sep 1, 2013 at 3:41 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> On Sun, Sep 1, 2013 at 4:26 AM, Felipe Contreras\n> <felipe.contreras@gmail.com> wrote:\n\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>\n> strbuf_release(&refspec);\n\nActually no, wee need that buffer. See setup_push_upstream().\n\n-- \nFelipe Contreras\n"},{"id":"226522","messageId":"vpq1u57n0rw.fsf@anie.imag.fr","threadId":"34823","inReplyTo":"1378024002-26190-1-git-send-email-felipe.contreras@gmail.com","subject":"Re: [PATCH 0/6] Introduce publish tracking branch","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2013-09-02T07:25:07Z","receivedAt":"2013-09-02T07:25:07Z","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> Hi,\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 may be stating the obvious, but isn't your series a duplicate of\nremote.pushDefault, introduced in 1.8.3?\n\nIf not, how does it complement it?\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"226524","messageId":"CAMP44s3cv9NmOXvdaF+iQgN03Q51rQ-WUJUrf+22U1AFFZKwbQ@mail.gmail.com","threadId":"34823","inReplyTo":"vpq1u57n0rw.fsf@anie.imag.fr","subject":"Re: [PATCH 0/6] Introduce publish tracking branch","fromName":"Felipe Contreras","fromEmail":"felipe.contreras@gmail.com","sentAt":"2013-09-02T07:38:21Z","receivedAt":"2013-09-02T07:38:21Z","isPatch":true,"sender":{"key":"felipe.contreras@gmail.com","avatar":"https://avatars.githubusercontent.com/u/8358?v=4"},"body":"On Mon, Sep 2, 2013 at 2:25 AM, Matthieu Moy\n<Matthieu.Moy@grenoble-inp.fr> 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 may be stating the obvious, but isn't your series a duplicate of\n> remote.pushDefault, introduced in 1.8.3?\n\nHow can you currently do this in v1.8.3?\n\n% git push --set-publish github master\n% git push --set-publish backup test feature-a\n\n% git branch -vv\n  master     xxxxx [origin/master, github/master] foo\n  test       xxxxx [master, backup/test: ahead 2] foo\n  feature-a  xxxxx [master, backup/feature-a] foo\n\n% git checkout master\n% git rebase # onto origin/master\n% git push # to github/master\n\n% git checkout feature-a\n% git rebase # onto master\n% git push --force # to backup/feature-a\n\n% git checkout test\n% git branch --set-publish github/my-test\n% git push # to github/my-test\n\nremote.pushDefault doesn't let you do any of that stuff. Maybe if you\nonly push to one remote, and all the branches have the same names, and\nyou don't need to keep track of them (with git branch -vv). In other\nwords, there's just no comparison.\n\n-- \nFelipe Contreras\n"}]}