{"thread":{"id":"35644","subject":"[PATCH 0/3] Minor preparation for @{publish}","startedAt":"2014-01-12T17:11:03Z","lastAt":"2014-01-13T20:27:30Z","messageCount":9,"participants":["Ramkumar Ramachandra","Jeff King","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"233041","messageId":"1389546666-17438-1-git-send-email-artagnon@gmail.com","threadId":"35644","inReplyTo":null,"subject":"[PATCH 0/3] Minor preparation for @{publish}","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-01-12T17:11:03Z","receivedAt":"2014-01-12T17:11:03Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi,\n\nI'm getting ready to switch timezones in a few days, so the @{publish}\nseries is on hold for some time. In the meantime, I thought I'd send\nin a few patches early.\n\n[1/3] is a minor typo fix I happened to notice while writing tests.\n\n[2/3] is an is an excellent patch by Peff, that greatly helped\nprettify the code. It's a pure refactor and should have no effect on\nfunctionality.\n\n[3/3] introduces branch->pushremote corresponding to branch->remote,\nwhich is crucial for getting @{publish} from the branch_get()\ninterface. Peff had sent a similar patch, but mine has some subtle\ndifferences.\n\nThanks.\n\nJeff King (1):\n  interpret_branch_name: factor out upstream handling\n\nRamkumar Ramachandra (2):\n  t1507 (rev-parse-upstream): fix typo in test title\n  remote: introduce and fill branch->pushremote\n\n remote.c                      | 15 ++++++--\n remote.h                      |  3 ++\n sha1_name.c                   | 83 +++++++++++++++++++++++++++----------------\n t/t1507-rev-parse-upstream.sh |  2 +-\n 4 files changed, 68 insertions(+), 35 deletions(-)\n\n-- \n1.8.5.2.313.g5abf4c0.dirty\n"},{"id":"233042","messageId":"1389546666-17438-2-git-send-email-artagnon@gmail.com","threadId":"35644","inReplyTo":"1389546666-17438-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 1/3] t1507 (rev-parse-upstream): fix typo in test title","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-01-12T17:11:04Z","receivedAt":"2014-01-12T17:11:04Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n t/t1507-rev-parse-upstream.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh\nindex 2a19e79..15f2e7e 100755\n--- a/t/t1507-rev-parse-upstream.sh\n+++ b/t/t1507-rev-parse-upstream.sh\n@@ -54,7 +54,7 @@ test_expect_success 'my-side@{upstream} resolves to correct full name' '\n \ttest refs/remotes/origin/side = \"$(full_name my-side@{u})\"\n '\n \n-test_expect_success 'refs/heads/my-side@{upstream} does not resolve to my-side{upstream}' '\n+test_expect_success 'refs/heads/my-side@{upstream} does not resolve to my-side@{upstream}' '\n \ttest_must_fail full_name refs/heads/my-side@{upstream}\n '\n \n-- \n1.8.5.2.313.g5abf4c0.dirty\n"},{"id":"233043","messageId":"1389546666-17438-3-git-send-email-artagnon@gmail.com","threadId":"35644","inReplyTo":"1389546666-17438-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 2/3] interpret_branch_name: factor out upstream handling","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-01-12T17:11:05Z","receivedAt":"2014-01-12T17:11:05Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"From: Jeff King <peff@peff.net>\n\nThis function checks a few different @{}-constructs. The\nearly part checks for and dispatches us to helpers for each\nconstruct, but the code for handling @{upstream} is inline.\n\nLet's factor this out into its own function. This makes\ninterpret_branch_name more readable, and will make it much\nsimpler to add more constructs in future patches.\n\nWhile we're at it, let's also break apart the refactored\ncode into a few helper functions. These will be useful when\nwe implement similar @{upstream}-like constructs.\n\nSigned-off-by: Jeff King <peff@peff.net>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n sha1_name.c | 83 ++++++++++++++++++++++++++++++++++++++-----------------------\n 1 file changed, 52 insertions(+), 31 deletions(-)\n\ndiff --git a/sha1_name.c b/sha1_name.c\nindex b1873d8..7ebb8ee 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -1046,6 +1046,54 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu\n \treturn ret - used + len;\n }\n \n+static void set_shortened_ref(struct strbuf *buf, const char *ref)\n+{\n+\tchar *s = shorten_unambiguous_ref(ref, 0);\n+\tstrbuf_reset(buf);\n+\tstrbuf_addstr(buf, s);\n+\tfree(s);\n+}\n+\n+static const char *get_upstream_branch(const char *branch_buf, int len)\n+{\n+\tchar *branch = xstrndup(branch_buf, len);\n+\tstruct branch *upstream = branch_get(*branch ? branch : 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+\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+\t\t\tdie(_(\"No upstream configured for branch '%s'\"),\n+\t\t\t\tupstream->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}\n+\tfree(branch);\n+\n+\treturn upstream->merge[0]->dst;\n+}\n+\n+static int interpret_upstream_mark(const char *name, int namelen,\n+\t\t\t\t   int at, struct strbuf *buf)\n+{\n+\tint len;\n+\n+\tlen = upstream_mark(name + at, namelen - at);\n+\tif (!len)\n+\t\treturn -1;\n+\n+\tset_shortened_ref(buf, get_upstream_branch(name, at));\n+\treturn len + at;\n+}\n+\n /*\n  * This reads short-hand syntax that not only evaluates to a commit\n  * object name, but also can act as if the end user spelled the name\n@@ -1070,9 +1118,7 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu\n int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)\n {\n \tchar *cp;\n-\tstruct branch *upstream;\n \tint len = interpret_nth_prior_checkout(name, buf);\n-\tint tmp_len;\n \n \tif (!namelen)\n \t\tnamelen = strlen(name);\n@@ -1094,36 +1140,11 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)\n \tif (len > 0)\n \t\treturn reinterpret(name, namelen, len, buf);\n \n-\ttmp_len = upstream_mark(cp, namelen - (cp - name));\n-\tif (!tmp_len)\n-\t\treturn -1;\n+\tlen = interpret_upstream_mark(name, namelen, cp - name, buf);\n+\tif (len > 0)\n+\t\treturn len;\n \n-\tlen = cp + tmp_len - name;\n-\tcp = xstrndup(name, cp - name);\n-\tupstream = branch_get(*cp ? cp : NULL);\n-\t/*\n-\t * Upstream can be NULL only if cp refers to HEAD and HEAD\n-\t * points to something different than a branch.\n-\t */\n-\tif (!upstream)\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'\"), cp);\n-\t\tif (!upstream->merge) {\n-\t\t\tdie(_(\"No upstream configured for branch '%s'\"),\n-\t\t\t\tupstream->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}\n-\tfree(cp);\n-\tcp = shorten_unambiguous_ref(upstream->merge[0]->dst, 0);\n-\tstrbuf_reset(buf);\n-\tstrbuf_addstr(buf, cp);\n-\tfree(cp);\n-\treturn len;\n+\treturn -1;\n }\n \n int strbuf_branchname(struct strbuf *sb, const char *name)\n-- \n1.8.5.2.313.g5abf4c0.dirty\n"},{"id":"233044","messageId":"1389546666-17438-4-git-send-email-artagnon@gmail.com","threadId":"35644","inReplyTo":"1389546666-17438-1-git-send-email-artagnon@gmail.com","subject":"[PATCH 3/3] remote: introduce and fill branch->pushremote","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-01-12T17:11:06Z","receivedAt":"2014-01-12T17:11:06Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"When a caller uses branch_get() to retrieve a \"struct branch\", they get\nthe per-branch remote name and a pointer to the remote struct. However,\nthey have no way of knowing about the per-branch pushremote from this\ninterface. So, let's expose that information via fields similar to\n\"remote\" and \"remote_name\"; \"pushremote\" and \"pushremote_name\".\n\nHelped-by: Jeff King <peff@peff.net>\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n remote.c | 15 ++++++++++++---\n remote.h |  3 +++\n 2 files changed, 15 insertions(+), 3 deletions(-)\n\ndiff --git a/remote.c b/remote.c\nindex a89efab..286cdce 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -351,9 +351,10 @@ 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 = 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@@ -1543,7 +1544,9 @@ struct branch *branch_get(const char *name)\n \t\tret = current_branch;\n \telse\n \t\tret = make_branch(name, 0);\n-\tif (ret && ret->remote_name) {\n+\tif (!ret)\n+\t\treturn ret;\n+\tif (ret->remote_name) {\n \t\tret->remote = remote_get(ret->remote_name);\n \t\tif (ret->merge_nr) {\n \t\t\tint i;\n@@ -1557,6 +1560,12 @@ struct branch *branch_get(const char *name)\n \t\t\t}\n \t\t}\n \t}\n+\tif (ret->pushremote_name)\n+\t\tret->pushremote = remote_get(ret->pushremote_name);\n+\telse if (pushremote_name)\n+\t\tret->pushremote = remote_get(pushremote_name);\n+\telse\n+\t\tret->pushremote = ret->remote;\n \treturn ret;\n }\n \ndiff --git a/remote.h b/remote.h\nindex 00c6a76..ac5aadc 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -201,6 +201,9 @@ struct branch {\n \tconst char *remote_name;\n \tstruct remote *remote;\n \n+\tconst char *pushremote_name;\n+\tstruct remote *pushremote;\n+\n \tconst char **merge_name;\n \tstruct refspec **merge;\n \tint merge_nr;\n-- \n1.8.5.2.313.g5abf4c0.dirty\n"},{"id":"233049","messageId":"20140113083421.GA18531@sigill.intra.peff.net","threadId":"35644","inReplyTo":"1389546666-17438-4-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH 3/3] remote: introduce and fill branch->pushremote","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-01-13T08:34:21Z","receivedAt":"2014-01-13T08:34:21Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Jan 12, 2014 at 10:41:06PM +0530, Ramkumar Ramachandra wrote:\n\n> When a caller uses branch_get() to retrieve a \"struct branch\", they get\n> the per-branch remote name and a pointer to the remote struct. However,\n> they have no way of knowing about the per-branch pushremote from this\n> interface. So, let's expose that information via fields similar to\n> \"remote\" and \"remote_name\"; \"pushremote\" and \"pushremote_name\".\n\nMakes sense. This is similar to what I posted before, but stops short of\nsetting branch->pushremote based on \"default.pushremote\". I think that's\na good thing. Your patch matches branch->remote better, and the logic\nfor doing that fallback should probably stay outside of the \"struct\nbranch\" construct.\n\nAll 3 patches look like sane building blocks to me.\n\nOne comment on this hunk, though:\n\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 = branch->pushremote_name;\n\nIn this code (both before and after your patch), pushremote_name does\ndouble-duty for storing both \"remote.pushdefault\", and the current\nbranch's \"branch.*.pushremote\". I introduced an extra variable in my\nversion of the patch to store \"remote.pushdefault\" directly, and turned\npushremote_name into an alias (either to the current branch config, or\nto the global config).\n\nI did that for two reasons, one minor and one that I think will come up\nfurther in the topic:\n\n  1. After your patch \"pushremote_name\" sometimes owns its memory (if\n     allocated for remote.pushdefault), and sometimes not (if an alias to\n     branch.*.pushremote). This isn't a problem in the current code,\n     because we never actually free() the string, meaning that if you\n     set push.default twice, we leak. But that probably does not matter\n     too much, and we have many such minor leaks of global config.\n\n  2. If the current branch has a branch.*.pushremote set, but we want to\n     know where a _different_ branch would be pushed, we have no way to\n     access remote.pushdefault (it gets overwritten in the hunk above).\n\n     @{upstream} does not have this problem, because it is _only_\n     defined if branch.*.remote is set. There is no such thing as\n     defaulting to a \"remote.default\" (or \"origin\") there, and we never\n     need to look at default_remote_name.\n\n     For @{publish}, though, I think we will want that default. The\n     common config will be to simply set \"remote.pushdefault\", rather\n     than setting up \"branch.*.pushremote\" for each branch, and we would\n     want @{publish} to handle that properly.\n\nSo I think your patch is OK as-is, as the problem in (2) does not show\nup until later in the series. But I suspect you will need to do\nsomething to address it (and I think it is fine as a patch that comes\nlater to do that refactoring).\n\n-Peff\n"},{"id":"233050","messageId":"CALkWK0ncSLza3Q0PSZ0oTZqB2YxjgGSqA7QYxk2+rN_77BKZMA@mail.gmail.com","threadId":"35644","inReplyTo":"20140113083421.GA18531@sigill.intra.peff.net","subject":"Re: [PATCH 3/3] remote: introduce and fill branch->pushremote","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2014-01-13T11:22:52Z","receivedAt":"2014-01-13T11:22:52Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Jeff King wrote:\n>   2. If the current branch has a branch.*.pushremote set, but we want to\n>      know where a _different_ branch would be pushed, we have no way to\n>      access remote.pushdefault (it gets overwritten in the hunk above).\n>\n>      @{upstream} does not have this problem, because it is _only_\n>      defined if branch.*.remote is set. There is no such thing as\n>      defaulting to a \"remote.default\" (or \"origin\") there, and we never\n>      need to look at default_remote_name.\n>\n>      For @{publish}, though, I think we will want that default. The\n>      common config will be to simply set \"remote.pushdefault\", rather\n>      than setting up \"branch.*.pushremote\" for each branch, and we would\n>      want @{publish} to handle that properly.\n\nNot sure I understand what the problem is. Let's say we have two\nbranches: \"master\", and \"side\" with remote.pushdefault = ram,\nbranch.*.remote = origin, and branch.side.pushremote = peff. Now, when\nI query master's pushremote, I get \"ram\" and when I query side's\npushremote, I get \"peff\"; all the logic for falling-back from\nbranch.*.pushremote to remote.pushdefault to branch.*.remote is in\nbranch_get(), so I need to do nothing extra on the caller-side. From\nthe caller's perspective, why does it matter if the pushremote of a\nparticular branch is due to branch.*.pushremote or remote.pushdefault?\n"},{"id":"233058","messageId":"20140113185946.GA30279@sigill.intra.peff.net","threadId":"35644","inReplyTo":"CALkWK0ncSLza3Q0PSZ0oTZqB2YxjgGSqA7QYxk2+rN_77BKZMA@mail.gmail.com","subject":"Re: [PATCH 3/3] remote: introduce and fill branch->pushremote","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-01-13T18:59:47Z","receivedAt":"2014-01-13T18:59:47Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jan 13, 2014 at 04:52:52PM +0530, Ramkumar Ramachandra wrote:\n\n> Not sure I understand what the problem is. Let's say we have two\n> branches: \"master\", and \"side\" with remote.pushdefault = ram,\n> branch.*.remote = origin, and branch.side.pushremote = peff. Now, when\n> I query master's pushremote, I get \"ram\" and when I query side's\n> pushremote, I get \"peff\"; all the logic for falling-back from\n> branch.*.pushremote to remote.pushdefault to branch.*.remote is in\n> branch_get(), so I need to do nothing extra on the caller-side. From\n> the caller's perspective, why does it matter if the pushremote of a\n> particular branch is due to branch.*.pushremote or remote.pushdefault?\n\nImagine your HEAD is at \"side\". What should \"master@{publish}\" produce?\nI would argue \"ram/master\". Where does \"ram\" come from in your code?\n\nIt does not matter for actually pushing, because to do a non-default\npush, you must always specify a remote. But \"@{publish}\" will ask the\nquestion \"even if I am on 'side' now, what would happen if I were to\ndefault-push on 'master'?\".\n\n-Peff\n"},{"id":"233062","messageId":"xmqqtxd763lf.fsf@gitster.dls.corp.google.com","threadId":"35644","inReplyTo":"20140113185946.GA30279@sigill.intra.peff.net","subject":"Re: [PATCH 3/3] remote: introduce and fill branch->pushremote","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2014-01-13T20:15:08Z","receivedAt":"2014-01-13T20:15:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> It does not matter for actually pushing, because to do a non-default\n> push, you must always specify a remote. But \"@{publish}\" will ask the\n> question \"even if I am on 'side' now, what would happen if I were to\n> default-push on 'master'?\".\n\nIn a similar wording to yours, it can be said that B@{upstream} is\n\"what would happen if I were to default-pull on 'B'?\".\n\nA related tangent is what should B@{publish} should yield when there\nis no triangular configuration variables like remote.pushdefault,\nbranch.B.pushremote and a possible future extension branch.B.push\nare defined.  The definition you gave, i.e. \"if I were to\ndefault-push\", gives a good guideline, I think.\n\nI.e. \"git push origin master\" does tell us to push out 'master', but\nit does not explicitly say what ref to update.  It may be set to\nupdate their remotes/satellite/master when we are emulating a fetch\nin reverse by pushing, via e.g.\n\n\t[remote \"origin\"]\n        \tpush = refs/heads/master:refs/remotes/satellite/master\n\nand it would be intuitive if we make \"master@{publish}\" resolve to\n\"refs/remotes/satellite/master\" in such a case.\n\nOne thing that makes things a bit fuzzy is what should happen if\nyou have more than one push destinations.  For example:\n\n\t[remote \"home\"]\n        \tpushurl = ...\n                push = refs/heads/master:refs/remotes/satellite/master\n\n\t[remote \"github\"]\n        \tpushurl = ...\n                mirror\n\n\t[remote]\n        \tpushdefault = ???\n\n\"git push home\" updates their 'refs/remotes/satellite/master' with\nmy 'master' with the above, while \"git push github\" will update\ntheir 'refs/heads/master' with 'master'.\n\nWe can say master@{publish} is 'remotes/satellite/master' if\nremote.pushdefault (or 'branch.master.pushremote\") is set to 'home',\nit is 'master' if it is 'github', and if \"git push\" while sitting on\n'master' does not push it anywhere then master@{publish} is an\nerror.  There may be a better definition of what \"if I were to\ndefault-push\" really means, but I don't think of any offhand.\n"},{"id":"233063","messageId":"20140113202730.GA32542@sigill.intra.peff.net","threadId":"35644","inReplyTo":"xmqqtxd763lf.fsf@gitster.dls.corp.google.com","subject":"Re: [PATCH 3/3] remote: introduce and fill branch->pushremote","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2014-01-13T20:27:30Z","receivedAt":"2014-01-13T20:27:30Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jan 13, 2014 at 12:15:08PM -0800, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > It does not matter for actually pushing, because to do a non-default\n> > push, you must always specify a remote. But \"@{publish}\" will ask the\n> > question \"even if I am on 'side' now, what would happen if I were to\n> > default-push on 'master'?\".\n> \n> In a similar wording to yours, it can be said that B@{upstream} is\n> \"what would happen if I were to default-pull on 'B'?\".\n\nRight. I wondered at first if there was a similar bug in @{upstream},\nbut as I noted earlier, it is not defined if a per-branch remote is not\nset. The answer to your question above is \"nothing\", so we do not have\nto worry about it. :)\n\n> A related tangent is what should B@{publish} should yield when there\n> is no triangular configuration variables like remote.pushdefault,\n> branch.B.pushremote and a possible future extension branch.B.push\n> are defined.  The definition you gave, i.e. \"if I were to\n> default-push\", gives a good guideline, I think.\n\nYes, that is what I tried for with my original patches. (e.g.,\n\"push.default=upstream\" should just make @{publish} a synonym for\n@{upstream}, which is what my patch did). I punted on \"simple\", but it\nwould ideally do the same thing as \"push\". Which is why I do not think\nmy patches are appropriate as-is; they need to somehow share the logic\nwith \"git push\" rather than try to reimplement it.\n\n> I.e. \"git push origin master\" does tell us to push out 'master', but\n> it does not explicitly say what ref to update.  It may be set to\n> update their remotes/satellite/master when we are emulating a fetch\n> in reverse by pushing, via e.g.\n> \n> \t[remote \"origin\"]\n>         \tpush = refs/heads/master:refs/remotes/satellite/master\n> \n> and it would be intuitive if we make \"master@{publish}\" resolve to\n> \"refs/remotes/satellite/master\" in such a case.\n\nRight. And my patches did that (or at least I intended them to :) ) by\napplying the push refspec (if any), and then applying the fetch refspec\non top of that. But again, that seems like policy that should be shared\nwith \"git push\".\n\nThat being said, I do not think your example is the best one for\n@{publish}. You have not specified any remote at all. I think the\nclosest \"push\" behavior for @{publish} would be something like:\n\n  git checkout master && git push\n\nI.e., where would _that_ push go?\n\n> One thing that makes things a bit fuzzy is what should happen if\n> you have more than one push destinations.  For example:\n> \n> \t[remote \"home\"]\n>         \tpushurl = ...\n>                 push = refs/heads/master:refs/remotes/satellite/master\n> \n> \t[remote \"github\"]\n>         \tpushurl = ...\n>                 mirror\n> \n> \t[remote]\n>         \tpushdefault = ???\n> \n> \"git push home\" updates their 'refs/remotes/satellite/master' with\n> my 'master' with the above, while \"git push github\" will update\n> their 'refs/heads/master' with 'master'.\n> \n> We can say master@{publish} is 'remotes/satellite/master' if\n> remote.pushdefault (or 'branch.master.pushremote\") is set to 'home',\n> it is 'master' if it is 'github', and if \"git push\" while sitting on\n> 'master' does not push it anywhere then master@{publish} is an\n> error.  There may be a better definition of what \"if I were to\n> default-push\" really means, but I don't think of any offhand.\n\nExactly.  I do not think the multiple push destinations matter here,\nbecause it is always \"what would I do if I were on the branch\".  At most\none of them can be the default in that case (based on the config as you\nnoted).\n\n-Peff\n"}]}