{"thread":{"id":"48399","subject":"[PATCH] checkout & worktree: introduce a core.DWIMRemote setting","startedAt":"2018-05-02T10:55:12Z","lastAt":"2018-08-22T21:55:05Z","messageCount":95,"participants":["Ævar Arnfjörð Bjarmason","Duy Nguyen","Eric Sunshine","Junio C Hamano","Thomas Gummerer","Stefan Beller","SZEDER Gábor","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"346445","messageId":"20180502105452.17583-1-avarab@gmail.com","threadId":"48399","inReplyTo":null,"subject":"[PATCH] checkout & worktree: introduce a core.DWIMRemote setting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-02T10:54:52Z","receivedAt":"2018-05-02T10:55:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Introduce a core.DWIMRemote setting which can be used to designate a\nremote to prefer (via core.DWIMRemote=origin) when running e.g. \"git\ncheckout master\" to mean origin/master, even though there's other\nremotes that have the \"master\" branch.\n\nI want this because it's very handy to use this workflow to checkout a\nrepository and create a topic branch, then get back to a \"master\" as\nretrieved from upstream:\n\n    (\n        rm -rf /tmp/tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git checkout master\n    )\n\nThat will output:\n\n    Branch 'master' set up to track remote branch 'master' from 'origin'.\n    Switched to a new branch 'master'\n\nBut as soon as a new remote is added (e.g. just to inspect something\nfrom someone else) the DWIMery goes away:\n\n    (\n        rm -rf /tmp/tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git remote add avar git@github.com:avar/tbdiff.git &&\n        git fetch avar &&\n        git checkout master\n    )\n\nWill output:\n\n    error: pathspec 'master' did not match any file(s) known to git.\n\nThe new core.DWIMRemote config allows me to say that whenever that\nambiguity comes up I'd like to prefer \"origin\", and it'll still work\nas though the only remote I had was \"origin\".\n\nI considered calling the config setting checkout.DWIMRemote, but then\ndiscovered that this behavior is also used by \"worktree\" for similar\npurposes, so it makes sense to have it under core.*. As noted in the\ndocumentation we may also want to use this in other commands in the\nfuture if they have similar DWIM behavior in the presence of one\nremote.\n\nSee also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\nfrotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\nto begin with, and 4e85333197 (\"worktree: make add <path> <branch>\ndwim\", 2017-11-26) which added it to git-worktree.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt       | 15 +++++++++++++++\n Documentation/git-checkout.txt |  5 +++++\n Documentation/git-worktree.txt |  5 +++++\n builtin/checkout.c             |  3 ++-\n checkout.c                     | 17 +++++++++++++++--\n t/t2024-checkout-dwim.sh       | 10 ++++++++++\n t/t2025-worktree-add.sh        | 18 ++++++++++++++++++\n 7 files changed, 70 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2659153cb3..d4740fcdb9 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -898,6 +898,21 @@ core.notesRef::\n This setting defaults to \"refs/notes/commits\", and it can be overridden by\n the `GIT_NOTES_REF` environment variable.  See linkgit:git-notes[1].\n \n+core.DWIMRemote::\n+\tVarious git commands will look at references on the configured\n+\tremotes and DW[YI]M (Do What (You|I) Mean) if the reference\n+\tonly exists on one remote. This setting allows for setting the\n+\tname of a special remote that should always win when it comes\n+\tto disambiguation. The typical use-case is to set this to\n+\t`origin`.\n++\n+Currently this is used by linkgit:git-checkout[1] when `git checkout\n+<something>' will checkout the '<something>' branch on another remote,\n+and linkgit:git-worktree[1] when 'git worktree add' similarly DWYM\n+when a branch is unique across remotes, or this setting is set to a\n+special remote. This setting might be used for other commands or\n+functionality in the future when appropriate.\n+\n core.sparseCheckout::\n \tEnable \"sparse checkout\" feature. See section \"Sparse checkout\" in\n \tlinkgit:git-read-tree[1] for more information.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ca5fc9c798..8ad7be6c53 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -38,6 +38,11 @@ equivalent to\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes the `core.DWIMRemote`\n+variable can be used to pick the remote you really mean. Set it to\n+e.g. `core.DWIMRemote=origin` to always checkout remote branches from\n+there. See also `core.DWIMRemote` in linkgit:git-config[1].\n++\n You could omit <branch>, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex 2755ca90e3..37db12f816 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -60,6 +60,11 @@ with a matching name, treat as equivalent to:\n $ git worktree add --track -b <branch> <path> <remote>/<branch>\n ------------\n +\n+It's also possible to use the `core.DWIMRemote` setting to designate a\n+special remote this rule should be applied to, even if the branch\n+isn't unique across all remotes. See `core.DWIMRemote` in\n+linkgit:git-config[1].\n++\n If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n then, as a convenience, a new branch based at HEAD is created automatically,\n as if `-b $(basename <path>)` was specified.\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex b49b582071..8622ad4ef1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -903,7 +903,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t *   (b) If <something> is _not_ a commit, either \"--\" is present\n \t *       or <something> is not a path, no -t or -b was given, and\n \t *       and there is a tracking branch whose name is <something>\n-\t *       in one and only one remote, then this is a short-hand to\n+\t *       in one and only one remote (or if the branch exists on the\n+\t *       remote named in core.DWIMRemote), then this is a short-hand to\n \t *       fork local <something> from that remote-tracking branch.\n \t *\n \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\ndiff --git a/checkout.c b/checkout.c\nindex ac42630f74..6e474fbfbb 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -1,12 +1,15 @@\n #include \"cache.h\"\n #include \"remote.h\"\n #include \"checkout.h\"\n+#include \"config.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n \tint unique;\n+\tconst char *dwim_remote;\n+\tchar *dwim_dst_ref;\n };\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n@@ -20,6 +23,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\tfree(query.dst);\n \t\treturn 0;\n \t}\n+\tif (cb->dwim_remote && !strcmp(remote->name, cb->dwim_remote))\n+\t\tcb->dwim_dst_ref = xstrdup(query.dst);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\tcb->unique = 0;\n@@ -31,13 +36,21 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \n const char *unique_tracking_name(const char *name, struct object_id *oid)\n {\n-\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n+\tconst char *dwim_remote = NULL;\n+\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1, NULL, NULL };\n+\tif (!git_config_get_string_const(\"core.dwimremote\", &dwim_remote))\n+\t\tcb_data.dwim_remote = dwim_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tfree(cb_data.src_ref);\n-\tif (cb_data.unique)\n+\tfree((char *)dwim_remote);\n+\tif (cb_data.unique) {\n+\t\tfree(cb_data.dwim_dst_ref);\n \t\treturn cb_data.dst_ref;\n+\t}\n \tfree(cb_data.dst_ref);\n+\tif (cb_data.dwim_dst_ref)\n+\t\treturn cb_data.dwim_dst_ref;\n \treturn NULL;\n }\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 3e5ac81bd2..d8db2a14d7 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -68,6 +68,16 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_branch master\n '\n \n+test_expect_success 'checkout of branch from multiple remotes succeeds with core.DWIMRemote #1' '\n+\tgit checkout -B master &&\n+\ttest_might_fail git branch -D foo &&\n+\n+\tgit -c core.DWIMRemote=repo_a checkout foo &&\n+\ttest_branch foo &&\n+\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n+\ttest_branch_upstream foo repo_a foo\n+'\n+\n test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \tgit checkout -B master &&\n \ttest_might_fail git branch -D bar &&\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex d0d2e4f7ec..6be4cd03da 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -450,6 +450,24 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '\n \t)\n '\n \n+test_expect_success '\"add\" <path> <branch> dwims with core.DWIMRemote' '\n+\ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n+\tsetup_remote_repo repo_upstream repo_dwim &&\n+\tgit init repo_dwim &&\n+\t(\n+\t\tcd repo_dwim &&\n+\t\tgit remote add repo_upstream2 ../repo_upstream &&\n+\t\tgit fetch repo_upstream2 &&\n+\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\tgit -c core.DWIMRemote=repo_upstream worktree add ../foo foo\n+\t) &&\n+\t(\n+\t\tcd foo &&\n+\t\ttest_branch_upstream foo repo_upstream foo &&\n+\t\ttest_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo\n+\t)\n+'\n+\n post_checkout_hook () {\n \tgitdir=${1:-.git}\n \ttest_when_finished \"rm -f $gitdir/hooks/post-checkout\" &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346457","messageId":"CACsJy8Ae9PP8BMbyX5mPJukGpC06jMvvbg0fMFL+fu+EUg1kPw@mail.gmail.com","threadId":"48399","inReplyTo":"20180502105452.17583-1-avarab@gmail.com","subject":"Re: [PATCH] checkout & worktree: introduce a core.DWIMRemote setting","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-02T15:21:16Z","receivedAt":"2018-05-02T15:26:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, May 2, 2018 at 12:54 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Introduce a core.DWIMRemote setting which can be used to designate a\n> remote to prefer (via core.DWIMRemote=origin) when running e.g. \"git\n> checkout master\" to mean origin/master, even though there's other\n> remotes that have the \"master\" branch.\n\nDo we anticipate more dwimy customizations to justify a new dwim group\nin config (i.e. dwim.remote instead of core.dwimRemote)?\n-- \nDuy\n"},{"id":"346472","messageId":"CAPig+cTZyYC-1_TxL2PrfOF6HAktUxxM+g5EXcByS5fCDMdCHg@mail.gmail.com","threadId":"48399","inReplyTo":"CACsJy8Ae9PP8BMbyX5mPJukGpC06jMvvbg0fMFL+fu+EUg1kPw@mail.gmail.com","subject":"Re: [PATCH] checkout & worktree: introduce a core.DWIMRemote setting","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-05-02T18:00:08Z","receivedAt":"2018-05-02T18:00:16Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, May 2, 2018 at 11:21 AM, Duy Nguyen <pclouds@gmail.com> wrote:\n> On Wed, May 2, 2018 at 12:54 PM, Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> Introduce a core.DWIMRemote setting which can be used to designate a\n>> remote to prefer (via core.DWIMRemote=origin) when running e.g. \"git\n>> checkout master\" to mean origin/master, even though there's other\n>> remotes that have the \"master\" branch.\n>\n> Do we anticipate more dwimy customizations to justify a new dwim group\n> in config (i.e. dwim.remote instead of core.dwimRemote)?\n\nA few observations:\n\n1. DWIM has broad meaning; while this certainly falls within DWIM,\nit's also just a _default_, so should this instead be named\n\"defaultRemote\"?\n\n2. Building on #1: How well is the term \"DWIM\" understood by non-power\nusers? A term, such as \"default\" is more well known.\n\n3. git-worktree learned --guess-remote and worktree.guessRemote in [1]\nand [2], which, on the surface, sound confusingly similar to\n\"DWIMRemote\". Even though I was well involved in the discussion and\nrepeatedly reviewed the patch series which introduced those, it still\ntook me an embarrassingly long time, and repeated re-reads of all\ncommit messages involved, to grasp (or re-grasp) how \"guess remote\"\nand \"DWIM remote\" differ. If it was that difficult for me, as a person\ninvolved in the patch series, to figure out \"guess remote\" vs. \"DWIM\nremote\", then I worry that it may be too confusing in general. If the\nnew config option had been named \"defaultRemote\", I don't think I'd\nhave been confused at all.\n\nIt may sound as if I'm advocating the name \"defaultRemote\", but that\nwasn't the intention, and I'm not wedded to it; that name simply fell\nout naturally as I enumerated the above observations.\n\n[1]: 71d6682d8c (worktree: add --guess-remote flag to add subcommand,\n2017-11-29)\n[2]: e92445a731 (add worktree.guessRemote config option, 2017-11-29)\n"},{"id":"346474","messageId":"CACsJy8C9yY-uJf-j=orfPUnwZ2U=-0vVMqWEbS306P0v5_UbPw@mail.gmail.com","threadId":"48399","inReplyTo":"CAPig+cTZyYC-1_TxL2PrfOF6HAktUxxM+g5EXcByS5fCDMdCHg@mail.gmail.com","subject":"Re: [PATCH] checkout & worktree: introduce a core.DWIMRemote setting","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-02T18:09:56Z","receivedAt":"2018-05-02T18:10:34Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, May 2, 2018 at 8:00 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> 2. Building on #1: How well is the term \"DWIM\" understood by non-power\n> users? A term, such as \"default\" is more well known.\n\nI'm going off topic but I kinda dislike this term. First time I\nencountered it in the code I didn't even know what it meant. Since it\nhas not been leaked to user documents (I only did a grep on\nDocumentation/) perhaps a better term should be used, preferably not\nanother acronym.\n--\nDuy\n"},{"id":"346476","messageId":"87y3h1ykwn.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"CAPig+cTZyYC-1_TxL2PrfOF6HAktUxxM+g5EXcByS5fCDMdCHg@mail.gmail.com","subject":"Re: [PATCH] checkout & worktree: introduce a core.DWIMRemote setting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-02T18:25:44Z","receivedAt":"2018-05-02T18:25:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, May 02 2018, Eric Sunshine wrote:\n\n> On Wed, May 2, 2018 at 11:21 AM, Duy Nguyen <pclouds@gmail.com> wrote:\n>> On Wed, May 2, 2018 at 12:54 PM, Ævar Arnfjörð Bjarmason\n>> <avarab@gmail.com> wrote:\n>>> Introduce a core.DWIMRemote setting which can be used to designate a\n>>> remote to prefer (via core.DWIMRemote=origin) when running e.g. \"git\n>>> checkout master\" to mean origin/master, even though there's other\n>>> remotes that have the \"master\" branch.\n>>\n>> Do we anticipate more dwimy customizations to justify a new dwim group\n>> in config (i.e. dwim.remote instead of core.dwimRemote)?\n>\n> A few observations:\n>\n> 1. DWIM has broad meaning; while this certainly falls within DWIM,\n> it's also just a _default_, so should this instead be named\n> \"defaultRemote\"?\n>\n> 2. Building on #1: How well is the term \"DWIM\" understood by non-power\n> users? A term, such as \"default\" is more well known.\n\nI've got no love for the DWIM term. And I think I should change it in\nv2, I just want some way to enable this functionality since this\nbehavior has annoyed me for a long time.\n\nI wonder though if something like \"core.defaultRemote\" might not bring\nup connotations about whether this would e.g. affect \"push\" in the minds\nof users (not that my initial suggestion is better).\n\nSo maybe something like checkout.implicitRemote would be better? And we\ncould just break the rule that only git-checkout would use it, since\ngit-worktree could be said to be doing something checkout-like, or just\nalso add a worktree.implicitRemote.\n\n> 3. git-worktree learned --guess-remote and worktree.guessRemote in [1]\n> and [2], which, on the surface, sound confusingly similar to\n> \"DWIMRemote\". Even though I was well involved in the discussion and\n> repeatedly reviewed the patch series which introduced those, it still\n> took me an embarrassingly long time, and repeated re-reads of all\n> commit messages involved, to grasp (or re-grasp) how \"guess remote\"\n> and \"DWIM remote\" differ. If it was that difficult for me, as a person\n> involved in the patch series, to figure out \"guess remote\" vs. \"DWIM\n> remote\", then I worry that it may be too confusing in general. If the\n> new config option had been named \"defaultRemote\", I don't think I'd\n> have been confused at all.\n\nI hadn't looked at this at all before today when I wrote the patch, and\nI've never used git-worktree (but maybe I should...), but my\nunderstanding of this --[no-]guess-remote functionality is that it\neffectively splits up the functionality that the \"git checkout\" does,\nand we'll unconditionally check out the branch, but the option controls\nwhether or not we'd set up the equivalent of remote tracking for\ngit-worktree.\n\nBut maybe I've completely misunderstood it.\n\n> It may sound as if I'm advocating the name \"defaultRemote\", but that\n> wasn't the intention, and I'm not wedded to it; that name simply fell\n> out naturally as I enumerated the above observations.\n>\n> [1]: 71d6682d8c (worktree: add --guess-remote flag to add subcommand,\n> 2017-11-29)\n> [2]: e92445a731 (add worktree.guessRemote config option, 2017-11-29)\n"},{"id":"346513","messageId":"20180503131840.27956-1-avarab@gmail.com","threadId":"48399","inReplyTo":"87y3h1ykwn.fsf@evledraar.gmail.com","subject":"[PATCH v2] checkout & worktree: introduce checkout.implicitRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-03T13:18:40Z","receivedAt":"2018-05-03T13:18:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Introduce a checkout.implicitRemote setting which can be used to\ndesignate a remote to prefer (via checkout.implicitRemote=origin) when\nrunning e.g. \"git checkout master\" to mean origin/master, even though\nthere's other remotes that have the \"master\" branch.\n\nI want this because it's very handy to use this workflow to checkout a\nrepository and create a topic branch, then get back to a \"master\" as\nretrieved from upstream:\n\n    (\n        rm -rf /tmp/tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git checkout master\n    )\n\nThat will output:\n\n    Branch 'master' set up to track remote branch 'master' from 'origin'.\n    Switched to a new branch 'master'\n\nBut as soon as a new remote is added (e.g. just to inspect something\nfrom someone else) the DWIMery goes away:\n\n    (\n        rm -rf /tmp/tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git remote add avar git@github.com:avar/tbdiff.git &&\n        git fetch avar &&\n        git checkout master\n    )\n\nWill output:\n\n    error: pathspec 'master' did not match any file(s) known to git.\n\nThe new checkout.implicitRemote config allows me to say that whenever\nthat ambiguity comes up I'd like to prefer \"origin\", and it'll still\nwork as though the only remote I had was \"origin\".\n\nI considered splitting this into checkout.implicitRemote and\nworktree.implicitRemote, but it's probably less confusing to break our\nown rules that anything shared between config should live in core.*\nthan have two config settings, and I couldn't come up with a short\nname under core.* that made sense (core.implicitRemoteForCheckout?).\n\nSee also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\nfrotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\nto begin with, and 4e85333197 (\"worktree: make add <path> <branch>\ndwim\", 2017-11-26) which added it to git-worktree.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nIn v2 I've changed the config name to checkout.implicitRemote as\nsuggested above, there's no worktree.implicitRemote, instead the\nconfig just explains that it's used for checkout-like things (and may\nbe expanded in the future).\n\nA branch-diff (new command from JS) follows:\n    \n    1:  18f13ecd89 ! 1:  905a17f35f checkout & worktree: introduce a core.DWIMRemote setting\n        @@ -1,11 +1,11 @@\n         Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         \n        -    checkout & worktree: introduce a core.DWIMRemote setting\n        +    checkout & worktree: introduce checkout.implicitRemote\n         \n        -    Introduce a core.DWIMRemote setting which can be used to designate a\n        -    remote to prefer (via core.DWIMRemote=origin) when running e.g. \"git\n        -    checkout master\" to mean origin/master, even though there's other\n        -    remotes that have the \"master\" branch.\n        +    Introduce a checkout.implicitRemote setting which can be used to\n        +    designate a remote to prefer (via checkout.implicitRemote=origin) when\n        +    running e.g. \"git checkout master\" to mean origin/master, even though\n        +    there's other remotes that have the \"master\" branch.\n         \n             I want this because it's very handy to use this workflow to checkout a\n             repository and create a topic branch, then get back to a \"master\" as\n        @@ -41,16 +41,15 @@\n         \n                 error: pathspec 'master' did not match any file(s) known to git.\n         \n        -    The new core.DWIMRemote config allows me to say that whenever that\n        -    ambiguity comes up I'd like to prefer \"origin\", and it'll still work\n        -    as though the only remote I had was \"origin\".\n        +    The new checkout.implicitRemote config allows me to say that whenever\n        +    that ambiguity comes up I'd like to prefer \"origin\", and it'll still\n        +    work as though the only remote I had was \"origin\".\n         \n        -    I considered calling the config setting checkout.DWIMRemote, but then\n        -    discovered that this behavior is also used by \"worktree\" for similar\n        -    purposes, so it makes sense to have it under core.*. As noted in the\n        -    documentation we may also want to use this in other commands in the\n        -    future if they have similar DWIM behavior in the presence of one\n        -    remote.\n        +    I considered splitting this into checkout.implicitRemote and\n        +    worktree.implicitRemote, but it's probably less confusing to break our\n        +    own rules that anything shared between config should live in core.*\n        +    than have two config settings, and I couldn't come up with a short\n        +    name under core.* that made sense (core.implicitRemoteForCheckout?).\n         \n             See also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\n             frotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\n        @@ -63,27 +62,29 @@\n         --- a/Documentation/config.txt\n         +++ b/Documentation/config.txt\n         @@\n        - This setting defaults to \"refs/notes/commits\", and it can be overridden by\n        - the `GIT_NOTES_REF` environment variable.  See linkgit:git-notes[1].\n        + \tbrowse HTML help (see `-w` option in linkgit:git-help[1]) or a\n        + \tworking repository in gitweb (see linkgit:git-instaweb[1]).\n          \n        -+core.DWIMRemote::\n        -+\tVarious git commands will look at references on the configured\n        -+\tremotes and DW[YI]M (Do What (You|I) Mean) if the reference\n        -+\tonly exists on one remote. This setting allows for setting the\n        -+\tname of a special remote that should always win when it comes\n        -+\tto disambiguation. The typical use-case is to set this to\n        ++checkout.implicitRemote::\n        ++\tWhen you run 'git checkout <something>' and only have one\n        ++\tremote, it may implicitly fall back on checking out and\n        ++\ttracking e.g. 'origin/<something>'. This stops working as soon\n        ++\tas you have more than one remote with a '<something>'\n        ++\treference. This setting allows for setting the name of a\n        ++\tspecial remote that should always win when it comes to\n        ++\tdisambiguation. The typical use-case is to set this to\n         +\t`origin`.\n         ++\n        -+Currently this is used by linkgit:git-checkout[1] when `git checkout\n        ++Currently this is used by linkgit:git-checkout[1] when 'git checkout\n         +<something>' will checkout the '<something>' branch on another remote,\n        -+and linkgit:git-worktree[1] when 'git worktree add' similarly DWYM\n        -+when a branch is unique across remotes, or this setting is set to a\n        -+special remote. This setting might be used for other commands or\n        -+functionality in the future when appropriate.\n        ++and by linkgit:git-worktree[1] when 'git worktree add' when referring\n        ++to a remote branch.  This setting might be used for other\n        ++checkout-like commands or functionality in the future when\n        ++appropriate.\n         +\n        - core.sparseCheckout::\n        - \tEnable \"sparse checkout\" feature. See section \"Sparse checkout\" in\n        - \tlinkgit:git-read-tree[1] for more information.\n        + clean.requireForce::\n        + \tA boolean to make git-clean do nothing unless given -f,\n        + \t-i or -n.   Defaults to true.\n         \n         diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\n         --- a/Documentation/git-checkout.txt\n        @@ -92,10 +93,11 @@\n          $ git checkout -b <branch> --track <remote>/<branch>\n          ------------\n          +\n        -+If the branch exists in multiple remotes the `core.DWIMRemote`\n        ++If the branch exists in multiple remotes the `checkout.implicitRemote`\n         +variable can be used to pick the remote you really mean. Set it to\n        -+e.g. `core.DWIMRemote=origin` to always checkout remote branches from\n        -+there. See also `core.DWIMRemote` in linkgit:git-config[1].\n        ++e.g. `checkout.implicitRemote=origin` to always checkout remote\n        ++branches from there. See also `checkout.implicitRemote` in\n        ++linkgit:git-config[1].\n         ++\n          You could omit <branch>, in which case the command degenerates to\n          \"check out the current branch\", which is a glorified no-op with\n        @@ -108,10 +110,10 @@\n          $ git worktree add --track -b <branch> <path> <remote>/<branch>\n          ------------\n          +\n        -+It's also possible to use the `core.DWIMRemote` setting to designate a\n        -+special remote this rule should be applied to, even if the branch\n        -+isn't unique across all remotes. See `core.DWIMRemote` in\n        -+linkgit:git-config[1].\n        ++It's also possible to use the `checkout.implicitRemote` setting to\n        ++designate a special remote this rule should be applied to, even if the\n        ++branch isn't unique across all remotes. See `checkout.implicitRemote`\n        ++in linkgit:git-config[1].\n         ++\n          If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n          then, as a convenience, a new branch based at HEAD is created automatically,\n        @@ -125,11 +127,14 @@\n          \t *       or <something> is not a path, no -t or -b was given, and\n          \t *       and there is a tracking branch whose name is <something>\n         -\t *       in one and only one remote, then this is a short-hand to\n        +-\t *       fork local <something> from that remote-tracking branch.\n         +\t *       in one and only one remote (or if the branch exists on the\n        -+\t *       remote named in core.DWIMRemote), then this is a short-hand to\n        - \t *       fork local <something> from that remote-tracking branch.\n        ++\t *       remote named in checkout.implicitRemote), then this is a\n        ++\t *       short-hand to fork local <something> from that\n        ++\t *       remote-tracking branch.\n          \t *\n          \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\n        + \t *\n         \n         diff --git a/checkout.c b/checkout.c\n         --- a/checkout.c\n        @@ -145,8 +150,8 @@\n          \tchar *dst_ref;\n          \tstruct object_id *dst_oid;\n          \tint unique;\n        -+\tconst char *dwim_remote;\n        -+\tchar *dwim_dst_ref;\n        ++\tconst char *implicit_remote;\n        ++\tchar *implicit_dst_ref;\n          };\n          \n          static int check_tracking_name(struct remote *remote, void *cb_data)\n        @@ -154,8 +159,8 @@\n          \t\tfree(query.dst);\n          \t\treturn 0;\n          \t}\n        -+\tif (cb->dwim_remote && !strcmp(remote->name, cb->dwim_remote))\n        -+\t\tcb->dwim_dst_ref = xstrdup(query.dst);\n        ++\tif (cb->implicit_remote && !strcmp(remote->name, cb->implicit_remote))\n        ++\t\tcb->implicit_dst_ref = xstrdup(query.dst);\n          \tif (cb->dst_ref) {\n          \t\tfree(query.dst);\n          \t\tcb->unique = 0;\n        @@ -164,23 +169,23 @@\n          const char *unique_tracking_name(const char *name, struct object_id *oid)\n          {\n         -\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n        -+\tconst char *dwim_remote = NULL;\n        ++\tconst char *implicit_remote = NULL;\n         +\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1, NULL, NULL };\n        -+\tif (!git_config_get_string_const(\"core.dwimremote\", &dwim_remote))\n        -+\t\tcb_data.dwim_remote = dwim_remote;\n        ++\tif (!git_config_get_string_const(\"checkout.implicitremote\", &implicit_remote))\n        ++\t\tcb_data.implicit_remote = implicit_remote;\n          \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n          \tcb_data.dst_oid = oid;\n          \tfor_each_remote(check_tracking_name, &cb_data);\n          \tfree(cb_data.src_ref);\n         -\tif (cb_data.unique)\n        -+\tfree((char *)dwim_remote);\n        ++\tfree((char *)implicit_remote);\n         +\tif (cb_data.unique) {\n        -+\t\tfree(cb_data.dwim_dst_ref);\n        ++\t\tfree(cb_data.implicit_dst_ref);\n          \t\treturn cb_data.dst_ref;\n         +\t}\n          \tfree(cb_data.dst_ref);\n        -+\tif (cb_data.dwim_dst_ref)\n        -+\t\treturn cb_data.dwim_dst_ref;\n        ++\tif (cb_data.implicit_dst_ref)\n        ++\t\treturn cb_data.implicit_dst_ref;\n          \treturn NULL;\n          }\n         \n        @@ -191,11 +196,11 @@\n          \ttest_branch master\n          '\n          \n        -+test_expect_success 'checkout of branch from multiple remotes succeeds with core.DWIMRemote #1' '\n        ++test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.implicitRemote #1' '\n         +\tgit checkout -B master &&\n         +\ttest_might_fail git branch -D foo &&\n         +\n        -+\tgit -c core.DWIMRemote=repo_a checkout foo &&\n        ++\tgit -c checkout.implicitRemote=repo_a checkout foo &&\n         +\ttest_branch foo &&\n         +\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n         +\ttest_branch_upstream foo repo_a foo\n        @@ -212,7 +217,7 @@\n          \t)\n          '\n          \n        -+test_expect_success '\"add\" <path> <branch> dwims with core.DWIMRemote' '\n        ++test_expect_success '\"add\" <path> <branch> dwims with checkout.implicitRemote' '\n         +\ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n         +\tsetup_remote_repo repo_upstream repo_dwim &&\n         +\tgit init repo_dwim &&\n        @@ -221,7 +226,7 @@\n         +\t\tgit remote add repo_upstream2 ../repo_upstream &&\n         +\t\tgit fetch repo_upstream2 &&\n         +\t\ttest_must_fail git worktree add ../foo foo &&\n        -+\t\tgit -c core.DWIMRemote=repo_upstream worktree add ../foo foo\n        ++\t\tgit -c checkout.implicitRemote=repo_upstream worktree add ../foo foo\n         +\t) &&\n         +\t(\n         +\t\tcd foo &&\n\n Documentation/config.txt       | 17 +++++++++++++++++\n Documentation/git-checkout.txt |  6 ++++++\n Documentation/git-worktree.txt |  5 +++++\n builtin/checkout.c             |  6 ++++--\n checkout.c                     | 17 +++++++++++++++--\n t/t2024-checkout-dwim.sh       | 10 ++++++++++\n t/t2025-worktree-add.sh        | 18 ++++++++++++++++++\n 7 files changed, 75 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 2659153cb3..83460bd443 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1084,6 +1084,23 @@ browser.<tool>.path::\n \tbrowse HTML help (see `-w` option in linkgit:git-help[1]) or a\n \tworking repository in gitweb (see linkgit:git-instaweb[1]).\n \n+checkout.implicitRemote::\n+\tWhen you run 'git checkout <something>' and only have one\n+\tremote, it may implicitly fall back on checking out and\n+\ttracking e.g. 'origin/<something>'. This stops working as soon\n+\tas you have more than one remote with a '<something>'\n+\treference. This setting allows for setting the name of a\n+\tspecial remote that should always win when it comes to\n+\tdisambiguation. The typical use-case is to set this to\n+\t`origin`.\n++\n+Currently this is used by linkgit:git-checkout[1] when 'git checkout\n+<something>' will checkout the '<something>' branch on another remote,\n+and by linkgit:git-worktree[1] when 'git worktree add' when referring\n+to a remote branch.  This setting might be used for other\n+checkout-like commands or functionality in the future when\n+appropriate.\n+\n clean.requireForce::\n \tA boolean to make git-clean do nothing unless given -f,\n \t-i or -n.   Defaults to true.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ca5fc9c798..753aa4001f 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -38,6 +38,12 @@ equivalent to\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes the `checkout.implicitRemote`\n+variable can be used to pick the remote you really mean. Set it to\n+e.g. `checkout.implicitRemote=origin` to always checkout remote\n+branches from there. See also `checkout.implicitRemote` in\n+linkgit:git-config[1].\n++\n You could omit <branch>, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex 2755ca90e3..35798a1132 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -60,6 +60,11 @@ with a matching name, treat as equivalent to:\n $ git worktree add --track -b <branch> <path> <remote>/<branch>\n ------------\n +\n+It's also possible to use the `checkout.implicitRemote` setting to\n+designate a special remote this rule should be applied to, even if the\n+branch isn't unique across all remotes. See `checkout.implicitRemote`\n+in linkgit:git-config[1].\n++\n If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n then, as a convenience, a new branch based at HEAD is created automatically,\n as if `-b $(basename <path>)` was specified.\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex b49b582071..495a5d7955 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -903,8 +903,10 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t *   (b) If <something> is _not_ a commit, either \"--\" is present\n \t *       or <something> is not a path, no -t or -b was given, and\n \t *       and there is a tracking branch whose name is <something>\n-\t *       in one and only one remote, then this is a short-hand to\n-\t *       fork local <something> from that remote-tracking branch.\n+\t *       in one and only one remote (or if the branch exists on the\n+\t *       remote named in checkout.implicitRemote), then this is a\n+\t *       short-hand to fork local <something> from that\n+\t *       remote-tracking branch.\n \t *\n \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\n \t *\ndiff --git a/checkout.c b/checkout.c\nindex ac42630f74..2c898cfe19 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -1,12 +1,15 @@\n #include \"cache.h\"\n #include \"remote.h\"\n #include \"checkout.h\"\n+#include \"config.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n \tint unique;\n+\tconst char *implicit_remote;\n+\tchar *implicit_dst_ref;\n };\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n@@ -20,6 +23,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\tfree(query.dst);\n \t\treturn 0;\n \t}\n+\tif (cb->implicit_remote && !strcmp(remote->name, cb->implicit_remote))\n+\t\tcb->implicit_dst_ref = xstrdup(query.dst);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\tcb->unique = 0;\n@@ -31,13 +36,21 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \n const char *unique_tracking_name(const char *name, struct object_id *oid)\n {\n-\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n+\tconst char *implicit_remote = NULL;\n+\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1, NULL, NULL };\n+\tif (!git_config_get_string_const(\"checkout.implicitremote\", &implicit_remote))\n+\t\tcb_data.implicit_remote = implicit_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tfree(cb_data.src_ref);\n-\tif (cb_data.unique)\n+\tfree((char *)implicit_remote);\n+\tif (cb_data.unique) {\n+\t\tfree(cb_data.implicit_dst_ref);\n \t\treturn cb_data.dst_ref;\n+\t}\n \tfree(cb_data.dst_ref);\n+\tif (cb_data.implicit_dst_ref)\n+\t\treturn cb_data.implicit_dst_ref;\n \treturn NULL;\n }\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 3e5ac81bd2..da6bd74bbc 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -68,6 +68,16 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_branch master\n '\n \n+test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.implicitRemote #1' '\n+\tgit checkout -B master &&\n+\ttest_might_fail git branch -D foo &&\n+\n+\tgit -c checkout.implicitRemote=repo_a checkout foo &&\n+\ttest_branch foo &&\n+\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n+\ttest_branch_upstream foo repo_a foo\n+'\n+\n test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \tgit checkout -B master &&\n \ttest_might_fail git branch -D bar &&\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex d0d2e4f7ec..6eb994ac12 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -450,6 +450,24 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '\n \t)\n '\n \n+test_expect_success '\"add\" <path> <branch> dwims with checkout.implicitRemote' '\n+\ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n+\tsetup_remote_repo repo_upstream repo_dwim &&\n+\tgit init repo_dwim &&\n+\t(\n+\t\tcd repo_dwim &&\n+\t\tgit remote add repo_upstream2 ../repo_upstream &&\n+\t\tgit fetch repo_upstream2 &&\n+\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\tgit -c checkout.implicitRemote=repo_upstream worktree add ../foo foo\n+\t) &&\n+\t(\n+\t\tcd foo &&\n+\t\ttest_branch_upstream foo repo_upstream foo &&\n+\t\ttest_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo\n+\t)\n+'\n+\n post_checkout_hook () {\n \tgitdir=${1:-.git}\n \ttest_when_finished \"rm -f $gitdir/hooks/post-checkout\" &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"346516","messageId":"CACsJy8CYp3zVSRtS5ozHCitnWU81AamqEGR8RZdDkwujShEatw@mail.gmail.com","threadId":"48399","inReplyTo":"20180503131840.27956-1-avarab@gmail.com","subject":"Re: [PATCH v2] checkout & worktree: introduce checkout.implicitRemote","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-03T15:14:14Z","receivedAt":"2018-05-03T15:14:50Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, May 3, 2018 at 3:18 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Introduce a checkout.implicitRemote setting which can be used to\n> designate a remote to prefer (via checkout.implicitRemote=origin) when\n> running e.g. \"git checkout master\" to mean origin/master, even though\n> there's other remotes that have the \"master\" branch.\n>\n> I want this because it's very handy to use this workflow to checkout a\n> repository and create a topic branch, then get back to a \"master\" as\n> retrieved from upstream:\n>\n>     (\n>         rm -rf /tmp/tbdiff &&\n>         git clone git@github.com:trast/tbdiff.git &&\n>         cd tbdiff &&\n>         git branch -m topic &&\n>         git checkout master\n>     )\n>\n> That will output:\n>\n>     Branch 'master' set up to track remote branch 'master' from 'origin'.\n>     Switched to a new branch 'master'\n>\n> But as soon as a new remote is added (e.g. just to inspect something\n> from someone else) the DWIMery goes away:\n>\n>     (\n>         rm -rf /tmp/tbdiff &&\n>         git clone git@github.com:trast/tbdiff.git &&\n>         cd tbdiff &&\n>         git branch -m topic &&\n>         git remote add avar git@github.com:avar/tbdiff.git &&\n>         git fetch avar &&\n>         git checkout master\n>     )\n>\n> Will output:\n>\n>     error: pathspec 'master' did not match any file(s) known to git.\n>\n> The new checkout.implicitRemote config allows me to say that whenever\n> that ambiguity comes up I'd like to prefer \"origin\", and it'll still\n> work as though the only remote I had was \"origin\".\n>\n> I considered splitting this into checkout.implicitRemote and\n> worktree.implicitRemote, but it's probably less confusing to break our\n> own rules that anything shared between config should live in core.*\n> than have two config settings, and I couldn't come up with a short\n> name under core.* that made sense (core.implicitRemoteForCheckout?).\n\nI wonder if it's difficult to add a dwim hook that allows us to\nreplace the entire dwim logic with a hook? Doing this \"preferring\norigin\" in a shell script should be easy and it lets us play more with\ntweaking dwim logic. (Yeah sorry I'm getting off topic again)\n-- \nDuy\n"},{"id":"346630","messageId":"CAPig+cSRBBzXDWMJd5k=ZGOt_ayATWB6fZQrcMqJkP8ja4Tq+g@mail.gmail.com","threadId":"48399","inReplyTo":"87y3h1ykwn.fsf@evledraar.gmail.com","subject":"Re: [PATCH] checkout & worktree: introduce a core.DWIMRemote setting","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-05-04T07:14:50Z","receivedAt":"2018-05-04T07:14:55Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, May 2, 2018 at 2:25 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> On Wed, May 02 2018, Eric Sunshine wrote:\n>> A few observations:\n>>\n>> 1. DWIM has broad meaning; while this certainly falls within DWIM,\n>> it's also just a _default_, so should this instead be named\n>> \"defaultRemote\"?\n>>\n>> 2. Building on #1: How well is the term \"DWIM\" understood by non-power\n>> users? A term, such as \"default\" is more well known.\n>\n> I've got no love for the DWIM term. And I think I should change it in\n> v2, I just want some way to enable this functionality since this\n> behavior has annoyed me for a long time.\n>\n> I wonder though if something like \"core.defaultRemote\" might not bring\n> up connotations about whether this would e.g. affect \"push\" in the minds\n> of users (not that my initial suggestion is better).\n\nA reasonable concern.\n\n> So maybe something like checkout.implicitRemote would be better? And we\n> could just break the rule that only git-checkout would use it, since\n> git-worktree could be said to be doing something checkout-like, or just\n> also add a worktree.implicitRemote.\n\nConsidering that git-worktree runs the post-checkout hook, it seems\npretty safe to say that it does something checkout-like.\n\nPersonally, I find \"defaultRemote\" easier to understand than\n\"implicitRemote\", but I suppose I can see your reasoning for choosing\n\"implicit\". Whereas \"default\" is something to \"fall back upon\" when\nsomething is missing, \"implicit\" suggests what to choose when a\nsomething has not been specified explicitly.\n\n>> 3. git-worktree learned --guess-remote and worktree.guessRemote in [1]\n>> and [2], which, on the surface, sound confusingly similar to\n>> \"DWIMRemote\". Even though I was well involved in the discussion and\n>> repeatedly reviewed the patch series which introduced those, it still\n>> took me an embarrassingly long time, and repeated re-reads of all\n>> commit messages involved, to grasp (or re-grasp) how \"guess remote\"\n>> and \"DWIM remote\" differ. If it was that difficult for me, as a person\n>> involved in the patch series, to figure out \"guess remote\" vs. \"DWIM\n>> remote\", then I worry that it may be too confusing in general. If the\n>> new config option had been named \"defaultRemote\", I don't think I'd\n>> have been confused at all.\n>\n> I hadn't looked at this at all before today when I wrote the patch, and\n> I've never used git-worktree (but maybe I should...), but my\n> understanding of this --[no-]guess-remote functionality is that it\n> effectively splits up the functionality that the \"git checkout\" does,\n> and we'll unconditionally check out the branch, but the option controls\n> whether or not we'd set up the equivalent of remote tracking for\n> git-worktree.\n>\n> But maybe I've completely misunderstood it.\n\nYes, you misunderstood it.\n\nThe setting up of a remote-tracking branch is DWIM'd as of 4e85333197\n(\"worktree: make add <path> <branch> dwim\", 2017-11-26); it doesn't\nrequire an explicit option to enable it (though tracking can be\ndisabled via --no-track). The \"guess-remote\" feature does something\nentirely different; it was added to avoid backward compatibility\nproblems.\n\nIn long-form:\n\n    git worktree add <path> <branch>\n\nadds a new worktree at <path> and checks out <branch>. As originally\nimplemented, shortened:\n\n    git worktree add <path>\n\ndoes one type of DWIM, as a convenience, and pretends that the user\nactually typed:\n\n    branch=$(basename <path>)\n    git branch $branch HEAD\n    git workree add <path> $branch\n\nwhich creates a new branch and then checks it out in the new worktree\nas usual. The \"guess remote\" feature which Thomas added augments that\nby adding a DWIM which checks if $(basename <path>) names a\nremote-tracking branch, in which case, it becomes shorthand for\n(something like):\n\n    branch=$(basename <path>)\n    if remote-tracking branch named $branch exists:\n        git branch --track $branch $guessedRemote/$branch\n    else:\n        git branch $branch HEAD\n    fi\n    git worktree add <path> $branch\n\nIn retrospect, this DWIM-checking for a like-named remote-tracking\nbranch should have been implemented from the start in git-worktree\nsince it mirrors how \"git checkout <branch>\" will DWIM remote-tracking\n<remote>/<branch>. However, such DWIM'ing was overlooked and\ngit-worktree existed long enough without it that, due to backward\ncompatibility concerns, the new DWIM'ing got hidden behind a switch,\nhence --guess-remote (\"worktree.guessRemote\") to enable it.\n\nUnrelated: Thomas added another DWIM, which we just finalized a few\ndays ago, which extends the shorthand \"git worktree add <path>\" to\nfirst check if a local branch named $(basename <path>) already exists\nand merely check that out into the new worktree rather than trying to\ncreate a new branch of that name (which is another obvious DWIM missed\nduring initial implementation). In other words:\n\n    branch=$(basename <path>)\n    if local branch named $branch does _not_ exist:\n        if remote-tracking branch named $branch exists:\n            git branch --track $branch $guessedRemote/$branch\n        else:\n            git branch $branch HEAD\n        fi\n    fi\n    git worktree add <path> $branch\n"},{"id":"346635","messageId":"CAPig+cS90ro649V2Oy5kytwpAsmYWdGG3depHhd1AFq3Cxn=7A@mail.gmail.com","threadId":"48399","inReplyTo":"CAPig+cSRBBzXDWMJd5k=ZGOt_ayATWB6fZQrcMqJkP8ja4Tq+g@mail.gmail.com","subject":"Re: [PATCH] checkout & worktree: introduce a core.DWIMRemote setting","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-05-04T07:23:24Z","receivedAt":"2018-05-04T07:23:28Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, May 4, 2018 at 3:14 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:\n> The setting up of a remote-tracking branch is DWIM'd as of 4e85333197\n> (\"worktree: make add <path> <branch> dwim\", 2017-11-26); it doesn't\n> require an explicit option to enable it (though tracking can be\n> disabled via --no-track). The \"guess-remote\" feature does something\n> entirely different; it was added to avoid backward compatibility\n> problems.\n>\n> In long-form:\n>\n>     git worktree add <path> <branch>\n>\n> adds a new worktree at <path> and checks out <branch>. As originally\n> implemented, shortened:\n>\n>     git worktree add <path>\n>\n> does one type of DWIM, as a convenience, and pretends that the user\n> actually typed:\n>\n>     branch=$(basename <path>)\n>     git branch $branch HEAD\n>     git workree add <path> $branch\n\nThis explanation isn't wrong, but it conflates two separate features,\nthus is confusing. The $(basename <branch>) DWIM'ing from the short\nform of the command, \"git worktree add <path>\", doesn't actually have\nanything to do with the DWIM'ing added by 4e85333197 (\"worktree: make\nadd <path> <branch> dwim\", 2017-11-26), which merely tries to DWIM a\nremote-tracking branch of similar name to $branch (whether $branch\ncame from long or short form of the command), so I confused the issue\nunnecessarily by talking about the short form.\n"},{"id":"346638","messageId":"87muxfyhy9.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"CACsJy8CYp3zVSRtS5ozHCitnWU81AamqEGR8RZdDkwujShEatw@mail.gmail.com","subject":"Re: [PATCH v2] checkout & worktree: introduce checkout.implicitRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-04T07:54:06Z","receivedAt":"2018-05-04T07:54:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, May 03 2018, Duy Nguyen wrote:\n\n> On Thu, May 3, 2018 at 3:18 PM, Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> Introduce a checkout.implicitRemote setting which can be used to\n>> designate a remote to prefer (via checkout.implicitRemote=origin) when\n>> running e.g. \"git checkout master\" to mean origin/master, even though\n>> there's other remotes that have the \"master\" branch.\n>>\n>> I want this because it's very handy to use this workflow to checkout a\n>> repository and create a topic branch, then get back to a \"master\" as\n>> retrieved from upstream:\n>>\n>>     (\n>>         rm -rf /tmp/tbdiff &&\n>>         git clone git@github.com:trast/tbdiff.git &&\n>>         cd tbdiff &&\n>>         git branch -m topic &&\n>>         git checkout master\n>>     )\n>>\n>> That will output:\n>>\n>>     Branch 'master' set up to track remote branch 'master' from 'origin'.\n>>     Switched to a new branch 'master'\n>>\n>> But as soon as a new remote is added (e.g. just to inspect something\n>> from someone else) the DWIMery goes away:\n>>\n>>     (\n>>         rm -rf /tmp/tbdiff &&\n>>         git clone git@github.com:trast/tbdiff.git &&\n>>         cd tbdiff &&\n>>         git branch -m topic &&\n>>         git remote add avar git@github.com:avar/tbdiff.git &&\n>>         git fetch avar &&\n>>         git checkout master\n>>     )\n>>\n>> Will output:\n>>\n>>     error: pathspec 'master' did not match any file(s) known to git.\n>>\n>> The new checkout.implicitRemote config allows me to say that whenever\n>> that ambiguity comes up I'd like to prefer \"origin\", and it'll still\n>> work as though the only remote I had was \"origin\".\n>>\n>> I considered splitting this into checkout.implicitRemote and\n>> worktree.implicitRemote, but it's probably less confusing to break our\n>> own rules that anything shared between config should live in core.*\n>> than have two config settings, and I couldn't come up with a short\n>> name under core.* that made sense (core.implicitRemoteForCheckout?).\n>\n> I wonder if it's difficult to add a dwim hook that allows us to\n> replace the entire dwim logic with a hook? Doing this \"preferring\n> origin\" in a shell script should be easy and it lets us play more with\n> tweaking dwim logic. (Yeah sorry I'm getting off topic again)\n\nRealistically the way we do hooks now would make the UI of this suck,\ni.e. you couldn't configure it globally or system-wide easily. Something\nlike what I noted in\nhttps://public-inbox.org/git/871sf3el01.fsf@evledraar.gmail.com/ would\nmake it suck less, but that's a much bigger task.\n\nI think for now this is a common enough case users run into that it\nmakes sense to add it to the code, and if we ever do have more\nextensible hook features in the future we can just go and replace\nvarious options like these to use them.\n"},{"id":"346642","messageId":"CAPig+cR59-OEDL7GDNZF2v7a-NOrUH2zC9EwDvuog8QLRtvKTA@mail.gmail.com","threadId":"48399","inReplyTo":"20180503131840.27956-1-avarab@gmail.com","subject":"Re: [PATCH v2] checkout & worktree: introduce checkout.implicitRemote","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-05-04T09:58:19Z","receivedAt":"2018-05-04T09:58:24Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, May 3, 2018 at 9:18 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Introduce a checkout.implicitRemote setting which can be used to\n> designate a remote to prefer (via checkout.implicitRemote=origin) when\n> running e.g. \"git checkout master\" to mean origin/master, even though\n> there's other remotes that have the \"master\" branch.\n> [...]\n> The new checkout.implicitRemote config allows me to say that whenever\n> that ambiguity comes up I'd like to prefer \"origin\", and it'll still\n> work as though the only remote I had was \"origin\".\n> [...]\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> @@ -1084,6 +1084,23 @@ browser.<tool>.path::\n> +checkout.implicitRemote::\n> +       When you run 'git checkout <something>' and only have one\n> +       remote, it may implicitly fall back on checking out and\n> +       tracking e.g. 'origin/<something>'. This stops working as soon\n> +       as you have more than one remote with a '<something>'\n> +       reference. This setting allows for setting the name of a\n> +       special remote that should always win when it comes to\n\n\"special\" is overly broad. \"preferred\" may better convey the intended\nmeaning. Simply dropping \"special\" also works.\n\nSubjective; not worth a re-roll.\n\n> +       disambiguation. The typical use-case is to set this to\n> +       `origin`.\n> ++\n> +Currently this is used by linkgit:git-checkout[1] when 'git checkout\n> +<something>' will checkout the '<something>' branch on another remote,\n> +and by linkgit:git-worktree[1] when 'git worktree add' when referring\n\n\"when ... when\"?\n\n> +to a remote branch.  This setting might be used for other\n> +checkout-like commands or functionality in the future when\n> +appropriate.\n\nNot sure the final sentence adds value as user-facing documentation\n(versus the commit message in which it may).\n\n> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\n> @@ -60,6 +60,11 @@ with a matching name, treat as equivalent to:\n>  $ git worktree add --track -b <branch> <path> <remote>/<branch>\n> +It's also possible to use the `checkout.implicitRemote` setting to\n> +designate a special remote this rule should be applied to, even if the\n\nAgain, you could drop \"special\".\n\n> +branch isn't unique across all remotes. See `checkout.implicitRemote`\n> +in linkgit:git-config[1].\n\nI have a hard time digesting this paragraph. Perhaps it wants to say:\n\n    Option `checkout.implicitRemote` can be configured to designate a\n    <remote> to use when <branch> isn't unique across all remotes.\n    See ...\n\n> diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\n> @@ -450,6 +450,24 @@ test_expect_success 'git worktree --no-guess-remote option overrides config' '\n> +test_expect_success '\"add\" <path> <branch> dwims with checkout.implicitRemote' '\n> +       test_when_finished rm -rf repo_upstream repo_dwim foo &&\n> +       setup_remote_repo repo_upstream repo_dwim &&\n> +       git init repo_dwim &&\n\nMaybe replace \"dwim\" here and in the test title with something else\nsince checkout.implicitRemote is no longer about DWIM'ing?\n\n> +       (\n> +               cd repo_dwim &&\n> +               git remote add repo_upstream2 ../repo_upstream &&\n> +               git fetch repo_upstream2 &&\n> +               test_must_fail git worktree add ../foo foo &&\n> +               git -c checkout.implicitRemote=repo_upstream worktree add ../foo foo\n> +       ) &&\n> +       (\n> +               cd foo &&\n> +               test_branch_upstream foo repo_upstream foo &&\n> +               test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo\n> +       )\n> +'\n"},{"id":"346652","messageId":"CACsJy8D+k+9SNBq7SNFpUhhdZDH7QaZ3+_rXMrge+b5KF_j9xA@mail.gmail.com","threadId":"48399","inReplyTo":"87muxfyhy9.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2] checkout & worktree: introduce checkout.implicitRemote","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-04T14:58:43Z","receivedAt":"2018-05-04T14:59:17Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, May 4, 2018 at 9:54 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Realistically the way we do hooks now would make the UI of this suck,\n> i.e. you couldn't configure it globally or system-wide easily. Something\n> like what I noted in\n> https://public-inbox.org/git/871sf3el01.fsf@evledraar.gmail.com/ would\n> make it suck less, but that's a much bigger task.\n\nI thought you would bring this up :) I've given some more thoughts on\nthis topic and am willing to implement something like below, in a week\nor two. Would that help change your mind?\n\nI proposed hooks.* config space in that same thread. Here is the\nextension to make it cover both of your points.\n\nhooks.* can have multiple values. So you can specify\nhooks.post-checkout multiple times and all those scripts will run (in\nthe same order you specified). Since we already have a search path for\nconfig (/etc/gitconfig -> $HOME/.gitconfig -> $REPO/config) this helps\nhooks management as well. Execution order is still the same: if you\nspecify hooks.post-checkout in both /etc/gitconfig and .git/config,\nthen the one in /etc/gitconfig is executed first, the one in .git\nsecond.\n\nAnd here's something extra to make it possible to override the search\norder: if you specify hooks.post-checkout = reset (reset is a random\nkeyword) then _all_ post-checkout hooks before this point are ignored.\nSo you can put this in .git/config\n\n[hooks]\n    post-checkout = reset\n    post-checkout = ~/some-hook\n\nand can be sure that post-checkout specified in $HOME and /etc will\nnot affect you, only ~/some-hook will run. If you drop the second line\nthen you have no post-checkout hooks. This is a workaround for a\nbigger problem (not being able to unset a config entry) but I think\nit's sufficient for this use case.\n-- \nDuy\n"},{"id":"346685","messageId":"87k1sjxps6.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"CACsJy8D+k+9SNBq7SNFpUhhdZDH7QaZ3+_rXMrge+b5KF_j9xA@mail.gmail.com","subject":"Re: [PATCH v2] checkout & worktree: introduce checkout.implicitRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-04T18:02:33Z","receivedAt":"2018-05-04T18:02:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, May 04 2018, Duy Nguyen wrote:\n\n> On Fri, May 4, 2018 at 9:54 AM, Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> Realistically the way we do hooks now would make the UI of this suck,\n>> i.e. you couldn't configure it globally or system-wide easily. Something\n>> like what I noted in\n>> https://public-inbox.org/git/871sf3el01.fsf@evledraar.gmail.com/ would\n>> make it suck less, but that's a much bigger task.\n>\n> I thought you would bring this up :) I've given some more thoughts on\n> this topic and am willing to implement something like below, in a week\n> or two. Would that help change your mind?\n>\n> I proposed hooks.* config space in that same thread. Here is the\n> extension to make it cover both of your points.\n>\n> hooks.* can have multiple values. So you can specify\n> hooks.post-checkout multiple times and all those scripts will run (in\n> the same order you specified). Since we already have a search path for\n> config (/etc/gitconfig -> $HOME/.gitconfig -> $REPO/config) this helps\n> hooks management as well. Execution order is still the same: if you\n> specify hooks.post-checkout in both /etc/gitconfig and .git/config,\n> then the one in /etc/gitconfig is executed first, the one in .git\n> second.\n>\n> And here's something extra to make it possible to override the search\n> order: if you specify hooks.post-checkout = reset (reset is a random\n> keyword) then _all_ post-checkout hooks before this point are ignored.\n> So you can put this in .git/config\n>\n> [hooks]\n>     post-checkout = reset\n>     post-checkout = ~/some-hook\n>\n> and can be sure that post-checkout specified in $HOME and /etc will\n> not affect you, only ~/some-hook will run. If you drop the second line\n> then you have no post-checkout hooks. This is a workaround for a\n> bigger problem (not being able to unset a config entry) but I think\n> it's sufficient for this use case.\n\nA few things:\n\n 1) I don't see a reason to hold back this feature in particular waiting\n    for some way to do it via config / hooks. If we grow some compatible\n    way to do it via hooks in the future, great, then we can just make\n    this (and numerous other config values) historical aliases for that\n    facility.\n\n 2) Let's not have some per-config type way to reset earlier config. I\n    suggested a way to do it generally in\n    https://public-inbox.org/git/874lkq11ug.fsf@evledraar.gmail.com/ I'm\n    not saying we should go for that suggestion in particular, but that\n    we should have something general.\n\n 3) Doing #2 will take a lot longer to implement than what you're\n    suggesting.\n\n 4) I think such a facility should also be able to replace something\n    like https://docs.gitlab.com/ee/administration/custom_hooks.html\n    which requires not just supporting hooks from the config, but\n    executing some hooks on the FS in glob() order.\n\n 5) What you're describing above is just 1/2 of what we need to have a\n    viable way to replace something like this checkout.implicitRemote\n    with a hook while providing the same functionality to the end\n    user.\n\n    If something ships as a config value like checkout.implicitRemote\n    users can just turn it on in their ~/.gitconfig without any further\n    config, or you can tell users in docs via one-liner to enable it.\n\n    We also need the other half which some method of shipping \"standard\"\n    hooks with git, i.e. with your proposal something like (along with\n    the general config reset):\n\n        [config]\n            reject = hooks.post-checkout\n        [hooks]\n            # Reads config from hooks.post-checkout-implicit-remote.*\n            # (e.g. hooks.post-checkout-implicit-remote.remote = origin)\n            post-checkout = git-hooks://post-checkout-implicit-remote-config\n\n   Only then will this be as easy to enable as `git config --global..`\n   (although you'll need two invocations of that, which is fine...)\n\n 6) The feature being discussed here would not be a post-checkout hook,\n    but would need to be fairly integrated with the internals of\n    checkout, see `dwim_ok` and the big comment in\n    parse_branchname_arg().\n\n    I.e. the thing that makes this work is the Nth step in some fairly\n    intricate fallback logic. It's not clear to me in the general case\n    how we'd turn this into a hook without having N number of\n    checkout-what-step-N-hooks, or requiring every checkout-what hook to\n    implement a huge part of what checkout.c is doing now just to tweak\n    some tiny aspect of it, such as this tweak of\n    unique_tracking_name().\n"},{"id":"348478","messageId":"20180524194704.936-1-avarab@gmail.com","threadId":"48399","inReplyTo":"CAPig+cR59-OEDL7GDNZF2v7a-NOrUH2zC9EwDvuog8QLRtvKTA@mail.gmail.com","subject":"[PATCH v3] checkout & worktree: introduce checkout.implicitRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-24T19:47:04Z","receivedAt":"2018-05-24T19:47:22Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Introduce a checkout.implicitRemote setting which can be used to\ndesignate a remote to prefer (via checkout.implicitRemote=origin) when\nrunning e.g. \"git checkout master\" to mean origin/master, even though\nthere's other remotes that have the \"master\" branch.\n\nI want this because it's very handy to use this workflow to checkout a\nrepository and create a topic branch, then get back to a \"master\" as\nretrieved from upstream:\n\n    (\n        rm -rf /tmp/tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git checkout master\n    )\n\nThat will output:\n\n    Branch 'master' set up to track remote branch 'master' from 'origin'.\n    Switched to a new branch 'master'\n\nBut as soon as a new remote is added (e.g. just to inspect something\nfrom someone else) the DWIMery goes away:\n\n    (\n        rm -rf /tmp/tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git remote add avar git@github.com:avar/tbdiff.git &&\n        git fetch avar &&\n        git checkout master\n    )\n\nWill output:\n\n    error: pathspec 'master' did not match any file(s) known to git.\n\nThe new checkout.implicitRemote config allows me to say that whenever\nthat ambiguity comes up I'd like to prefer \"origin\", and it'll still\nwork as though the only remote I had was \"origin\".\n\nI considered splitting this into checkout.implicitRemote and\nworktree.implicitRemote, but it's probably less confusing to break our\nown rules that anything shared between config should live in core.*\nthan have two config settings, and I couldn't come up with a short\nname under core.* that made sense (core.implicitRemoteForCheckout?).\n\nSee also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\nfrotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\nto begin with, and 4e85333197 (\"worktree: make add <path> <branch>\ndwim\", 2017-11-26) which added it to git-worktree.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nTook me a while to get around to this, but this v3 addresses feedback\nby Eric about the docs. Thanks! interdiff:\n    \n    1: 905a17f35f ! 1: b23d1b71e9 checkout & worktree: introduce checkout.implicitRemote\n        @@ -71,16 +71,15 @@\n         +\ttracking e.g. 'origin/<something>'. This stops working as soon\n         +\tas you have more than one remote with a '<something>'\n         +\treference. This setting allows for setting the name of a\n        -+\tspecial remote that should always win when it comes to\n        ++\tpreferred remote that should always win when it comes to\n         +\tdisambiguation. The typical use-case is to set this to\n         +\t`origin`.\n         ++\n         +Currently this is used by linkgit:git-checkout[1] when 'git checkout\n         +<something>' will checkout the '<something>' branch on another remote,\n        -+and by linkgit:git-worktree[1] when 'git worktree add' when referring\n        -+to a remote branch.  This setting might be used for other\n        -+checkout-like commands or functionality in the future when\n        -+appropriate.\n        ++and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n        ++remote branch. This setting might be used for other checkout-like\n        ++commands or functionality in the future.\n         +\n          clean.requireForce::\n          \tA boolean to make git-clean do nothing unless given -f,\n        @@ -110,14 +109,14 @@\n          $ git worktree add --track -b <branch> <path> <remote>/<branch>\n          ------------\n          +\n        -+It's also possible to use the `checkout.implicitRemote` setting to\n        -+designate a special remote this rule should be applied to, even if the\n        -+branch isn't unique across all remotes. See `checkout.implicitRemote`\n        -+in linkgit:git-config[1].\n        ++The `checkout.implicitRemote` setting can be used to to designate a\n        ++preferred `<remote>` this rule should be applied to, even if the\n        ++`<branch>` isn't unique across all remotes. See\n        ++`checkout.implicitRemote` in linkgit:git-config[1].\n         ++\n          If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n        - then, as a convenience, a new branch based at HEAD is created automatically,\n        - as if `-b $(basename <path>)` was specified.\n        + then, as a convenience, the new worktree is associated with a branch\n        + (call it `<branch>`) named after `$(basename <path>)`.  If `<branch>`\n         \n         diff --git a/builtin/checkout.c b/builtin/checkout.c\n         --- a/builtin/checkout.c\n        @@ -235,6 +234,6 @@\n         +\t)\n         +'\n         +\n        - post_checkout_hook () {\n        - \tgitdir=${1:-.git}\n        - \ttest_when_finished \"rm -f $gitdir/hooks/post-checkout\" &&\n        + test_expect_success 'git worktree add does not match remote' '\n        + \ttest_when_finished rm -rf repo_a repo_b foo &&\n        + \tsetup_remote_repo repo_a repo_b &&\n\n Documentation/config.txt       | 16 ++++++++++++++++\n Documentation/git-checkout.txt |  6 ++++++\n Documentation/git-worktree.txt |  5 +++++\n builtin/checkout.c             |  6 ++++--\n checkout.c                     | 17 +++++++++++++++--\n t/t2024-checkout-dwim.sh       | 10 ++++++++++\n t/t2025-worktree-add.sh        | 18 ++++++++++++++++++\n 7 files changed, 74 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 84e2891aed..7f095a260a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1098,6 +1098,22 @@ browser.<tool>.path::\n \tbrowse HTML help (see `-w` option in linkgit:git-help[1]) or a\n \tworking repository in gitweb (see linkgit:git-instaweb[1]).\n \n+checkout.implicitRemote::\n+\tWhen you run 'git checkout <something>' and only have one\n+\tremote, it may implicitly fall back on checking out and\n+\ttracking e.g. 'origin/<something>'. This stops working as soon\n+\tas you have more than one remote with a '<something>'\n+\treference. This setting allows for setting the name of a\n+\tpreferred remote that should always win when it comes to\n+\tdisambiguation. The typical use-case is to set this to\n+\t`origin`.\n++\n+Currently this is used by linkgit:git-checkout[1] when 'git checkout\n+<something>' will checkout the '<something>' branch on another remote,\n+and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n+remote branch. This setting might be used for other checkout-like\n+commands or functionality in the future.\n+\n clean.requireForce::\n \tA boolean to make git-clean do nothing unless given -f,\n \t-i or -n.   Defaults to true.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ca5fc9c798..753aa4001f 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -38,6 +38,12 @@ equivalent to\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes the `checkout.implicitRemote`\n+variable can be used to pick the remote you really mean. Set it to\n+e.g. `checkout.implicitRemote=origin` to always checkout remote\n+branches from there. See also `checkout.implicitRemote` in\n+linkgit:git-config[1].\n++\n You could omit <branch>, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex afc6576a14..6febb21dca 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -60,6 +60,11 @@ with a matching name, treat as equivalent to:\n $ git worktree add --track -b <branch> <path> <remote>/<branch>\n ------------\n +\n+The `checkout.implicitRemote` setting can be used to to designate a\n+preferred `<remote>` this rule should be applied to, even if the\n+`<branch>` isn't unique across all remotes. See\n+`checkout.implicitRemote` in linkgit:git-config[1].\n++\n If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n then, as a convenience, the new worktree is associated with a branch\n (call it `<branch>`) named after `$(basename <path>)`.  If `<branch>`\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2b3b768eff..88e650f0f8 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -909,8 +909,10 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t *   (b) If <something> is _not_ a commit, either \"--\" is present\n \t *       or <something> is not a path, no -t or -b was given, and\n \t *       and there is a tracking branch whose name is <something>\n-\t *       in one and only one remote, then this is a short-hand to\n-\t *       fork local <something> from that remote-tracking branch.\n+\t *       in one and only one remote (or if the branch exists on the\n+\t *       remote named in checkout.implicitRemote), then this is a\n+\t *       short-hand to fork local <something> from that\n+\t *       remote-tracking branch.\n \t *\n \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\n \t *\ndiff --git a/checkout.c b/checkout.c\nindex ac42630f74..2c898cfe19 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -1,12 +1,15 @@\n #include \"cache.h\"\n #include \"remote.h\"\n #include \"checkout.h\"\n+#include \"config.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n \tint unique;\n+\tconst char *implicit_remote;\n+\tchar *implicit_dst_ref;\n };\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n@@ -20,6 +23,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\tfree(query.dst);\n \t\treturn 0;\n \t}\n+\tif (cb->implicit_remote && !strcmp(remote->name, cb->implicit_remote))\n+\t\tcb->implicit_dst_ref = xstrdup(query.dst);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\tcb->unique = 0;\n@@ -31,13 +36,21 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \n const char *unique_tracking_name(const char *name, struct object_id *oid)\n {\n-\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n+\tconst char *implicit_remote = NULL;\n+\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1, NULL, NULL };\n+\tif (!git_config_get_string_const(\"checkout.implicitremote\", &implicit_remote))\n+\t\tcb_data.implicit_remote = implicit_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tfree(cb_data.src_ref);\n-\tif (cb_data.unique)\n+\tfree((char *)implicit_remote);\n+\tif (cb_data.unique) {\n+\t\tfree(cb_data.implicit_dst_ref);\n \t\treturn cb_data.dst_ref;\n+\t}\n \tfree(cb_data.dst_ref);\n+\tif (cb_data.implicit_dst_ref)\n+\t\treturn cb_data.implicit_dst_ref;\n \treturn NULL;\n }\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 3e5ac81bd2..da6bd74bbc 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -68,6 +68,16 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_branch master\n '\n \n+test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.implicitRemote #1' '\n+\tgit checkout -B master &&\n+\ttest_might_fail git branch -D foo &&\n+\n+\tgit -c checkout.implicitRemote=repo_a checkout foo &&\n+\ttest_branch foo &&\n+\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n+\ttest_branch_upstream foo repo_a foo\n+'\n+\n test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \tgit checkout -B master &&\n \ttest_might_fail git branch -D bar &&\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex 2240498924..271a6413f0 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -402,6 +402,24 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n \t)\n '\n \n+test_expect_success '\"add\" <path> <branch> dwims with checkout.implicitRemote' '\n+\ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n+\tsetup_remote_repo repo_upstream repo_dwim &&\n+\tgit init repo_dwim &&\n+\t(\n+\t\tcd repo_dwim &&\n+\t\tgit remote add repo_upstream2 ../repo_upstream &&\n+\t\tgit fetch repo_upstream2 &&\n+\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\tgit -c checkout.implicitRemote=repo_upstream worktree add ../foo foo\n+\t) &&\n+\t(\n+\t\tcd foo &&\n+\t\ttest_branch_upstream foo repo_upstream foo &&\n+\t\ttest_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo\n+\t)\n+'\n+\n test_expect_success 'git worktree add does not match remote' '\n \ttest_when_finished rm -rf repo_a repo_b foo &&\n \tsetup_remote_repo repo_a repo_b &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348523","messageId":"xmqqpo1knog0.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180524194704.936-1-avarab@gmail.com","subject":"Re: [PATCH v3] checkout & worktree: introduce checkout.implicitRemote","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-05-25T08:12:47Z","receivedAt":"2018-05-25T08:12:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> +checkout.implicitRemote::\n> +\tWhen you run 'git checkout <something>' and only have one\n> +\tremote, it may implicitly fall back on checking out and\n> +\ttracking e.g. 'origin/<something>'.\n\nYup, that is quite implicit.  It works without configuring anything,\nonly as long as you have a single remote.  That is quite implicit.\n\n\n> This stops working as soon\n> +\tas you have more than one remote with a '<something>'\n> +\treference. This setting allows for setting the name of a\n> +\tpreferred remote that should always win when it comes to\n> +\tdisambiguation. The typical use-case is to set this to\n> +\t`origin`.\n\nSo this feature and configuration feels more like an explicit one,\nto be used to affect how Git works when the implicit one does not\nwork well.  I would have called it checkout.defaultRemote, as it\nwould be a nonsense name to call it checkout.explicitRemote ;-).\n\n> +Currently this is used by linkgit:git-checkout[1] when 'git checkout\n> +<something>' will checkout the '<something>' branch on another remote,\n> +and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n> +remote branch. This setting might be used for other checkout-like\n> +commands or functionality in the future.\n\nHmph, that is an interesting direction.  You foresee that you'll\nhave a single repository with multiple remotes to grab and share\nobjects from different people working on the same project, and use\nmultiple worktrees to work on different branches, yet you are happy\nto declare that each worktree is to work with one particular remote?\n\nWe'd need a per-worktree config file to make it work, I guess, or\na three-level checkout.$worktree_id.defaultRemote configuration\nvariable, perhaps?\n\nIn any case I can see how this will help those with multiple remotes\n(including me ;-).  Thanks for moving this topic forward.\n\n"},{"id":"348543","messageId":"CACsJy8BjdiNF623vygUkp_1T82X5DwQiCQP9T55mATzFHuH1RQ@mail.gmail.com","threadId":"48399","inReplyTo":"xmqqpo1knog0.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3] checkout & worktree: introduce checkout.implicitRemote","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-25T14:42:18Z","receivedAt":"2018-05-25T14:42:53Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, May 25, 2018 at 10:12 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> +Currently this is used by linkgit:git-checkout[1] when 'git checkout\n>> +<something>' will checkout the '<something>' branch on another remote,\n>> +and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n>> +remote branch. This setting might be used for other checkout-like\n>> +commands or functionality in the future.\n>\n> Hmph, that is an interesting direction.  You foresee that you'll\n> have a single repository with multiple remotes to grab and share\n> objects from different people working on the same project, and use\n> multiple worktrees to work on different branches, yet you are happy\n> to declare that each worktree is to work with one particular remote?\n>\n> We'd need a per-worktree config file to make it work, I guess, or\n> a three-level checkout.$worktree_id.defaultRemote configuration\n> variable, perhaps?\n\nI still plan to revisit per-worktree config support [1] at some point\nand finish it off. Or is it decided that we don't need a generic\nmechanism and will add a new level like this for each config var that\nis per-worktree? If defaultRemote sets a precedence, then it'll be the\nway to go from now on or we'll have another mess when some config does\n\"foo.$worktree.bar\" while others use per-worktree config files.\n\n[1] https://public-inbox.org/git/20170110112524.12870-1-pclouds@gmail.com/\n-- \nDuy\n"},{"id":"348549","messageId":"87k1rr7f86.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"CACsJy8BjdiNF623vygUkp_1T82X5DwQiCQP9T55mATzFHuH1RQ@mail.gmail.com","subject":"Re: [PATCH v3] checkout & worktree: introduce checkout.implicitRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-25T18:38:33Z","receivedAt":"2018-05-25T18:38:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, May 25 2018, Duy Nguyen wrote:\n\n> On Fri, May 25, 2018 at 10:12 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> +Currently this is used by linkgit:git-checkout[1] when 'git checkout\n>>> +<something>' will checkout the '<something>' branch on another remote,\n>>> +and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n>>> +remote branch. This setting might be used for other checkout-like\n>>> +commands or functionality in the future.\n>>\n>> Hmph, that is an interesting direction.  You foresee that you'll\n>> have a single repository with multiple remotes to grab and share\n>> objects from different people working on the same project, and use\n>> multiple worktrees to work on different branches, yet you are happy\n>> to declare that each worktree is to work with one particular remote?\n>>\n>> We'd need a per-worktree config file to make it work, I guess, or\n>> a three-level checkout.$worktree_id.defaultRemote configuration\n>> variable, perhaps?\n>\n> I still plan to revisit per-worktree config support [1] at some point\n> and finish it off. Or is it decided that we don't need a generic\n> mechanism and will add a new level like this for each config var that\n> is per-worktree? If defaultRemote sets a precedence, then it'll be the\n> way to go from now on or we'll have another mess when some config does\n> \"foo.$worktree.bar\" while others use per-worktree config files.\n\nWhat do you think about including worktree in this at this time? I'm not\nvery familiar with it and just included it because I worked my way\nbackwards from the DWIM function, but I could exclude it if you think\nit's too much trouble, i.e. if you'd like to hold out for some facility\nlike the per-worktree config Junio mentioned.\n"},{"id":"348589","messageId":"CACsJy8A67bNEHqdi-dg5YCKseutiKhZGyBeBF_z3oY_EB6rQqQ@mail.gmail.com","threadId":"48399","inReplyTo":"87k1rr7f86.fsf@evledraar.gmail.com","subject":"Re: [PATCH v3] checkout & worktree: introduce checkout.implicitRemote","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-05-26T12:49:53Z","receivedAt":"2018-05-26T12:50:28Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Fri, May 25, 2018 at 8:38 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> On Fri, May 25 2018, Duy Nguyen wrote:\n>\n>> On Fri, May 25, 2018 at 10:12 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>>> +Currently this is used by linkgit:git-checkout[1] when 'git checkout\n>>>> +<something>' will checkout the '<something>' branch on another remote,\n>>>> +and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n>>>> +remote branch. This setting might be used for other checkout-like\n>>>> +commands or functionality in the future.\n>>>\n>>> Hmph, that is an interesting direction.  You foresee that you'll\n>>> have a single repository with multiple remotes to grab and share\n>>> objects from different people working on the same project, and use\n>>> multiple worktrees to work on different branches, yet you are happy\n>>> to declare that each worktree is to work with one particular remote?\n>>>\n>>> We'd need a per-worktree config file to make it work, I guess, or\n>>> a three-level checkout.$worktree_id.defaultRemote configuration\n>>> variable, perhaps?\n>>\n>> I still plan to revisit per-worktree config support [1] at some point\n>> and finish it off. Or is it decided that we don't need a generic\n>> mechanism and will add a new level like this for each config var that\n>> is per-worktree? If defaultRemote sets a precedence, then it'll be the\n>> way to go from now on or we'll have another mess when some config does\n>> \"foo.$worktree.bar\" while others use per-worktree config files.\n>\n> What do you think about including worktree in this at this time? I'm not\n> very familiar with it and just included it because I worked my way\n> backwards from the DWIM function, but I could exclude it if you think\n> it's too much trouble, i.e. if you'd like to hold out for some facility\n> like the per-worktree config Junio mentioned.\n\nI think Junio was talking about the future. What is currently in the\npatch, both code and document, is fine. But if you're going to\nimplement core.$worktree.defaultRemote at this time, maybe wait a bit.\nI could try to resurrect the per-worktree config topic in a month or\nso and if it does not work out, then everybody will add new config\nvars if they want per-worktree support.\n-- \nDuy\n"},{"id":"348885","messageId":"87a7sg9sjz.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"20180524194704.936-1-avarab@gmail.com","subject":"Re: [PATCH v3] checkout & worktree: introduce checkout.implicitRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T07:45:52Z","receivedAt":"2018-05-31T07:46:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, May 24 2018, Ævar Arnfjörð Bjarmason wrote:\n\n>  struct tracking_name_data {\n>  \t/* const */ char *src_ref;\n>  \tchar *dst_ref;\n>  \tstruct object_id *dst_oid;\n>  \tint unique;\n> +\tconst char *implicit_remote;\n> +\tchar *implicit_dst_ref;\n>  };\n\nThere's a bug here that I'll fix in a v3. We need to have a implicit_*\nvariant for dst_oid as well. Currently this will be buggy and check out\norigin/<branch>, but then check the index out to the tree of whatever\nthe last <someremote>/<branch> we iterated over was.\n\nEasiy fix and I already have it locally, I just want to improve some of\nthe testing. I missed it because in my tests I'd just re-add the same\nremote again, so the trees were the same.\n\n>  static int check_tracking_name(struct remote *remote, void *cb_data)\n> @@ -20,6 +23,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n>  \t\tfree(query.dst);\n>  \t\treturn 0;\n>  \t}\n> +\tif (cb->implicit_remote && !strcmp(remote->name, cb->implicit_remote))\n> +\t\tcb->implicit_dst_ref = xstrdup(query.dst);\n>  \tif (cb->dst_ref) {\n>  \t\tfree(query.dst);\n>  \t\tcb->unique = 0;\n> @@ -31,13 +36,21 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n>\n>  const char *unique_tracking_name(const char *name, struct object_id *oid)\n>  {\n> -\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n> +\tconst char *implicit_remote = NULL;\n> +\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1, NULL, NULL };\n> +\tif (!git_config_get_string_const(\"checkout.implicitremote\", &implicit_remote))\n> +\t\tcb_data.implicit_remote = implicit_remote;\n>  \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n>  \tcb_data.dst_oid = oid;\n>  \tfor_each_remote(check_tracking_name, &cb_data);\n>  \tfree(cb_data.src_ref);\n> -\tif (cb_data.unique)\n> +\tfree((char *)implicit_remote);\n> +\tif (cb_data.unique) {\n> +\t\tfree(cb_data.implicit_dst_ref);\n>  \t\treturn cb_data.dst_ref;\n> +\t}\n>  \tfree(cb_data.dst_ref);\n> +\tif (cb_data.implicit_dst_ref)\n> +\t\treturn cb_data.implicit_dst_ref;\n>  \treturn NULL;\n>  }\n> diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\n> index 3e5ac81bd2..da6bd74bbc 100755\n> --- a/t/t2024-checkout-dwim.sh\n> +++ b/t/t2024-checkout-dwim.sh\n> @@ -68,6 +68,16 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n>  \ttest_branch master\n>  '\n>\n> +test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.implicitRemote #1' '\n> +\tgit checkout -B master &&\n> +\ttest_might_fail git branch -D foo &&\n> +\n> +\tgit -c checkout.implicitRemote=repo_a checkout foo &&\n> +\ttest_branch foo &&\n> +\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n> +\ttest_branch_upstream foo repo_a foo\n> +'\n> +\n>  test_expect_success 'checkout of branch from a single remote succeeds #1' '\n>  \tgit checkout -B master &&\n>  \ttest_might_fail git branch -D bar &&\n> diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\n> index 2240498924..271a6413f0 100755\n> --- a/t/t2025-worktree-add.sh\n> +++ b/t/t2025-worktree-add.sh\n> @@ -402,6 +402,24 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n>  \t)\n>  '\n>\n> +test_expect_success '\"add\" <path> <branch> dwims with checkout.implicitRemote' '\n> +\ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n> +\tsetup_remote_repo repo_upstream repo_dwim &&\n> +\tgit init repo_dwim &&\n> +\t(\n> +\t\tcd repo_dwim &&\n> +\t\tgit remote add repo_upstream2 ../repo_upstream &&\n> +\t\tgit fetch repo_upstream2 &&\n> +\t\ttest_must_fail git worktree add ../foo foo &&\n> +\t\tgit -c checkout.implicitRemote=repo_upstream worktree add ../foo foo\n> +\t) &&\n> +\t(\n> +\t\tcd foo &&\n> +\t\ttest_branch_upstream foo repo_upstream foo &&\n> +\t\ttest_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo\n> +\t)\n> +'\n> +\n>  test_expect_success 'git worktree add does not match remote' '\n>  \ttest_when_finished rm -rf repo_a repo_b foo &&\n>  \tsetup_remote_repo repo_a repo_b &&\n"},{"id":"348929","messageId":"20180531195252.29173-1-avarab@gmail.com","threadId":"48399","inReplyTo":"87a7sg9sjz.fsf@evledraar.gmail.com","subject":"[PATCH v4 0/9] ambiguous checkout UI & checkout.defaultRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:43Z","receivedAt":"2018-05-31T19:53:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"v4 started as a simple bug-fix for this one-part series, but since\nit's not going to make 2.18.0 at this point I thought I'd do some more\nwork on it. Comments on patches below:\n\nÆvar Arnfjörð Bjarmason (9):\n  checkout tests: index should be clean after dwim checkout\n\nTests that would have revealed the bug in v3.\n\n  checkout.h: wrap the arguments to unique_tracking_name()\n  checkout.[ch]: move struct declaration into *.h\n\nBoring moving code around.\n\n  checkout.[ch]: introduce an *_INIT macro\n\nMake checkout.h have a TRACKING_NAME_DATA_INIT for its struct.\n\n  checkout.[ch]: change \"unique\" member to \"num_matches\"\n  checkout: pass the \"num_matches\" up to callers\n  builtin/checkout.c: use \"ret\" variable for return\n\nRefactoring with no changes yet to make subsequent changes smaller.\n\n  checkout: add advice for ambiguous \"checkout <branch>\"\n\nEven if checkout.defaultRemote is off we now print advice telling the\nuser why their \"git checkout branch\" didn't work.\n\n  checkout & worktree: introduce checkout.defaultRemote\n\nIt's now called checkout.defaultRemote not checkout.implicitRemote on\nJunio's suggestion. On reflection that's better.\n\nImproved tests for git-worktree (similar to the dwim checkout tests\nimprovements earlier), and the the documentation for git-checkout &\ngit-worktree.\n\nI'm omitting the tbdiff because most of it's because of the new\npatches in this series. Better just to read them.\n\n Documentation/config.txt       | 26 +++++++++++++++\n Documentation/git-checkout.txt |  9 ++++++\n Documentation/git-worktree.txt |  9 ++++++\n advice.c                       |  2 ++\n advice.h                       |  1 +\n builtin/checkout.c             | 44 ++++++++++++++++++++------\n builtin/worktree.c             |  4 +--\n checkout.c                     | 37 +++++++++++++++-------\n checkout.h                     | 16 +++++++++-\n t/t2024-checkout-dwim.sh       | 58 +++++++++++++++++++++++++++++++++-\n t/t2025-worktree-add.sh        | 21 ++++++++++++\n 11 files changed, 203 insertions(+), 24 deletions(-)\n\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348930","messageId":"20180531195252.29173-2-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v4 1/9] checkout tests: index should be clean after dwim checkout","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:44Z","receivedAt":"2018-05-31T19:53:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Assert that whenever there's a DWIM checkout that the index should be\nclean afterwards, in addition to the correct branch being checked-out.\n\nThe way the DWIM checkout code in checkout.[ch] works is by looping\nover all remotes, and for each remote trying to find if a given\nreference name only exists on that remote, or if it exists anywhere\nelse.\n\nThis is done by starting out with a `unique = 1` tracking variable in\na struct shared by the entire loop, which will get set to `0` if the\ndata reference is not unique.\n\nThus if we find a match we know the dst_oid member of\ntracking_name_data must be correct, since it's associated with the\nonly reference on the only remote that could have matched our query.\n\nBut if there was ever a mismatch there for some reason we might end up\nwith the correct branch checked out, but at the wrong oid, which would\nshow whatever the difference between the two staged in the\nindex (checkout branch A, stage changes from the state of branch B).\n\nSo let's amend the tests (mostly added in) 399e4a1c56 (\"t2024: Add\ntests verifying current DWIM behavior of 'git checkout <branch>'\",\n2013-04-21) to always assert that \"status\" is clean after we run\n\"checkout\", that's being done with \"-uno\" because there's going to be\nsome untracked files related to the test itself which we don't care\nabout.\n\nThen if we ever run into this sort of regression, either in the\nexisting code or with a new feature, we'll know.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t2024-checkout-dwim.sh | 32 +++++++++++++++++++++++++++++++-\n 1 file changed, 31 insertions(+), 1 deletion(-)\n\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 3e5ac81bd2..29c1eada17 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -23,6 +23,12 @@ test_branch_upstream () {\n \ttest_cmp expect.upstream actual.upstream\n }\n \n+status_uno_is_clean() {\n+\t>status.expect &&\n+\tgit status -uno --porcelain >status.actual &&\n+\ttest_cmp status.expect status.actual\n+}\n+\n test_expect_success 'setup' '\n \ttest_commit my_master &&\n \tgit init repo_a &&\n@@ -55,6 +61,7 @@ test_expect_success 'checkout of non-existing branch fails' '\n \ttest_might_fail git branch -D xyzzy &&\n \n \ttest_must_fail git checkout xyzzy &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/xyzzy &&\n \ttest_branch master\n '\n@@ -64,8 +71,10 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_might_fail git branch -D foo &&\n \n \ttest_must_fail git checkout foo &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/foo &&\n-\ttest_branch master\n+\ttest_branch master &&\n+\tstatus_uno_is_clean\n '\n \n test_expect_success 'checkout of branch from a single remote succeeds #1' '\n@@ -73,6 +82,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \ttest_might_fail git branch -D bar &&\n \n \tgit checkout bar &&\n+\tstatus_uno_is_clean &&\n \ttest_branch bar &&\n \ttest_cmp_rev remotes/repo_a/bar HEAD &&\n \ttest_branch_upstream bar repo_a bar\n@@ -83,6 +93,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '\n \ttest_might_fail git branch -D baz &&\n \n \tgit checkout baz &&\n+\tstatus_uno_is_clean &&\n \ttest_branch baz &&\n \ttest_cmp_rev remotes/other_b/baz HEAD &&\n \ttest_branch_upstream baz repo_b baz\n@@ -90,6 +101,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '\n \n test_expect_success '--no-guess suppresses branch auto-vivification' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D bar &&\n \n \ttest_must_fail git checkout --no-guess bar &&\n@@ -99,6 +111,7 @@ test_expect_success '--no-guess suppresses branch auto-vivification' '\n \n test_expect_success 'setup more remotes with unconventional refspecs' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \tgit init repo_c &&\n \t(\n \t\tcd repo_c &&\n@@ -128,27 +141,33 @@ test_expect_success 'setup more remotes with unconventional refspecs' '\n \n test_expect_success 'checkout of branch from multiple remotes fails #2' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D bar &&\n \n \ttest_must_fail git checkout bar &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/bar &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch from multiple remotes fails #3' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D baz &&\n \n \ttest_must_fail git checkout baz &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/baz &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch from a single remote succeeds #3' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \tgit checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_branch spam &&\n \ttest_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&\n \ttest_branch_upstream spam repo_c spam\n@@ -156,9 +175,11 @@ test_expect_success 'checkout of branch from a single remote succeeds #3' '\n \n test_expect_success 'checkout of branch from a single remote succeeds #4' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D eggs &&\n \n \tgit checkout eggs &&\n+\tstatus_uno_is_clean &&\n \ttest_branch eggs &&\n \ttest_cmp_rev refs/repo_d/eggs HEAD &&\n \ttest_branch_upstream eggs repo_d eggs\n@@ -166,32 +187,38 @@ test_expect_success 'checkout of branch from a single remote succeeds #4' '\n \n test_expect_success 'checkout of branch with a file having the same name fails' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \ttest_must_fail git checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/spam &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch with a file in subdir having the same name fails' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \tmkdir sub &&\n \tmv spam sub/spam &&\n \ttest_must_fail git -C sub checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/spam &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \tgit checkout spam -- &&\n+\tstatus_uno_is_clean &&\n \ttest_branch spam &&\n \ttest_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&\n \ttest_branch_upstream spam repo_c spam\n@@ -200,6 +227,7 @@ test_expect_success 'checkout <branch> -- succeeds, even if a file with the same\n test_expect_success 'loosely defined local base branch is reported correctly' '\n \n \tgit checkout master &&\n+\tstatus_uno_is_clean &&\n \tgit branch strict &&\n \tgit branch loose &&\n \tgit commit --allow-empty -m \"a bit more\" &&\n@@ -210,7 +238,9 @@ test_expect_success 'loosely defined local base branch is reported correctly' '\n \ttest_config branch.loose.merge master &&\n \n \tgit checkout strict | sed -e \"s/strict/BRANCHNAME/g\" >expect &&\n+\tstatus_uno_is_clean &&\n \tgit checkout loose | sed -e \"s/loose/BRANCHNAME/g\" >actual &&\n+\tstatus_uno_is_clean &&\n \n \ttest_cmp expect actual\n '\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348931","messageId":"20180531195252.29173-3-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v4 2/9] checkout.h: wrap the arguments to unique_tracking_name()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:45Z","receivedAt":"2018-05-31T19:53:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The line was too long already, and will be longer still when a later\nchange adds another argument.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.h | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/checkout.h b/checkout.h\nindex 9980711179..4cd4cd1c23 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -8,6 +8,7 @@\n  * tracking branch.  Return the name of the remote if such a branch\n  * exists, NULL otherwise.\n  */\n-extern const char *unique_tracking_name(const char *name, struct object_id *oid);\n+extern const char *unique_tracking_name(const char *name,\n+\t\t\t\t\tstruct object_id *oid);\n \n #endif /* CHECKOUT_H */\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348932","messageId":"20180531195252.29173-4-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v4 3/9] checkout.[ch]: move struct declaration into *.h","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:46Z","receivedAt":"2018-05-31T19:53:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the tracking_name_data struct used in checkout.c into its\ncorresponding header file. This wasn't done in 7c85a87c54 (\"checkout:\nfactor out functions to new lib file\", 2017-11-26) when checkout.[ch]\nwere created, and is more consistent with the rest of the codebase.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.c | 7 -------\n checkout.h | 7 +++++++\n 2 files changed, 7 insertions(+), 7 deletions(-)\n\ndiff --git a/checkout.c b/checkout.c\nindex bdefc888ba..8d68f75ad1 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -3,13 +3,6 @@\n #include \"refspec.h\"\n #include \"checkout.h\"\n \n-struct tracking_name_data {\n-\t/* const */ char *src_ref;\n-\tchar *dst_ref;\n-\tstruct object_id *dst_oid;\n-\tint unique;\n-};\n-\n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n \tstruct tracking_name_data *cb = cb_data;\ndiff --git a/checkout.h b/checkout.h\nindex 4cd4cd1c23..04b52f9ffe 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -3,6 +3,13 @@\n \n #include \"cache.h\"\n \n+struct tracking_name_data {\n+\t/* const */ char *src_ref;\n+\tchar *dst_ref;\n+\tstruct object_id *dst_oid;\n+\tint unique;\n+};\n+\n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n  * tracking branch.  Return the name of the remote if such a branch\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348933","messageId":"20180531195252.29173-5-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v4 4/9] checkout.[ch]: introduce an *_INIT macro","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:47Z","receivedAt":"2018-05-31T19:53:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add an *_INIT macro for the tracking_name_data similar to what exists\nelsewhere in the codebase, e.g. OID_ARRAY_INIT in sha1-array.h. This\nwill make it more idiomatic in later changes to add more fields to the\nstruct & its initialization macro.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.c | 2 +-\n checkout.h | 2 ++\n 2 files changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/checkout.c b/checkout.c\nindex 8d68f75ad1..629fc1d5c4 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -25,7 +25,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \n const char *unique_tracking_name(const char *name, struct object_id *oid)\n {\n-\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n+\tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\ndiff --git a/checkout.h b/checkout.h\nindex 04b52f9ffe..a61ec93e65 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -10,6 +10,8 @@ struct tracking_name_data {\n \tint unique;\n };\n \n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n+\n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n  * tracking branch.  Return the name of the remote if such a branch\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348934","messageId":"20180531195252.29173-6-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v4 5/9] checkout.[ch]: change \"unique\" member to \"num_matches\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:48Z","receivedAt":"2018-05-31T19:53:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Internally track how many matches we find in the check_tracking_name()\ncallback. Nothing uses this now, but it will be made use of in a later\nchange.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.c | 4 ++--\n checkout.h | 4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/checkout.c b/checkout.c\nindex 629fc1d5c4..7ce5306bc7 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -14,9 +14,9 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\tfree(query.dst);\n \t\treturn 0;\n \t}\n+\tcb->num_matches++;\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n-\t\tcb->unique = 0;\n \t\treturn 0;\n \t}\n \tcb->dst_ref = query.dst;\n@@ -30,7 +30,7 @@ const char *unique_tracking_name(const char *name, struct object_id *oid)\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tfree(cb_data.src_ref);\n-\tif (cb_data.unique)\n+\tif (cb_data.num_matches == 1)\n \t\treturn cb_data.dst_ref;\n \tfree(cb_data.dst_ref);\n \treturn NULL;\ndiff --git a/checkout.h b/checkout.h\nindex a61ec93e65..2decb9b820 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -7,10 +7,10 @@ struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n-\tint unique;\n+\tint num_matches;\n };\n \n-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n \n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348935","messageId":"20180531195252.29173-8-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v4 7/9] builtin/checkout.c: use \"ret\" variable for return","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:50Z","receivedAt":"2018-05-31T19:53:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There is no point in doing this right now, but in later change the\n\"ret\" variable will be inspected. This change makes that meaningful\nchange smaller.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 10 ++++++----\n 1 file changed, 6 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex ec7cf93b4a..423e056acd 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1266,9 +1266,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr)\n-\t\treturn checkout_paths(&opts, new_branch_info.name,\n-\t\t\t\t      &dwim_remotes_matched);\n-\telse\n+\tif (opts.patch_mode || opts.pathspec.nr) {\n+\t\tint ret = checkout_paths(&opts, new_branch_info.name,\n+\t\t\t\t\t &dwim_remotes_matched);\n+\t\treturn ret;\n+\t} else {\n \t\treturn checkout_branch(&opts, &new_branch_info);\n+\t}\n }\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348936","messageId":"20180531195252.29173-7-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v4 6/9] checkout: pass the \"num_matches\" up to callers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:49Z","receivedAt":"2018-05-31T19:53:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Pass the previously added \"num_matches\" struct value up to the callers\nof unique_tracking_name(). This will allow callers to optionally print\nbetter error messages in a later change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 16 +++++++++++-----\n builtin/worktree.c |  4 ++--\n checkout.c         |  5 ++++-\n checkout.h         |  3 ++-\n 4 files changed, 19 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2e1d2376d2..ec7cf93b4a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -239,7 +239,8 @@ static int checkout_merged(int pos, const struct checkout *state)\n }\n \n static int checkout_paths(const struct checkout_opts *opts,\n-\t\t\t  const char *revision)\n+\t\t\t  const char *revision,\n+\t\t\t  int *dwim_remotes_matched)\n {\n \tint pos;\n \tstruct checkout state = CHECKOUT_INIT;\n@@ -878,7 +879,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n \t\t\t\tstruct checkout_opts *opts,\n-\t\t\t\tstruct object_id *rev)\n+\t\t\t\tstruct object_id *rev,\n+\t\t\t\tint *dwim_remotes_matched)\n {\n \tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n@@ -972,7 +974,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n-\t\t\tconst char *remote = unique_tracking_name(arg, rev);\n+\t\t\tconst char *remote = unique_tracking_name(arg, rev,\n+\t\t\t\t\t\t\t\t  dwim_remotes_matched);\n \t\t\tif (remote) {\n \t\t\t\t*new_branch = arg;\n \t\t\t\targ = remote;\n@@ -1109,6 +1112,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch = 1;\n+\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n \t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n@@ -1219,7 +1223,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts.new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev);\n+\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\n@@ -1262,7 +1267,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \n \tUNLEAK(opts);\n \tif (opts.patch_mode || opts.pathspec.nr)\n-\t\treturn checkout_paths(&opts, new_branch_info.name);\n+\t\treturn checkout_paths(&opts, new_branch_info.name,\n+\t\t\t\t      &dwim_remotes_matched);\n \telse\n \t\treturn checkout_branch(&opts, &new_branch_info);\n }\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 5c7d2bb180..a763dbdccb 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -412,7 +412,7 @@ static const char *dwim_branch(const char *path, const char **new_branch)\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n \t\tconst char *remote =\n-\t\t\tunique_tracking_name(*new_branch, &oid);\n+\t\t\tunique_tracking_name(*new_branch, &oid, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -484,7 +484,7 @@ static int add(int ac, const char **av, const char *prefix)\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 7ce5306bc7..c578782baa 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -23,12 +23,15 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \treturn 0;\n }\n \n-const char *unique_tracking_name(const char *name, struct object_id *oid)\n+const char *unique_tracking_name(const char *name, struct object_id *oid,\n+\t\t\t\t int *dwim_remotes_matched)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n+\tif (dwim_remotes_matched)\n+\t\t*dwim_remotes_matched = cb_data.num_matches;\n \tfree(cb_data.src_ref);\n \tif (cb_data.num_matches == 1)\n \t\treturn cb_data.dst_ref;\ndiff --git a/checkout.h b/checkout.h\nindex 2decb9b820..4e518c801a 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -18,6 +18,7 @@ struct tracking_name_data {\n  * exists, NULL otherwise.\n  */\n extern const char *unique_tracking_name(const char *name,\n-\t\t\t\t\tstruct object_id *oid);\n+\t\t\t\t\tstruct object_id *oid,\n+\t\t\t\t\tint *dwim_remotes_matched);\n \n #endif /* CHECKOUT_H */\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348937","messageId":"20180531195252.29173-10-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v4 9/9] checkout & worktree: introduce checkout.defaultRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:52Z","receivedAt":"2018-05-31T19:53:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Introduce a checkout.defaultRemote setting which can be used to\ndesignate a remote to prefer (via checkout.defaultRemote=origin) when\nrunning e.g. \"git checkout master\" to mean origin/master, even though\nthere's other remotes that have the \"master\" branch.\n\nI want this because it's very handy to use this workflow to checkout a\nrepository and create a topic branch, then get back to a \"master\" as\nretrieved from upstream:\n\n    (\n        cd /tmp &&\n        rm -rf tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git checkout master\n    )\n\nThat will output:\n\n    Branch 'master' set up to track remote branch 'master' from 'origin'.\n    Switched to a new branch 'master'\n\nBut as soon as a new remote is added (e.g. just to inspect something\nfrom someone else) the DWIMery goes away:\n\n    (\n        cd /tmp &&\n        rm -rf tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git remote add avar git@github.com:avar/tbdiff.git &&\n        git fetch avar &&\n        git checkout master\n    )\n\nWill output (without the advice output added earlier in this series):\n\n    error: pathspec 'master' did not match any file(s) known to git.\n\nThe new checkout.defaultRemote config allows me to say that whenever\nthat ambiguity comes up I'd like to prefer \"origin\", and it'll still\nwork as though the only remote I had was \"origin\".\n\nAlso adjust the advice.checkoutAmbiguousRemoteBranchName message to\nmention this new config setting to the user, the full output on my\ngit.git is now (the last paragraph is new):\n\n    $ ./git --exec-path=$PWD checkout master\n    error: pathspec 'master' did not match any file(s) known to git.\n    hint: The argument 'master' matched more than one remote tracking branch.\n    hint: We found 26 remotes with a reference that matched. So we fell back\n    hint: on trying to resolve the argument as a path, but failed there too!\n    hint:\n    hint: Perhaps you meant fully qualify the branch name? E.g. origin/<name>\n    hint: instead of <name>?\n    hint:\n    hint: If you'd like to always have checkouts of 'master' prefer one remote,\n    hint: e.g. the 'origin' remote, consider setting checkout.defaultRemote=origin\n    hint: in your config. See the 'git-config' manual page for details.\n\nI considered splitting this into checkout.defaultRemote and\nworktree.defaultRemote, but it's probably less confusing to break our\nown rules that anything shared between config should live in core.*\nthan have two config settings, and I couldn't come up with a short\nname under core.* that made sense (core.defaultRemoteForCheckout?).\n\nSee also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\nfrotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\nto begin with, and 4e85333197 (\"worktree: make add <path> <branch>\ndwim\", 2017-11-26) which added it to git-worktree.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt       | 21 ++++++++++++++++++++-\n Documentation/git-checkout.txt |  9 +++++++++\n Documentation/git-worktree.txt |  9 +++++++++\n builtin/checkout.c             | 15 +++++++++++----\n checkout.c                     | 21 ++++++++++++++++++++-\n checkout.h                     |  5 ++++-\n t/t2024-checkout-dwim.sh       | 12 ++++++++++++\n t/t2025-worktree-add.sh        | 21 +++++++++++++++++++++\n 8 files changed, 106 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 08d3e70989..e0d92217ac 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -350,7 +350,10 @@ advice.*::\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\n-\t\tchecked out.\n+\t\tchecked out. See the `checkout.defaultRemote`\n+\t\tconfiguration variable for how to set a given remote\n+\t\tto used by default in some situations where this\n+\t\tadvice would be printed.\n \tamWorkDir::\n \t\tAdvice that shows the location of the patch file when\n \t\tlinkgit:git-am[1] fails to apply it.\n@@ -1105,6 +1108,22 @@ browser.<tool>.path::\n \tbrowse HTML help (see `-w` option in linkgit:git-help[1]) or a\n \tworking repository in gitweb (see linkgit:git-instaweb[1]).\n \n+checkout.defaultRemote::\n+\tWhen you run 'git checkout <something>' and only have one\n+\tremote, it may implicitly fall back on checking out and\n+\ttracking e.g. 'origin/<something>'. This stops working as soon\n+\tas you have more than one remote with a '<something>'\n+\treference. This setting allows for setting the name of a\n+\tpreferred remote that should always win when it comes to\n+\tdisambiguation. The typical use-case is to set this to\n+\t`origin`.\n++\n+Currently this is used by linkgit:git-checkout[1] when 'git checkout\n+<something>' will checkout the '<something>' branch on another remote,\n+and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n+remote branch. This setting might be used for other checkout-like\n+commands or functionality in the future.\n+\n clean.requireForce::\n \tA boolean to make git-clean do nothing unless given -f,\n \t-i or -n.   Defaults to true.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ca5fc9c798..8cb77bddeb 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -38,6 +38,15 @@ equivalent to\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch> is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n You could omit <branch>, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex afc6576a14..6638d5ca3d 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -60,6 +60,15 @@ with a matching name, treat as equivalent to:\n $ git worktree add --track -b <branch> <path> <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch> is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n then, as a convenience, the new worktree is associated with a branch\n (call it `<branch>`) named after `$(basename <path>)`.  If `<branch>`\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 710369a60a..78bbead000 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -913,8 +913,10 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t *   (b) If <something> is _not_ a commit, either \"--\" is present\n \t *       or <something> is not a path, no -t or -b was given, and\n \t *       and there is a tracking branch whose name is <something>\n-\t *       in one and only one remote, then this is a short-hand to\n-\t *       fork local <something> from that remote-tracking branch.\n+\t *       in one and only one remote (or if the branch exists on the\n+\t *       remote named in checkout.defaultRemote), then this is a\n+\t *       short-hand to fork local <something> from that\n+\t *       remote-tracking branch.\n \t *\n \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\n \t *\n@@ -1277,9 +1279,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n \t\t\t\t \"\\n\"\n \t\t\t\t \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n-\t\t\t\t \"instead of <name>?\"),\n+\t\t\t\t \"instead of <name>?\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"If you'd like to always have checkouts of '%s' prefer one remote,\\n\"\n+\t\t\t\t \"e.g. the 'origin' remote, consider setting checkout.defaultRemote=origin\\n\"\n+\t\t\t\t \"in your config. See the 'git-config' manual page for details.\"),\n \t\t\t       argv[0],\n-\t\t\t       dwim_remotes_matched);\n+\t\t\t       dwim_remotes_matched,\n+\t\t\t       argv[0]);\n \t\treturn ret;\n \t} else {\n \t\treturn checkout_branch(&opts, &new_branch_info);\ndiff --git a/checkout.c b/checkout.c\nindex c578782baa..e6cfe6d2ab 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -2,6 +2,7 @@\n #include \"remote.h\"\n #include \"refspec.h\"\n #include \"checkout.h\"\n+#include \"config.h\"\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n@@ -15,6 +16,12 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\treturn 0;\n \t}\n \tcb->num_matches++;\n+\tif (cb->default_remote && !strcmp(remote->name, cb->default_remote)) {\n+\t\tstruct object_id *dst = xmalloc(sizeof(*cb->default_dst_oid));\n+\t\tcb->default_dst_ref = xstrdup(query.dst);\n+\t\toidcpy(dst, cb->dst_oid);\n+\t\tcb->default_dst_oid = dst;\n+\t}\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -27,14 +34,26 @@ const char *unique_tracking_name(const char *name, struct object_id *oid,\n \t\t\t\t int *dwim_remotes_matched)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n+\tconst char *default_remote = NULL;\n+\tif (!git_config_get_string_const(\"checkout.defaultremote\", &default_remote))\n+\t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\n \tfree(cb_data.src_ref);\n-\tif (cb_data.num_matches == 1)\n+\tfree((char *)default_remote);\n+\tif (cb_data.num_matches == 1) {\n+\t\tfree(cb_data.default_dst_ref);\n+\t\tfree(cb_data.default_dst_oid);\n \t\treturn cb_data.dst_ref;\n+\t}\n \tfree(cb_data.dst_ref);\n+\tif (cb_data.default_dst_ref) {\n+\t\toidcpy(oid, cb_data.default_dst_oid);\n+\t\tfree(cb_data.default_dst_oid);\n+\t\treturn cb_data.default_dst_ref;\n+\t}\n \treturn NULL;\n }\ndiff --git a/checkout.h b/checkout.h\nindex 4e518c801a..e9f1372f32 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -8,9 +8,12 @@ struct tracking_name_data {\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n \tint num_matches;\n+\tconst char *default_remote;\n+\tchar *default_dst_ref;\n+\tstruct object_id *default_dst_oid;\n };\n \n-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0, NULL, NULL, NULL }\n \n /*\n  * Check if the branch name uniquely matches a branch name on a remote\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 14735f5bb8..ccd96ba85c 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -91,6 +91,18 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'\n \ttest_i18ngrep ! \"^hint: \" stderr\n '\n \n+test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.defaultRemote #1' '\n+\tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n+\ttest_might_fail git branch -D foo &&\n+\n+\tgit -c checkout.defaultRemote=repo_a checkout foo &&\n+\tstatus_uno_is_clean &&\n+\ttest_branch foo &&\n+\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n+\ttest_branch_upstream foo repo_a foo\n+'\n+\n test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \tgit checkout -B master &&\n \ttest_might_fail git branch -D bar &&\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex d2e49f7632..be6e093142 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -402,6 +402,27 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n \t)\n '\n \n+test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n+\ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n+\tsetup_remote_repo repo_upstream repo_dwim &&\n+\tgit init repo_dwim &&\n+\t(\n+\t\tcd repo_dwim &&\n+\t\tgit remote add repo_upstream2 ../repo_upstream &&\n+\t\tgit fetch repo_upstream2 &&\n+\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n+\t\t>status.expect &&\n+\t\tgit status -uno --porcelain >status.actual &&\n+\t\ttest_cmp status.expect status.actual\n+\t) &&\n+\t(\n+\t\tcd foo &&\n+\t\ttest_branch_upstream foo repo_upstream foo &&\n+\t\ttest_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo\n+\t)\n+'\n+\n test_expect_success 'git worktree add does not match remote' '\n \ttest_when_finished rm -rf repo_a repo_b foo &&\n \tsetup_remote_repo repo_a repo_b &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348938","messageId":"20180531195252.29173-9-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v4 8/9] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-05-31T19:52:51Z","receivedAt":"2018-05-31T19:53:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As the \"checkout\" documentation describes:\n\n    If <branch> is not found but there does exist a tracking branch in\n    exactly one remote (call it <remote>) with a matching name, treat\n    as equivalent to [...] <remote>/<branch.\n\nThis is a really useful feature, the problem is that when you another\nremote (e.g. a fork) git won't find a unique branch name anymore, and\nwill instead print this nondescript message:\n\n    $ git checkout master\n    error: pathspec 'master' did not match any file(s) known to git\n\nNow it will, on my git.git checkout, print:\n\n    $ ./git --exec-path=$PWD checkout master\n    error: pathspec 'master' did not match any file(s) known to git.\n    hint: The argument 'master' matched more than one remote tracking branch.\n    hint: We found 26 remotes with a reference that matched. So we fell back\n    hint: on trying to resolve the argument as a path, but failed there too!\n    hint:\n    hint: Perhaps you meant fully qualify the branch name? E.g. origin/<name>\n    hint: instead of <name>?\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt |  7 +++++++\n advice.c                 |  2 ++\n advice.h                 |  1 +\n builtin/checkout.c       | 11 +++++++++++\n t/t2024-checkout-dwim.sh | 14 ++++++++++++++\n 5 files changed, 35 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 7d8383433c..08d3e70989 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -344,6 +344,13 @@ advice.*::\n \t\tAdvice shown when you used linkgit:git-checkout[1] to\n \t\tmove to the detach HEAD state, to instruct how to create\n \t\ta local branch after the fact.\n+\tcheckoutAmbiguousRemoteBranchName::\n+\t\tAdvice shown when the argument to\n+\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tremote tracking branch on more than one remote in\n+\t\tsituations where an unambiguous argument would have\n+\t\totherwise caused a remote-tracking branch to be\n+\t\tchecked out.\n \tamWorkDir::\n \t\tAdvice that shows the location of the patch file when\n \t\tlinkgit:git-am[1] fails to apply it.\ndiff --git a/advice.c b/advice.c\nindex 370a56d054..75e7dede90 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -21,6 +21,7 @@ int advice_add_embedded_repo = 1;\n int advice_ignored_hook = 1;\n int advice_waiting_for_editor = 1;\n int advice_graft_file_deprecated = 1;\n+int advice_checkout_ambiguous_remote_branch_name = 1;\n \n static int advice_use_color = -1;\n static char advice_colors[][COLOR_MAXLEN] = {\n@@ -72,6 +73,7 @@ static struct {\n \t{ \"ignoredhook\", &advice_ignored_hook },\n \t{ \"waitingforeditor\", &advice_waiting_for_editor },\n \t{ \"graftfiledeprecated\", &advice_graft_file_deprecated },\n+\t{ \"checkoutambiguousremotebranchname\", &advice_checkout_ambiguous_remote_branch_name },\n \n \t/* make this an alias for backward compatibility */\n \t{ \"pushnonfastforward\", &advice_push_update_rejected }\ndiff --git a/advice.h b/advice.h\nindex 9f5064e82a..4d11d51d43 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -22,6 +22,7 @@ extern int advice_add_embedded_repo;\n extern int advice_ignored_hook;\n extern int advice_waiting_for_editor;\n extern int advice_graft_file_deprecated;\n+extern int advice_checkout_ambiguous_remote_branch_name;\n \n int git_default_advice_config(const char *var, const char *value);\n __attribute__((format (printf, 1, 2)))\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 423e056acd..710369a60a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -22,6 +22,7 @@\n #include \"resolve-undo.h\"\n #include \"submodule-config.h\"\n #include \"submodule.h\"\n+#include \"advice.h\"\n \n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n@@ -1269,6 +1270,16 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tif (opts.patch_mode || opts.pathspec.nr) {\n \t\tint ret = checkout_paths(&opts, new_branch_info.name,\n \t\t\t\t\t &dwim_remotes_matched);\n+\t\tif (ret && dwim_remotes_matched > 1 &&\n+\t\t    advice_checkout_ambiguous_remote_branch_name)\n+\t\t\tadvise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n+\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n+\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n+\t\t\t\t \"instead of <name>?\"),\n+\t\t\t       argv[0],\n+\t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n \t\treturn checkout_branch(&opts, &new_branch_info);\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 29c1eada17..14735f5bb8 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -77,6 +77,20 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \tstatus_uno_is_clean\n '\n \n+test_expect_success 'checkout of branch from multiple remotes fails with advice' '\n+\tgit checkout -B master &&\n+\ttest_might_fail git branch -D foo &&\n+\ttest_must_fail git checkout foo 2>stderr &&\n+\ttest_branch master &&\n+\tstatus_uno_is_clean &&\n+\ttest_i18ngrep \"^hint: \" stderr &&\n+\ttest_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \\\n+\t\tcheckout foo 2>stderr &&\n+\ttest_branch master &&\n+\tstatus_uno_is_clean &&\n+\ttest_i18ngrep ! \"^hint: \" stderr\n+'\n+\n test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \tgit checkout -B master &&\n \ttest_might_fail git branch -D bar &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"348939","messageId":"20180531214546.GC26159@hank.intra.tgummerer.com","threadId":"48399","inReplyTo":"20180531195252.29173-4-avarab@gmail.com","subject":"Re: [PATCH v4 3/9] checkout.[ch]: move struct declaration into *.h","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2018-05-31T21:45:46Z","receivedAt":"2018-05-31T21:45:14Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 05/31, Ævar Arnfjörð Bjarmason wrote:\n> Move the tracking_name_data struct used in checkout.c into its\n> corresponding header file. This wasn't done in 7c85a87c54 (\"checkout:\n> factor out functions to new lib file\", 2017-11-26) when checkout.[ch]\n> were created, and is more consistent with the rest of the codebase.\n\nWe seem to have plenty of structs defined in '.c' files, if they are\nonly needed there.  Its use also seems to be single purpose for the\ncallback data, so I'm a bit puzzled how having this in a header file\ninstead of the .c file would be helpful?\n\nI feel like having only the \"public\" part in the header file also\nhelps developers that are just looking for documentation of the\nfunctions they are looking at, by having less things to go through,\nthat they wouldn't necessarily care about.\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  checkout.c | 7 -------\n>  checkout.h | 7 +++++++\n>  2 files changed, 7 insertions(+), 7 deletions(-)\n> \n> diff --git a/checkout.c b/checkout.c\n> index bdefc888ba..8d68f75ad1 100644\n> --- a/checkout.c\n> +++ b/checkout.c\n> @@ -3,13 +3,6 @@\n>  #include \"refspec.h\"\n>  #include \"checkout.h\"\n>  \n> -struct tracking_name_data {\n> -\t/* const */ char *src_ref;\n> -\tchar *dst_ref;\n> -\tstruct object_id *dst_oid;\n> -\tint unique;\n> -};\n> -\n>  static int check_tracking_name(struct remote *remote, void *cb_data)\n>  {\n>  \tstruct tracking_name_data *cb = cb_data;\n> diff --git a/checkout.h b/checkout.h\n> index 4cd4cd1c23..04b52f9ffe 100644\n> --- a/checkout.h\n> +++ b/checkout.h\n> @@ -3,6 +3,13 @@\n>  \n>  #include \"cache.h\"\n>  \n> +struct tracking_name_data {\n> +\t/* const */ char *src_ref;\n> +\tchar *dst_ref;\n> +\tstruct object_id *dst_oid;\n> +\tint unique;\n> +};\n> +\n>  /*\n>   * Check if the branch name uniquely matches a branch name on a remote\n>   * tracking branch.  Return the name of the remote if such a branch\n> -- \n> 2.17.0.290.gded63e768a\n> \n"},{"id":"348940","messageId":"CAGZ79kZKaZq+bqFTwe+Y3FU-1DHPjzNrvMNU=DM10EyOXxZJVw@mail.gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-10-avarab@gmail.com","subject":"Re: [PATCH v4 9/9] checkout & worktree: introduce checkout.defaultRemote","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2018-05-31T21:49:06Z","receivedAt":"2018-05-31T21:49:10Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"Hi Ævar,\n\nSorry for chiming in late. I have a couple of thoughts:\n\n>     (\n>         cd /tmp &&\n>         rm -rf tbdiff &&\n>         git clone git@github.com:trast/tbdiff.git &&\n>         cd tbdiff &&\n>         git branch -m topic &&\n>         git checkout master\n>     )\n>\n> That will output:\n>\n>     Branch 'master' set up to track remote branch 'master' from 'origin'.\n>     Switched to a new branch 'master'\n\nI thought master is already there after the clone operation and\nyou'd merely switch back to the local branch that was created at\nclone time?\n\n    $ git clone git@github.com:trast/tbdiff.git && cd tbdiff\n    $ git branch\n    * master\n    $ cat .git/config\n...\n[branch \"master\"]\n    remote = origin\n    merge = refs/heads/master\n\nBut the observation is right, we get that message. When do we\ndo the setup for the master branch specifically?\n\n>\n> But as soon as a new remote is added (e.g. just to inspect something\n> from someone else) the DWIMery goes away:\n>\n>     (\n>         cd /tmp &&\n>         rm -rf tbdiff &&\n>         git clone git@github.com:trast/tbdiff.git &&\n>         cd tbdiff &&\n>         git branch -m topic &&\n>         git remote add avar git@github.com:avar/tbdiff.git &&\n>         git fetch avar &&\n>         git checkout master\n>     )\n>\n> Will output (without the advice output added earlier in this series):\n>\n>     error: pathspec 'master' did not match any file(s) known to git.\n>\n> The new checkout.defaultRemote config allows me to say that whenever\n> that ambiguity comes up I'd like to prefer \"origin\", and it'll still\n> work as though the only remote I had was \"origin\".\n>\n> Also adjust the advice.checkoutAmbiguousRemoteBranchName message to\n> mention this new config setting to the user, the full output on my\n> git.git is now (the last paragraph is new):\n>\n>     $ ./git --exec-path=$PWD checkout master\n>     error: pathspec 'master' did not match any file(s) known to git.\n>     hint: The argument 'master' matched more than one remote tracking branch.\n>     hint: We found 26 remotes with a reference that matched. So we fell back\n>     hint: on trying to resolve the argument as a path, but failed there too!\n>     hint:\n>     hint: Perhaps you meant fully qualify the branch name? E.g. origin/<name>\n\ns/meant fully/meant to fully/\ns/? E.g./?\\nFor example/\n\n>     hint: instead of <name>?\n\nIn builtin/submodule--helper.c there is get_default_remote() which also\nhardcodes \"origin\". I think that is a safe thing to do.\n\n>     hint:\n>     hint: If you'd like to always have checkouts of 'master' prefer one remote,\n>     hint: e.g. the 'origin' remote, consider setting checkout.defaultRemote=origin\n>     hint: in your config. See the 'git-config' manual page for details.\n\nhis new setting elevates one remote over all others, which may\nbe enough for most setups and not confusing, too.\nConsider the following:\n\n    git clone https://kernel.googlesource.com/pub/scm/git/git && cd git\n    git remote add gitster https://github.com/gitster/git\n    git remote add interesting-patches https://github.com/avar/git\n    git remote add my-github https://github.com/stefanbeller/git\n\n    git checkout master\n\nThis probably means I want to have origin/master (from kernel.org)\n\n    git checkout ab/checkout-implicit-remote\n\nThis probably wants to have it from gitster/ (as it is not found on kernel.org);\nI am not sure if it would want to look at interesting-patches/ that mirrors\ngithub, but probably if it were not to be found at gitster.\n\nSo maybe we rather want a setup to give a defined priority for\nthe search order:\n\n  git config dwim.remoteSearchOrder origin gitster avar\n\nStepping back a bit, there is already an order in the config file\nfor the remotes, and that order is used for example for 'fetch --all'.\n\nI wonder if we want to take that order? (Or are the days of hand\nediting the config over and this is too arcane? We would need a\nconfig command to re order remotes). Then we could just have a\nboolean switch to use the config order on ambiguity.\nAlthough you might want to have a different order for fetching\nand looking for the right checkout.\n\n> I considered splitting this into checkout.defaultRemote and\n> worktree.defaultRemote, but it's probably less confusing to break our\n> own rules that anything shared between config should live in core.*\n> than have two config settings, and I couldn't come up with a short\n> name under core.* that made sense (core.defaultRemoteForCheckout?).\n\n  core.dwimRemote ? It's a bit cryptic, though.\n\n> See also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\n> frotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\n> to begin with, and 4e85333197 (\"worktree: make add <path> <branch>\n> dwim\", 2017-11-26) which added it to git-worktree.\n\nStefan\n"},{"id":"348941","messageId":"20180531222253.GD26159@hank.intra.tgummerer.com","threadId":"48399","inReplyTo":"20180531195252.29173-10-avarab@gmail.com","subject":"Re: [PATCH v4 9/9] checkout & worktree: introduce checkout.defaultRemote","fromName":"Thomas Gummerer","fromEmail":"t.gummerer@gmail.com","sentAt":"2018-05-31T22:22:53Z","receivedAt":"2018-05-31T22:22:25Z","isPatch":true,"sender":{"key":"t.gummerer@gmail.com","avatar":"https://avatars.githubusercontent.com/u/191004?v=4"},"body":"On 05/31, Ævar Arnfjörð Bjarmason wrote:\n> Introduce a checkout.defaultRemote setting which can be used to\n> designate a remote to prefer (via checkout.defaultRemote=origin) when\n> running e.g. \"git checkout master\" to mean origin/master, even though\n> there's other remotes that have the \"master\" branch.\n> \n> I want this because it's very handy to use this workflow to checkout a\n> repository and create a topic branch, then get back to a \"master\" as\n> retrieved from upstream:\n> \n>     (\n>         cd /tmp &&\n>         rm -rf tbdiff &&\n>         git clone git@github.com:trast/tbdiff.git &&\n>         cd tbdiff &&\n>         git branch -m topic &&\n>         git checkout master\n>     )\n> \n> That will output:\n> \n>     Branch 'master' set up to track remote branch 'master' from 'origin'.\n>     Switched to a new branch 'master'\n> \n> But as soon as a new remote is added (e.g. just to inspect something\n> from someone else) the DWIMery goes away:\n> \n>     (\n>         cd /tmp &&\n>         rm -rf tbdiff &&\n>         git clone git@github.com:trast/tbdiff.git &&\n>         cd tbdiff &&\n>         git branch -m topic &&\n>         git remote add avar git@github.com:avar/tbdiff.git &&\n>         git fetch avar &&\n>         git checkout master\n>     )\n> \n> Will output (without the advice output added earlier in this series):\n> \n>     error: pathspec 'master' did not match any file(s) known to git.\n> \n> The new checkout.defaultRemote config allows me to say that whenever\n> that ambiguity comes up I'd like to prefer \"origin\", and it'll still\n> work as though the only remote I had was \"origin\".\n> \n> Also adjust the advice.checkoutAmbiguousRemoteBranchName message to\n> mention this new config setting to the user, the full output on my\n> git.git is now (the last paragraph is new):\n> \n>     $ ./git --exec-path=$PWD checkout master\n>     error: pathspec 'master' did not match any file(s) known to git.\n>     hint: The argument 'master' matched more than one remote tracking branch.\n>     hint: We found 26 remotes with a reference that matched. So we fell back\n>     hint: on trying to resolve the argument as a path, but failed there too!\n>     hint:\n>     hint: Perhaps you meant fully qualify the branch name? E.g. origin/<name>\n>     hint: instead of <name>?\n>     hint:\n>     hint: If you'd like to always have checkouts of 'master' prefer one remote,\n>     hint: e.g. the 'origin' remote, consider setting checkout.defaultRemote=origin\n>     hint: in your config. See the 'git-config' manual page for details.\n> \n> I considered splitting this into checkout.defaultRemote and\n> worktree.defaultRemote, but it's probably less confusing to break our\n> own rules that anything shared between config should live in core.*\n> than have two config settings, and I couldn't come up with a short\n> name under core.* that made sense (core.defaultRemoteForCheckout?).\n\nI agree that splitting this into two variables would be needlessly\nconfusing.  'checkout' and 'worktree add' are similar enough in\nspirit, that users only setting one of the configuration variables\nwould end up confused at some point.  Because the commands are so\nsimilar, I also feel like it would be okay to break our own rules\nhere, and use the 'core.defaultRemote' name you suggested (I also\ncan't come up with anything better in core.* right now).\n\n> See also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\n> frotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\n> to begin with, and 4e85333197 (\"worktree: make add <path> <branch>\n> dwim\", 2017-11-26) which added it to git-worktree.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  Documentation/config.txt       | 21 ++++++++++++++++++++-\n>  Documentation/git-checkout.txt |  9 +++++++++\n>  Documentation/git-worktree.txt |  9 +++++++++\n>  builtin/checkout.c             | 15 +++++++++++----\n>  checkout.c                     | 21 ++++++++++++++++++++-\n>  checkout.h                     |  5 ++++-\n>  t/t2024-checkout-dwim.sh       | 12 ++++++++++++\n>  t/t2025-worktree-add.sh        | 21 +++++++++++++++++++++\n>  8 files changed, 106 insertions(+), 7 deletions(-)\n>\n> [snip]\n>\n> diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\n> index ca5fc9c798..8cb77bddeb 100644\n> --- a/Documentation/git-checkout.txt\n> +++ b/Documentation/git-checkout.txt\n> @@ -38,6 +38,15 @@ equivalent to\n>  $ git checkout -b <branch> --track <remote>/<branch>\n>  ------------\n>  +\n> +If the branch exists in multiple remotes and one of them is named by\n> +the `checkout.defaultRemote` configuration variable, we'll use that\n> +one for the purposes of disambiguation, even if the `<branch>` isn't\n> +unique across all remotes. Set it to\n> +e.g. `checkout.defaultRemote=origin` to always checkout remote\n> +branches from there if `<branch> is ambiguous but exists on the\n\ns/`<branch>/&`/\n\n> +'origin' remote. See also `checkout.defaultRemote` in\n> +linkgit:git-config[1].\n> ++\n>  You could omit <branch>, in which case the command degenerates to\n>  \"check out the current branch\", which is a glorified no-op with\n>  rather expensive side-effects to show only the tracking information,\n> diff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\n> index afc6576a14..6638d5ca3d 100644\n> --- a/Documentation/git-worktree.txt\n> +++ b/Documentation/git-worktree.txt\n> @@ -60,6 +60,15 @@ with a matching name, treat as equivalent to:\n>  $ git worktree add --track -b <branch> <path> <remote>/<branch>\n>  ------------\n>  +\n> +If the branch exists in multiple remotes and one of them is named by\n> +the `checkout.defaultRemote` configuration variable, we'll use that\n> +one for the purposes of disambiguation, even if the `<branch>` isn't\n> +unique across all remotes. Set it to\n> +e.g. `checkout.defaultRemote=origin` to always checkout remote\n> +branches from there if `<branch> is ambiguous but exists on the\n\ns/`<branch>/&`/\n\n> +'origin' remote. See also `checkout.defaultRemote` in\n> +linkgit:git-config[1].\n> ++\n>  If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n>  then, as a convenience, the new worktree is associated with a branch\n>  (call it `<branch>`) named after `$(basename <path>)`.  If `<branch>`\n>\n> [snip]\n>\n> -- \n> 2.17.0.290.gded63e768a\n> \n"},{"id":"348961","messageId":"xmqqpo1b9rs8.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180531214546.GC26159@hank.intra.tgummerer.com","subject":"Re: [PATCH v4 3/9] checkout.[ch]: move struct declaration into *.h","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-06-01T02:14:47Z","receivedAt":"2018-06-01T02:14:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n> We seem to have plenty of structs defined in '.c' files, if they are\n> only needed there.  Its use also seems to be single purpose for the\n> callback data, so I'm a bit puzzled how having this in a header file\n> instead of the .c file would be helpful?\n>\n> I feel like having only the \"public\" part in the header file also\n> helps developers that are just looking for documentation of the\n> functions they are looking at, by having less things to go through,\n> that they wouldn't necessarily care about.\n\nYup, sounds like a sensible criterion to choose between <*.h> and\n<*.c>.\n"},{"id":"348962","messageId":"xmqqlgbz9rn9.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180531222253.GD26159@hank.intra.tgummerer.com","subject":"Re: [PATCH v4 9/9] checkout & worktree: introduce checkout.defaultRemote","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-06-01T02:17:46Z","receivedAt":"2018-06-01T02:17:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thomas Gummerer <t.gummerer@gmail.com> writes:\n\n>> I considered splitting this into checkout.defaultRemote and\n>> worktree.defaultRemote, but it's probably less confusing to break our\n>> own rules that anything shared between config should live in core.*\n>> than have two config settings, and I couldn't come up with a short\n>> name under core.* that made sense (core.defaultRemoteForCheckout?).\n\nI do think \"checkout\" in name is grately helpful.  I do not see why\nit is a bad idea for the worktree codepath to pay attention to the\ncheckout.defaultRemote configuration variable, especially when those\nwho are discussing this thread agree \"checkout\" and \"worktree add\"\nare quite similar in end-users' minds.\n"},{"id":"348967","messageId":"xmqqwovj882f.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180531195252.29173-2-avarab@gmail.com","subject":"Re: [PATCH v4 1/9] checkout tests: index should be clean after dwim checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-06-01T04:06:00Z","receivedAt":"2018-06-01T04:06:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Assert that whenever there's a DWIM checkout that the index should be\n> clean afterwards, in addition to the correct branch being checked-out.\n> ...\n> So let's amend the tests (mostly added in) 399e4a1c56 (\"t2024: Add\n> tests verifying current DWIM behavior of 'git checkout <branch>'\",\n> 2013-04-21) to always assert that \"status\" is clean after we run\n> \"checkout\", that's being done with \"-uno\" because there's going to be\n> some untracked files related to the test itself which we don't care\n> about.\n\nIt might not be absolutely necessary to state, but it would be\nhelpful to say that you are assuming to start a checkout (DWIM or\notherwise) from a clean state; without the assumption, the readers\nneed to think for a few breaths why \"the index should be clean\" is\ntrue.\n\nThe intention and the implementation of the change both mostly look\ngood to me from a quick read.\n\n>  test_expect_success 'setup' '\n>  \ttest_commit my_master &&\n>  \tgit init repo_a &&\n> @@ -55,6 +61,7 @@ test_expect_success 'checkout of non-existing branch fails' '\n>  \ttest_might_fail git branch -D xyzzy &&\n>  \n>  \ttest_must_fail git checkout xyzzy &&\n> +\tstatus_uno_is_clean &&\n>  \ttest_must_fail git rev-parse --verify refs/heads/xyzzy &&\n>  \ttest_branch master\n>  '\n> @@ -64,8 +71,10 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n>  \ttest_might_fail git branch -D foo &&\n>  \n>  \ttest_must_fail git checkout foo &&\n> +\tstatus_uno_is_clean &&\n>  \ttest_must_fail git rev-parse --verify refs/heads/foo &&\n> -\ttest_branch master\n> +\ttest_branch master &&\n> +\tstatus_uno_is_clean\n\nHmm, what's the point of this second one?\n\n>  '\n"},{"id":"348968","messageId":"xmqqsh6787kz.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180531195252.29173-5-avarab@gmail.com","subject":"Re: [PATCH v4 4/9] checkout.[ch]: introduce an *_INIT macro","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-06-01T04:16:28Z","receivedAt":"2018-06-01T04:16:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Add an *_INIT macro for the tracking_name_data similar to what exists\n> elsewhere in the codebase, e.g. OID_ARRAY_INIT in sha1-array.h. This\n> will make it more idiomatic in later changes to add more fields to the\n> struct & its initialization macro.\n\nMakes sense; Thomas's comment on 3/9 still stands at this point, as\nwe have no outside users of the definition of the callback data yet\nat this point.\n\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  checkout.c | 2 +-\n>  checkout.h | 2 ++\n>  2 files changed, 3 insertions(+), 1 deletion(-)\n>\n> diff --git a/checkout.c b/checkout.c\n> index 8d68f75ad1..629fc1d5c4 100644\n> --- a/checkout.c\n> +++ b/checkout.c\n> @@ -25,7 +25,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n>  \n>  const char *unique_tracking_name(const char *name, struct object_id *oid)\n>  {\n> -\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n> +\tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n>  \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n>  \tcb_data.dst_oid = oid;\n>  \tfor_each_remote(check_tracking_name, &cb_data);\n> diff --git a/checkout.h b/checkout.h\n> index 04b52f9ffe..a61ec93e65 100644\n> --- a/checkout.h\n> +++ b/checkout.h\n> @@ -10,6 +10,8 @@ struct tracking_name_data {\n>  \tint unique;\n>  };\n>  \n> +#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n> +\n>  /*\n>   * Check if the branch name uniquely matches a branch name on a remote\n>   * tracking branch.  Return the name of the remote if such a branch\n"},{"id":"348969","messageId":"xmqqin7386tv.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180531195252.29173-9-avarab@gmail.com","subject":"Re: [PATCH v4 8/9] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-06-01T04:32:44Z","receivedAt":"2018-06-01T04:32:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> @@ -1269,6 +1270,16 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>  \tif (opts.patch_mode || opts.pathspec.nr) {\n>  \t\tint ret = checkout_paths(&opts, new_branch_info.name,\n>  \t\t\t\t\t &dwim_remotes_matched);\n> +\t\tif (ret && dwim_remotes_matched > 1 &&\n> +\t\t    advice_checkout_ambiguous_remote_branch_name)\n> +\t\t\tadvise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n> +\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n> +\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n> +\t\t\t\t \"\\n\"\n> +\t\t\t\t \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n> +\t\t\t\t \"instead of <name>?\"),\n> +\t\t\t       argv[0],\n> +\t\t\t       dwim_remotes_matched);\n>  \t\treturn ret;\n\nDo we give \"checkout -p no-such-file\" the above wall of text?\n\nSomehow checkout_paths(), which is \"we were given a tree-ish and\npathspec and told to grab the matching paths out of it and stuff\nthem to the index and the working tree\", is a wrong place to be\ndoing the \"oh, what the caller thought was pathspec may turn out to\nbe a rev, so check that too for such a confused caller\".  Shouldn't\nthe caller be doing all that (which would mean we wan't need to pass\n\"remotes-matched\" to the function, as the helper has nothing to do\nwith deciding which arg is the tree-ish).\n\n"},{"id":"348971","messageId":"xmqqefhr850j.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"xmqqin7386tv.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4 8/9] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-06-01T05:11:56Z","receivedAt":"2018-06-01T05:12:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> @@ -1269,6 +1270,16 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>>  \tif (opts.patch_mode || opts.pathspec.nr) {\n>>  \t\tint ret = checkout_paths(&opts, new_branch_info.name,\n>>  \t\t\t\t\t &dwim_remotes_matched);\n>> +\t\tif (ret && dwim_remotes_matched > 1 &&\n>> +\t\t    advice_checkout_ambiguous_remote_branch_name)\n>> +\t\t\tadvise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n>> +\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n>> +\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n>> +\t\t\t\t \"\\n\"\n>> +\t\t\t\t \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n>> +\t\t\t\t \"instead of <name>?\"),\n>> +\t\t\t       argv[0],\n>> +\t\t\t       dwim_remotes_matched);\n>>  \t\treturn ret;\n>\n> Do we give \"checkout -p no-such-file\" the above wall of text?\n>\n> Somehow checkout_paths(), which is \"we were given a tree-ish and\n> pathspec and told to grab the matching paths out of it and stuff\n> them to the index and the working tree\", is a wrong place to be\n> doing the \"oh, what the caller thought was pathspec may turn out to\n> be a rev, so check that too for such a confused caller\".  Shouldn't\n> the caller be doing all that (which would mean we wan't need to pass\n> \"remotes-matched\" to the function, as the helper has nothing to do\n> with deciding which arg is the tree-ish).\n\nWell, upon closer inspection adding *dwim_remotes_matched parameter\nto checkout_paths() done in an earlier step seems to be totally\nbogus and only serves the purpose of confusing reviewers.  The\nfunction does not touch the pointer in any way---it does not use the\npointer to return its findings, and it does not use an earlier\nfindings to affect its behaviour by dereferencing it.\n\nThe dwim_remotes_matched is set by an earlier call to\nparse_branchname_arg(), which does gain an int* parameter in this\nseries.  And that addition _does_ make sense.  That codepath is\nwhere the \"do we have many remotes that could match, or none, or\nunique?\" determination is made.\n\n"},{"id":"348984","messageId":"CAPig+cQfw8-qJSt=Xbyxy1SUUDQZ-e1cGrG4g0KnO4zd7m+2dw@mail.gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-9-avarab@gmail.com","subject":"Re: [PATCH v4 8/9] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-06-01T07:53:17Z","receivedAt":"2018-06-01T07:53:20Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, May 31, 2018 at 3:52 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> As the \"checkout\" documentation describes:\n>\n>     If <branch> is not found but there does exist a tracking branch in\n>     exactly one remote (call it <remote>) with a matching name, treat\n>     as equivalent to [...] <remote>/<branch.\n>\n> This is a really useful feature, the problem is that when you another\n\ns/, the/. The/\ns/you/& add/\n\n> remote (e.g. a fork) git won't find a unique branch name anymore, and\n> will instead print this nondescript message:\n>\n>     $ git checkout master\n>     error: pathspec 'master' did not match any file(s) known to git\n>\n> Now it will, on my git.git checkout, print:\n>\n>     $ ./git --exec-path=$PWD checkout master\n>     error: pathspec 'master' did not match any file(s) known to git.\n>     hint: The argument 'master' matched more than one remote tracking branch.\n>     hint: We found 26 remotes with a reference that matched. So we fell back\n>     hint: on trying to resolve the argument as a path, but failed there too!\n>     hint:\n>     hint: Perhaps you meant fully qualify the branch name? E.g. origin/<name>\n\ns/meant/& to/\n\n>     hint: instead of <name>?\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> @@ -1269,6 +1270,16 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +               if (ret && dwim_remotes_matched > 1 &&\n> +                   advice_checkout_ambiguous_remote_branch_name)\n> +                       advise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n> +                                \"We found %d remotes with a reference that matched. So we fell back\\n\"\n> +                                \"on trying to resolve the argument as a path, but failed there too!\\n\"\n> +                                \"\\n\"\n> +                                \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n\ns/meant/& to/\n\n> +                                \"instead of <name>?\"),\n"},{"id":"348985","messageId":"CAPig+cSk9Dt3ZLQRjWwpxqMyP3npu3KbEQxkNfjV5RxRtro82Q@mail.gmail.com","threadId":"48399","inReplyTo":"CAGZ79kZKaZq+bqFTwe+Y3FU-1DHPjzNrvMNU=DM10EyOXxZJVw@mail.gmail.com","subject":"Re: [PATCH v4 9/9] checkout & worktree: introduce checkout.defaultRemote","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-06-01T08:04:00Z","receivedAt":"2018-06-01T08:04:08Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, May 31, 2018 at 5:49 PM, Stefan Beller <sbeller@google.com> wrote:\n>> I considered splitting this into checkout.defaultRemote and\n>> worktree.defaultRemote, but it's probably less confusing to break our\n>> own rules that anything shared between config should live in core.*\n>> than have two config settings, and I couldn't come up with a short\n>> name under core.* that made sense (core.defaultRemoteForCheckout?).\n>\n>   core.dwimRemote ? It's a bit cryptic, though.\n\nThis option started out as 'core.dwimRemote' in the very first version\nof this series[1], but someone argued against it for several reasons\nand suggested 'defaultRemote' instead[2].\n\n[1]: https://public-inbox.org/git/20180502105452.17583-1-avarab@gmail.com/\n[2]: https://public-inbox.org/git/CAPig+cTZyYC-1_TxL2PrfOF6HAktUxxM+g5EXcByS5fCDMdCHg@mail.gmail.com/\n"},{"id":"348993","messageId":"874limale3.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"CAGZ79kZKaZq+bqFTwe+Y3FU-1DHPjzNrvMNU=DM10EyOXxZJVw@mail.gmail.com","subject":"Re: [PATCH v4 9/9] checkout & worktree: introduce checkout.defaultRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T09:47:32Z","receivedAt":"2018-06-01T09:47:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, May 31 2018, Stefan Beller wrote:\n\n> Hi Ævar,\n>\n> Sorry for chiming in late. I have a couple of thoughts:\n>\n>>     (\n>>         cd /tmp &&\n>>         rm -rf tbdiff &&\n>>         git clone git@github.com:trast/tbdiff.git &&\n>>         cd tbdiff &&\n>>         git branch -m topic &&\n>>         git checkout master\n>>     )\n>>\n>> That will output:\n>>\n>>     Branch 'master' set up to track remote branch 'master' from 'origin'.\n>>     Switched to a new branch 'master'\n>\n> I thought master is already there after the clone operation and\n> you'd merely switch back to the local branch that was created at\n> clone time?\n>\n>     $ git clone git@github.com:trast/tbdiff.git && cd tbdiff\n>     $ git branch\n>     * master\n>     $ cat .git/config\n> ...\n> [branch \"master\"]\n>     remote = origin\n>     merge = refs/heads/master\n>\n> But the observation is right, we get that message. When do we\n> do the setup for the master branch specifically?\n\nWhat you're missing is this part:\n\n    git branch -m topic\n\nI.e. we clone the repo, and have a \"master\" branch, we then rename\n\"master\" to \"topic\", now there's no local master branch. Then we\ncheckout master either with only one remote or two.\n\n>>\n>> But as soon as a new remote is added (e.g. just to inspect something\n>> from someone else) the DWIMery goes away:\n>>\n>>     (\n>>         cd /tmp &&\n>>         rm -rf tbdiff &&\n>>         git clone git@github.com:trast/tbdiff.git &&\n>>         cd tbdiff &&\n>>         git branch -m topic &&\n>>         git remote add avar git@github.com:avar/tbdiff.git &&\n>>         git fetch avar &&\n>>         git checkout master\n>>     )\n>>\n>> Will output (without the advice output added earlier in this series):\n>>\n>>     error: pathspec 'master' did not match any file(s) known to git.\n>>\n>> The new checkout.defaultRemote config allows me to say that whenever\n>> that ambiguity comes up I'd like to prefer \"origin\", and it'll still\n>> work as though the only remote I had was \"origin\".\n>>\n>> Also adjust the advice.checkoutAmbiguousRemoteBranchName message to\n>> mention this new config setting to the user, the full output on my\n>> git.git is now (the last paragraph is new):\n>>\n>>     $ ./git --exec-path=$PWD checkout master\n>>     error: pathspec 'master' did not match any file(s) known to git.\n>>     hint: The argument 'master' matched more than one remote tracking branch.\n>>     hint: We found 26 remotes with a reference that matched. So we fell back\n>>     hint: on trying to resolve the argument as a path, but failed there too!\n>>     hint:\n>>     hint: Perhaps you meant fully qualify the branch name? E.g. origin/<name>\n>\n> s/meant fully/meant to fully/\n> s/? E.g./?\\nFor example/\n\nThanks, will fix.\n\n>>     hint: instead of <name>?\n>\n> In builtin/submodule--helper.c there is get_default_remote() which also\n> hardcodes \"origin\". I think that is a safe thing to do.\n>\n>>     hint:\n>>     hint: If you'd like to always have checkouts of 'master' prefer one remote,\n>>     hint: e.g. the 'origin' remote, consider setting checkout.defaultRemote=origin\n>>     hint: in your config. See the 'git-config' manual page for details.\n>\n> his new setting elevates one remote over all others, which may\n> be enough for most setups and not confusing, too.\n> Consider the following:\n>\n>     git clone https://kernel.googlesource.com/pub/scm/git/git && cd git\n>     git remote add gitster https://github.com/gitster/git\n>     git remote add interesting-patches https://github.com/avar/git\n>     git remote add my-github https://github.com/stefanbeller/git\n>\n>     git checkout master\n>\n> This probably means I want to have origin/master (from kernel.org)\n>\n>     git checkout ab/checkout-implicit-remote\n>\n> This probably wants to have it from gitster/ (as it is not found on kernel.org);\n> I am not sure if it would want to look at interesting-patches/ that mirrors\n> github, but probably if it were not to be found at gitster.\n>\n> So maybe we rather want a setup to give a defined priority for\n> the search order:\n>\n>   git config dwim.remoteSearchOrder origin gitster avar\n>\n> Stepping back a bit, there is already an order in the config file\n> for the remotes, and that order is used for example for 'fetch --all'.\n>\n> I wonder if we want to take that order? (Or are the days of hand\n> editing the config over and this is too arcane? We would need a\n> config command to re order remotes). Then we could just have a\n> boolean switch to use the config order on ambiguity.\n> Although you might want to have a different order for fetching\n> and looking for the right checkout.\n\nI thought about this use-case, and if we want this in the future I think\nthe most straightforward way is not to invent some new search order\nvariable, but just make use of git config allowing multi-values, i.e.:\n\n    [checkout]\n        defaultRemote = origin\n        defaultRemote = gitster\n\nAlthough I'm not interested in implementing that now, and unlike just\nhaving one special remote I don't think it's of interest to the vast\nmajority of git users.\n\n>> I considered splitting this into checkout.defaultRemote and\n>> worktree.defaultRemote, but it's probably less confusing to break our\n>> own rules that anything shared between config should live in core.*\n>> than have two config settings, and I couldn't come up with a short\n>> name under core.* that made sense (core.defaultRemoteForCheckout?).\n>\n>   core.dwimRemote ? It's a bit cryptic, though.\n\nCovered by Eric's reply in\n<CAPig+cSk9Dt3ZLQRjWwpxqMyP3npu3KbEQxkNfjV5RxRtro82Q@mail.gmail.com>\n\n>> See also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\n>> frotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\n>> to begin with, and 4e85333197 (\"worktree: make add <path> <branch>\n>> dwim\", 2017-11-26) which added it to git-worktree.\n"},{"id":"348994","messageId":"8736y6al9l.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"xmqqin7386tv.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4 8/9] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T09:50:14Z","receivedAt":"2018-06-01T09:50:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jun 01 2018, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> @@ -1269,6 +1270,16 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>>  \tif (opts.patch_mode || opts.pathspec.nr) {\n>>  \t\tint ret = checkout_paths(&opts, new_branch_info.name,\n>>  \t\t\t\t\t &dwim_remotes_matched);\n>> +\t\tif (ret && dwim_remotes_matched > 1 &&\n>> +\t\t    advice_checkout_ambiguous_remote_branch_name)\n>> +\t\t\tadvise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n>> +\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n>> +\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n>> +\t\t\t\t \"\\n\"\n>> +\t\t\t\t \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n>> +\t\t\t\t \"instead of <name>?\"),\n>> +\t\t\t       argv[0],\n>> +\t\t\t       dwim_remotes_matched);\n>>  \t\treturn ret;\n>\n> Do we give \"checkout -p no-such-file\" the above wall of text?\n\nNo:\n\n    $ ./git --exec-path=$PWD checkout -p master\n    No changes.\n    $ ./git --exec-path=$PWD checkout master\n    error: pathspec 'master' did not match any file(s) known to git.\n    hint: The argument 'master' matched more than one remote tracking branch.\n    [...]\n\n> Somehow checkout_paths(), which is \"we were given a tree-ish and\n> pathspec and told to grab the matching paths out of it and stuff\n> them to the index and the working tree\", is a wrong place to be\n> doing the \"oh, what the caller thought was pathspec may turn out to\n> be a rev, so check that too for such a confused caller\".  Shouldn't\n> the caller be doing all that (which would mean we wan't need to pass\n> \"remotes-matched\" to the function, as the helper has nothing to do\n> with deciding which arg is the tree-ish).\n\nI skimmed the rest of this thread and saw there were more specific\nsuggestions. I'm open to moving this somewhere else. The \"what is this\nand what do we do\" logic in checkout.c is spread over multiple\nfunctions, and this seemed the most straightforward way to add this.\n"},{"id":"348995","messageId":"871sdqal2g.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"xmqqefhr850j.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4 8/9] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T09:54:31Z","receivedAt":"2018-06-01T09:54:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jun 01 2018, Junio C Hamano wrote:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>\n>>> @@ -1269,6 +1270,16 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>>>  \tif (opts.patch_mode || opts.pathspec.nr) {\n>>>  \t\tint ret = checkout_paths(&opts, new_branch_info.name,\n>>>  \t\t\t\t\t &dwim_remotes_matched);\n>>> +\t\tif (ret && dwim_remotes_matched > 1 &&\n>>> +\t\t    advice_checkout_ambiguous_remote_branch_name)\n>>> +\t\t\tadvise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n>>> +\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n>>> +\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n>>> +\t\t\t\t \"\\n\"\n>>> +\t\t\t\t \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n>>> +\t\t\t\t \"instead of <name>?\"),\n>>> +\t\t\t       argv[0],\n>>> +\t\t\t       dwim_remotes_matched);\n>>>  \t\treturn ret;\n>>\n>> Do we give \"checkout -p no-such-file\" the above wall of text?\n>>\n>> Somehow checkout_paths(), which is \"we were given a tree-ish and\n>> pathspec and told to grab the matching paths out of it and stuff\n>> them to the index and the working tree\", is a wrong place to be\n>> doing the \"oh, what the caller thought was pathspec may turn out to\n>> be a rev, so check that too for such a confused caller\".  Shouldn't\n>> the caller be doing all that (which would mean we wan't need to pass\n>> \"remotes-matched\" to the function, as the helper has nothing to do\n>> with deciding which arg is the tree-ish).\n>\n> Well, upon closer inspection adding *dwim_remotes_matched parameter\n> to checkout_paths() done in an earlier step seems to be totally\n> bogus and only serves the purpose of confusing reviewers.  The\n> function does not touch the pointer in any way---it does not use the\n> pointer to return its findings, and it does not use an earlier\n> findings to affect its behaviour by dereferencing it.\n\nYes, sorry. I'll fix that. This was a relic from an earlier version of\nthis that never escaped into the wild where I first tried printing out\nthis error printing it out near the report_path_error() codepath called\nfrom checkout_paths().\n\nI.e. I was trying to avoid printing out the \"error: pathspec 'master'\ndid not match any file(s) known to git.\" error altogether. That's still\narguably a good direction, since we *know* \"master\" would have otherwise\nmatched a remote branch, so that's probably a more informative message\nthan falling back to checking out pathspecs and failing, and complaining\nabout there being no such pathspec.\n\nBut it was a pain to handle the various edge cases, e.g.:\n\n    $ ./git --exec-path=$PWD checkout x y z\n    error: pathspec 'x' did not match any file(s) known to git.\n    error: pathspec 'y' did not match any file(s) known to git.\n    error: pathspec 'z' did not match any file(s) known to git.\n\nSo I decided just to let checkout_paths() to its thing and then print\nout an error about dwim branches if applicable if it failed.\n\n> The dwim_remotes_matched is set by an earlier call to\n> parse_branchname_arg(), which does gain an int* parameter in this\n> series.  And that addition _does_ make sense.  That codepath is\n> where the \"do we have many remotes that could match, or none, or\n> unique?\" determination is made.\n\n*nod*\n"},{"id":"348996","messageId":"87zi0e96dw.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"xmqqpo1b9rs8.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4 3/9] checkout.[ch]: move struct declaration into *.h","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T09:56:59Z","receivedAt":"2018-06-01T09:57:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jun 01 2018, Junio C Hamano wrote:\n\n> Thomas Gummerer <t.gummerer@gmail.com> writes:\n>\n>> We seem to have plenty of structs defined in '.c' files, if they are\n>> only needed there.  Its use also seems to be single purpose for the\n>> callback data, so I'm a bit puzzled how having this in a header file\n>> instead of the .c file would be helpful?\n>>\n>> I feel like having only the \"public\" part in the header file also\n>> helps developers that are just looking for documentation of the\n>> functions they are looking at, by having less things to go through,\n>> that they wouldn't necessarily care about.\n>\n> Yup, sounds like a sensible criterion to choose between <*.h> and\n> <*.c>.\n\nYes this makes sense. Thanks both. I misunderstood the idiom. Makes\nsense in hindsight (& with both of your advices) only to put structs in\nthe *.h if it's actually used outside of that API. Will move this around\nin v5.\n"},{"id":"349013","messageId":"87y3fy8f8p.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"xmqqwovj882f.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v4 1/9] checkout tests: index should be clean after dwim checkout","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T19:43:18Z","receivedAt":"2018-06-01T19:43:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jun 01 2018, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Assert that whenever there's a DWIM checkout that the index should be\n>> clean afterwards, in addition to the correct branch being checked-out.\n>> ...\n>> So let's amend the tests (mostly added in) 399e4a1c56 (\"t2024: Add\n>> tests verifying current DWIM behavior of 'git checkout <branch>'\",\n>> 2013-04-21) to always assert that \"status\" is clean after we run\n>> \"checkout\", that's being done with \"-uno\" because there's going to be\n>> some untracked files related to the test itself which we don't care\n>> about.\n>\n> It might not be absolutely necessary to state, but it would be\n> helpful to say that you are assuming to start a checkout (DWIM or\n> otherwise) from a clean state; without the assumption, the readers\n> need to think for a few breaths why \"the index should be clean\" is\n> true.\n>\n> The intention and the implementation of the change both mostly look\n> good to me from a quick read.\n\nMakes sense, will fix.\n\n>>  test_expect_success 'setup' '\n>>  \ttest_commit my_master &&\n>>  \tgit init repo_a &&\n>> @@ -55,6 +61,7 @@ test_expect_success 'checkout of non-existing branch fails' '\n>>  \ttest_might_fail git branch -D xyzzy &&\n>>\n>>  \ttest_must_fail git checkout xyzzy &&\n>> +\tstatus_uno_is_clean &&\n>>  \ttest_must_fail git rev-parse --verify refs/heads/xyzzy &&\n>>  \ttest_branch master\n>>  '\n>> @@ -64,8 +71,10 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n>>  \ttest_might_fail git branch -D foo &&\n>>\n>>  \ttest_must_fail git checkout foo &&\n>> +\tstatus_uno_is_clean &&\n>>  \ttest_must_fail git rev-parse --verify refs/heads/foo &&\n>> -\ttest_branch master\n>> +\ttest_branch master &&\n>> +\tstatus_uno_is_clean\n>\n> Hmm, what's the point of this second one?\n>\n>>  '\n\nSlipped in, will remove. Thanks.\n"},{"id":"349016","messageId":"87wovi8ehj.fsf@evledraar.gmail.com","threadId":"48399","inReplyTo":"CAPig+cQfw8-qJSt=Xbyxy1SUUDQZ-e1cGrG4g0KnO4zd7m+2dw@mail.gmail.com","subject":"Re: [PATCH v4 8/9] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T19:59:36Z","receivedAt":"2018-06-01T19:59:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jun 01 2018, Eric Sunshine wrote:\n\n> On Thu, May 31, 2018 at 3:52 PM, Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n>> As the \"checkout\" documentation describes:\n>>\n>>     If <branch> is not found but there does exist a tracking branch in\n>>     exactly one remote (call it <remote>) with a matching name, treat\n>>     as equivalent to [...] <remote>/<branch.\n>>\n>> This is a really useful feature, the problem is that when you another\n>\n> s/, the/. The/\n> s/you/& add/\n\nThanks!\n\n>> remote (e.g. a fork) git won't find a unique branch name anymore, and\n>> will instead print this nondescript message:\n>>\n>>     $ git checkout master\n>>     error: pathspec 'master' did not match any file(s) known to git\n>>\n>> Now it will, on my git.git checkout, print:\n>>\n>>     $ ./git --exec-path=$PWD checkout master\n>>     error: pathspec 'master' did not match any file(s) known to git.\n>>     hint: The argument 'master' matched more than one remote tracking branch.\n>>     hint: We found 26 remotes with a reference that matched. So we fell back\n>>     hint: on trying to resolve the argument as a path, but failed there too!\n>>     hint:\n>>     hint: Perhaps you meant fully qualify the branch name? E.g. origin/<name>\n>\n> s/meant/& to/\n>\n>>     hint: instead of <name>?\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>> diff --git a/builtin/checkout.c b/builtin/checkout.c\n>> @@ -1269,6 +1270,16 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n>> +               if (ret && dwim_remotes_matched > 1 &&\n>> +                   advice_checkout_ambiguous_remote_branch_name)\n>> +                       advise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n>> +                                \"We found %d remotes with a reference that matched. So we fell back\\n\"\n>> +                                \"on trying to resolve the argument as a path, but failed there too!\\n\"\n>> +                                \"\\n\"\n>> +                                \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n>\n> s/meant/& to/\n>\n>> +                                \"instead of <name>?\"),\n\nWill rephrase to make this less confusing.\n"},{"id":"349024","messageId":"20180601211015.11919-1-avarab@gmail.com","threadId":"48399","inReplyTo":"20180531195252.29173-1-avarab@gmail.com","subject":"[PATCH v5 0/8] ambiguous checkout UI & checkout.defaultRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T21:10:07Z","receivedAt":"2018-06-01T21:10:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This v5 should address all the comments to v4. Thanks all! It's one\npatch less because the struct isn't being moved around anymore.\n\ntbdiff:\n    \n    1: 16d656ee3b ! 1: ab4529d9f5 checkout tests: index should be clean after dwim checkout\n        @@ -29,6 +29,10 @@\n             \"checkout\", that's being done with \"-uno\" because there's going to be\n             some untracked files related to the test itself which we don't care\n             about.\n        +    \n        +    In all these tests (DWIM or otherwise) we start with a clean index, so\n        +    these tests are asserting that that's still the case after the\n        +    \"checkout\", failed or otherwise.\n             \n             Then if we ever run into this sort of regression, either in the\n             existing code or with a new feature, we'll know.\n        @@ -65,12 +69,8 @@\n          \ttest_must_fail git checkout foo &&\n         +\tstatus_uno_is_clean &&\n          \ttest_must_fail git rev-parse --verify refs/heads/foo &&\n        --\ttest_branch master\n        -+\ttest_branch master &&\n        -+\tstatus_uno_is_clean\n        - '\n        - \n        - test_expect_success 'checkout of branch from a single remote succeeds #1' '\n        + \ttest_branch master\n        + '\n         @@\n          \ttest_might_fail git branch -D bar &&\n          \n    2: 159cc0634b = 2: c8bbece403 checkout.h: wrap the arguments to unique_tracking_name()\n    3: 3df4594e2d < -:  ------- checkout.[ch]: move struct declaration into *.h\n    4: 35c6481208 < -:  ------- checkout.[ch]: introduce an *_INIT macro\n    -:  ------- > 3: 4fc5ab27fa checkout.[ch]: introduce an *_INIT macro\n    5: 69a834f010 ! 4: fbce6df584 checkout.[ch]: change \"unique\" member to \"num_matches\"\n        @@ -11,6 +11,19 @@\n         diff --git a/checkout.c b/checkout.c\n         --- a/checkout.c\n         +++ b/checkout.c\n        +@@\n        + \t/* const */ char *src_ref;\n        + \tchar *dst_ref;\n        + \tstruct object_id *dst_oid;\n        +-\tint unique;\n        ++\tint num_matches;\n        + };\n        + \n        +-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n        ++#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n        + \n        + static int check_tracking_name(struct remote *remote, void *cb_data)\n        + {\n         @@\n          \t\tfree(query.dst);\n          \t\treturn 0;\n        @@ -31,20 +44,3 @@\n          \t\treturn cb_data.dst_ref;\n          \tfree(cb_data.dst_ref);\n          \treturn NULL;\n        -\n        -diff --git a/checkout.h b/checkout.h\n        ---- a/checkout.h\n        -+++ b/checkout.h\n        -@@\n        - \t/* const */ char *src_ref;\n        - \tchar *dst_ref;\n        - \tstruct object_id *dst_oid;\n        --\tint unique;\n        -+\tint num_matches;\n        - };\n        - \n        --#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n        -+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n        - \n        - /*\n        -  * Check if the branch name uniquely matches a branch name on a remote\n    6: 13547824dc ! 5: 6e016d43d7 checkout: pass the \"num_matches\" up to callers\n        @@ -11,16 +11,6 @@\n         diff --git a/builtin/checkout.c b/builtin/checkout.c\n         --- a/builtin/checkout.c\n         +++ b/builtin/checkout.c\n        -@@\n        - }\n        - \n        - static int checkout_paths(const struct checkout_opts *opts,\n        --\t\t\t  const char *revision)\n        -+\t\t\t  const char *revision,\n        -+\t\t\t  int *dwim_remotes_matched)\n        - {\n        - \tint pos;\n        - \tstruct checkout state = CHECKOUT_INIT;\n         @@\n          \t\t\t\tint dwim_new_local_branch_ok,\n          \t\t\t\tstruct branch_info *new_branch_info,\n        @@ -59,16 +49,6 @@\n          \t\targv += n;\n          \t\targc -= n;\n          \t}\n        -@@\n        - \n        - \tUNLEAK(opts);\n        - \tif (opts.patch_mode || opts.pathspec.nr)\n        --\t\treturn checkout_paths(&opts, new_branch_info.name);\n        -+\t\treturn checkout_paths(&opts, new_branch_info.name,\n        -+\t\t\t\t      &dwim_remotes_matched);\n        - \telse\n        - \t\treturn checkout_branch(&opts, &new_branch_info);\n        - }\n         \n         diff --git a/builtin/worktree.c b/builtin/worktree.c\n         --- a/builtin/worktree.c\n    7: 6895b5c903 ! 6: 07b11b133d builtin/checkout.c: use \"ret\" variable for return\n        @@ -16,12 +16,10 @@\n          \n          \tUNLEAK(opts);\n         -\tif (opts.patch_mode || opts.pathspec.nr)\n        --\t\treturn checkout_paths(&opts, new_branch_info.name,\n        --\t\t\t\t      &dwim_remotes_matched);\n        +-\t\treturn checkout_paths(&opts, new_branch_info.name);\n         -\telse\n         +\tif (opts.patch_mode || opts.pathspec.nr) {\n        -+\t\tint ret = checkout_paths(&opts, new_branch_info.name,\n        -+\t\t\t\t\t &dwim_remotes_matched);\n        ++\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n         +\t\treturn ret;\n         +\t} else {\n          \t\treturn checkout_branch(&opts, &new_branch_info);\n    8: 5cfc0896e5 ! 7: 97e84f6e1c checkout: add advice for ambiguous \"checkout <branch>\"\n        @@ -8,9 +8,9 @@\n                 exactly one remote (call it <remote>) with a matching name, treat\n                 as equivalent to [...] <remote>/<branch.\n             \n        -    This is a really useful feature, the problem is that when you another\n        -    remote (e.g. a fork) git won't find a unique branch name anymore, and\n        -    will instead print this nondescript message:\n        +    This is a really useful feature. The problem is that when you and\n        +    another remote (e.g. a fork) git won't find a unique branch name\n        +    anymore, and will instead print this nondescript message:\n             \n                 $ git checkout master\n                 error: pathspec 'master' did not match any file(s) known to git\n        @@ -23,8 +23,10 @@\n                 hint: We found 26 remotes with a reference that matched. So we fell back\n                 hint: on trying to resolve the argument as a path, but failed there too!\n                 hint:\n        -        hint: Perhaps you meant fully qualify the branch name? E.g. origin/<name>\n        -        hint: instead of <name>?\n        +        hint: If you meant to check out a remote tracking branch on e.g. 'origin'\n        +        hint: you can do so by fully-qualifying the name with the --track option:\n        +        hint:\n        +        hint:     git checkout --track origin/<name>\n             \n             Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         \n        @@ -90,17 +92,19 @@\n          static const char * const checkout_usage[] = {\n          \tN_(\"git checkout [<options>] <branch>\"),\n         @@\n        + \tUNLEAK(opts);\n          \tif (opts.patch_mode || opts.pathspec.nr) {\n        - \t\tint ret = checkout_paths(&opts, new_branch_info.name,\n        - \t\t\t\t\t &dwim_remotes_matched);\n        + \t\tint ret = checkout_paths(&opts, new_branch_info.name);\n         +\t\tif (ret && dwim_remotes_matched > 1 &&\n         +\t\t    advice_checkout_ambiguous_remote_branch_name)\n         +\t\t\tadvise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n         +\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n         +\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n         +\t\t\t\t \"\\n\"\n        -+\t\t\t\t \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n        -+\t\t\t\t \"instead of <name>?\"),\n        ++\t\t\t\t \"If you meant to check out a remote tracking branch on e.g. 'origin'\\n\"\n        ++\t\t\t\t \"you can do so by fully-qualifying the name with the --track option:\\n\"\n        ++\t\t\t\t \"\\n\"\n        ++\t\t\t\t \"    git checkout --track origin/<name>\"),\n         +\t\t\t       argv[0],\n         +\t\t\t       dwim_remotes_matched);\n          \t\treturn ret;\n        @@ -111,7 +115,7 @@\n         --- a/t/t2024-checkout-dwim.sh\n         +++ b/t/t2024-checkout-dwim.sh\n         @@\n        - \tstatus_uno_is_clean\n        + \ttest_branch master\n          '\n          \n         +test_expect_success 'checkout of branch from multiple remotes fails with advice' '\n    9: fad1df1edd ! 8: a5cc070ebf checkout & worktree: introduce checkout.defaultRemote\n        @@ -57,12 +57,14 @@\n                 hint: We found 26 remotes with a reference that matched. So we fell back\n                 hint: on trying to resolve the argument as a path, but failed there too!\n                 hint:\n        -        hint: Perhaps you meant fully qualify the branch name? E.g. origin/<name>\n        -        hint: instead of <name>?\n        +        hint: If you meant to check out a remote tracking branch on e.g. 'origin'\n        +        hint: you can do so by fully-qualifying the name with the --track option:\n                 hint:\n        -        hint: If you'd like to always have checkouts of 'master' prefer one remote,\n        -        hint: e.g. the 'origin' remote, consider setting checkout.defaultRemote=origin\n        -        hint: in your config. See the 'git-config' manual page for details.\n        +        hint:     git checkout --track origin/<name>\n        +        hint:\n        +        hint: If you'd like to always have checkouts of an ambiguous <name> prefer\n        +        hint: one remote, e.g. the 'origin' remote, consider setting\n        +        hint: checkout.defaultRemote=origin in your config.\n             \n             I considered splitting this into checkout.defaultRemote and\n             worktree.defaultRemote, but it's probably less confusing to break our\n        @@ -128,7 +130,7 @@\n         +one for the purposes of disambiguation, even if the `<branch>` isn't\n         +unique across all remotes. Set it to\n         +e.g. `checkout.defaultRemote=origin` to always checkout remote\n        -+branches from there if `<branch> is ambiguous but exists on the\n        ++branches from there if `<branch>` is ambiguous but exists on the\n         +'origin' remote. See also `checkout.defaultRemote` in\n         +linkgit:git-config[1].\n         ++\n        @@ -148,7 +150,7 @@\n         +one for the purposes of disambiguation, even if the `<branch>` isn't\n         +unique across all remotes. Set it to\n         +e.g. `checkout.defaultRemote=origin` to always checkout remote\n        -+branches from there if `<branch> is ambiguous but exists on the\n        ++branches from there if `<branch>` is ambiguous but exists on the\n         +'origin' remote. See also `checkout.defaultRemote` in\n         +linkgit:git-config[1].\n         ++\n        @@ -173,22 +175,18 @@\n          \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\n          \t *\n         @@\n        - \t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n        + \t\t\t\t \"If you meant to check out a remote tracking branch on e.g. 'origin'\\n\"\n        + \t\t\t\t \"you can do so by fully-qualifying the name with the --track option:\\n\"\n          \t\t\t\t \"\\n\"\n        - \t\t\t\t \"Perhaps you meant fully qualify the branch name? E.g. origin/<name>\\n\"\n        --\t\t\t\t \"instead of <name>?\"),\n        -+\t\t\t\t \"instead of <name>?\\n\"\n        +-\t\t\t\t \"    git checkout --track origin/<name>\"),\n        ++\t\t\t\t \"    git checkout --track origin/<name>\\n\"\n         +\t\t\t\t \"\\n\"\n        -+\t\t\t\t \"If you'd like to always have checkouts of '%s' prefer one remote,\\n\"\n        -+\t\t\t\t \"e.g. the 'origin' remote, consider setting checkout.defaultRemote=origin\\n\"\n        -+\t\t\t\t \"in your config. See the 'git-config' manual page for details.\"),\n        ++\t\t\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n        ++\t\t\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n        ++\t\t\t\t \"checkout.defaultRemote=origin in your config.\"),\n          \t\t\t       argv[0],\n        --\t\t\t       dwim_remotes_matched);\n        -+\t\t\t       dwim_remotes_matched,\n        -+\t\t\t       argv[0]);\n        + \t\t\t       dwim_remotes_matched);\n          \t\treturn ret;\n        - \t} else {\n        - \t\treturn checkout_branch(&opts, &new_branch_info);\n         \n         diff --git a/checkout.c b/checkout.c\n         --- a/checkout.c\n        @@ -198,6 +196,19 @@\n          #include \"refspec.h\"\n          #include \"checkout.h\"\n         +#include \"config.h\"\n        + \n        + struct tracking_name_data {\n        + \t/* const */ char *src_ref;\n        + \tchar *dst_ref;\n        + \tstruct object_id *dst_oid;\n        + \tint num_matches;\n        ++\tconst char *default_remote;\n        ++\tchar *default_dst_ref;\n        ++\tstruct object_id *default_dst_oid;\n        + };\n        + \n        +-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n        ++#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0, NULL, NULL, NULL }\n          \n          static int check_tracking_name(struct remote *remote, void *cb_data)\n          {\n        @@ -243,31 +254,21 @@\n          \treturn NULL;\n          }\n         \n        -diff --git a/checkout.h b/checkout.h\n        ---- a/checkout.h\n        -+++ b/checkout.h\n        -@@\n        - \tchar *dst_ref;\n        - \tstruct object_id *dst_oid;\n        - \tint num_matches;\n        -+\tconst char *default_remote;\n        -+\tchar *default_dst_ref;\n        -+\tstruct object_id *default_dst_oid;\n        - };\n        - \n        --#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n        -+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0, NULL, NULL, NULL }\n        - \n        - /*\n        -  * Check if the branch name uniquely matches a branch name on a remote\n        -\n         diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\n         --- a/t/t2024-checkout-dwim.sh\n         +++ b/t/t2024-checkout-dwim.sh\n         @@\n        - \ttest_i18ngrep ! \"^hint: \" stderr\n        - '\n        - \n        + \t\tcheckout foo 2>stderr &&\n        + \ttest_branch master &&\n        + \tstatus_uno_is_clean &&\n        +-\ttest_i18ngrep ! \"^hint: \" stderr\n        ++\ttest_i18ngrep ! \"^hint: \" stderr &&\n        ++\t# Make sure the likes of checkout -p don not print this hint\n        ++\tgit checkout -p foo 2>stderr &&\n        ++\ttest_i18ngrep ! \"^hint: \" stderr &&\n        ++\tstatus_uno_is_clean\n        ++'\n        ++\n         +test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.defaultRemote #1' '\n         +\tgit checkout -B master &&\n         +\tstatus_uno_is_clean &&\n        @@ -278,11 +279,9 @@\n         +\ttest_branch foo &&\n         +\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n         +\ttest_branch_upstream foo repo_a foo\n        -+'\n        -+\n        + '\n        + \n          test_expect_success 'checkout of branch from a single remote succeeds #1' '\n        - \tgit checkout -B master &&\n        - \ttest_might_fail git branch -D bar &&\n         \n         diff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\n         --- a/t/t2025-worktree-add.sh\n\nÆvar Arnfjörð Bjarmason (8):\n  checkout tests: index should be clean after dwim checkout\n  checkout.h: wrap the arguments to unique_tracking_name()\n  checkout.[ch]: introduce an *_INIT macro\n  checkout.[ch]: change \"unique\" member to \"num_matches\"\n  checkout: pass the \"num_matches\" up to callers\n  builtin/checkout.c: use \"ret\" variable for return\n  checkout: add advice for ambiguous \"checkout <branch>\"\n  checkout & worktree: introduce checkout.defaultRemote\n\n Documentation/config.txt       | 26 +++++++++++++++\n Documentation/git-checkout.txt |  9 ++++++\n Documentation/git-worktree.txt |  9 ++++++\n advice.c                       |  2 ++\n advice.h                       |  1 +\n builtin/checkout.c             | 41 ++++++++++++++++++-----\n builtin/worktree.c             |  4 +--\n checkout.c                     | 37 ++++++++++++++++++---\n checkout.h                     |  4 ++-\n t/t2024-checkout-dwim.sh       | 59 ++++++++++++++++++++++++++++++++++\n t/t2025-worktree-add.sh        | 21 ++++++++++++\n 11 files changed, 197 insertions(+), 16 deletions(-)\n\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349025","messageId":"20180601211015.11919-2-avarab@gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-1-avarab@gmail.com","subject":"[PATCH v5 1/8] checkout tests: index should be clean after dwim checkout","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T21:10:08Z","receivedAt":"2018-06-01T21:10:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Assert that whenever there's a DWIM checkout that the index should be\nclean afterwards, in addition to the correct branch being checked-out.\n\nThe way the DWIM checkout code in checkout.[ch] works is by looping\nover all remotes, and for each remote trying to find if a given\nreference name only exists on that remote, or if it exists anywhere\nelse.\n\nThis is done by starting out with a `unique = 1` tracking variable in\na struct shared by the entire loop, which will get set to `0` if the\ndata reference is not unique.\n\nThus if we find a match we know the dst_oid member of\ntracking_name_data must be correct, since it's associated with the\nonly reference on the only remote that could have matched our query.\n\nBut if there was ever a mismatch there for some reason we might end up\nwith the correct branch checked out, but at the wrong oid, which would\nshow whatever the difference between the two staged in the\nindex (checkout branch A, stage changes from the state of branch B).\n\nSo let's amend the tests (mostly added in) 399e4a1c56 (\"t2024: Add\ntests verifying current DWIM behavior of 'git checkout <branch>'\",\n2013-04-21) to always assert that \"status\" is clean after we run\n\"checkout\", that's being done with \"-uno\" because there's going to be\nsome untracked files related to the test itself which we don't care\nabout.\n\nIn all these tests (DWIM or otherwise) we start with a clean index, so\nthese tests are asserting that that's still the case after the\n\"checkout\", failed or otherwise.\n\nThen if we ever run into this sort of regression, either in the\nexisting code or with a new feature, we'll know.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t2024-checkout-dwim.sh | 29 +++++++++++++++++++++++++++++\n 1 file changed, 29 insertions(+)\n\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 3e5ac81bd2..ed32828105 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -23,6 +23,12 @@ test_branch_upstream () {\n \ttest_cmp expect.upstream actual.upstream\n }\n \n+status_uno_is_clean() {\n+\t>status.expect &&\n+\tgit status -uno --porcelain >status.actual &&\n+\ttest_cmp status.expect status.actual\n+}\n+\n test_expect_success 'setup' '\n \ttest_commit my_master &&\n \tgit init repo_a &&\n@@ -55,6 +61,7 @@ test_expect_success 'checkout of non-existing branch fails' '\n \ttest_might_fail git branch -D xyzzy &&\n \n \ttest_must_fail git checkout xyzzy &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/xyzzy &&\n \ttest_branch master\n '\n@@ -64,6 +71,7 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_might_fail git branch -D foo &&\n \n \ttest_must_fail git checkout foo &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/foo &&\n \ttest_branch master\n '\n@@ -73,6 +81,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \ttest_might_fail git branch -D bar &&\n \n \tgit checkout bar &&\n+\tstatus_uno_is_clean &&\n \ttest_branch bar &&\n \ttest_cmp_rev remotes/repo_a/bar HEAD &&\n \ttest_branch_upstream bar repo_a bar\n@@ -83,6 +92,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '\n \ttest_might_fail git branch -D baz &&\n \n \tgit checkout baz &&\n+\tstatus_uno_is_clean &&\n \ttest_branch baz &&\n \ttest_cmp_rev remotes/other_b/baz HEAD &&\n \ttest_branch_upstream baz repo_b baz\n@@ -90,6 +100,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '\n \n test_expect_success '--no-guess suppresses branch auto-vivification' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D bar &&\n \n \ttest_must_fail git checkout --no-guess bar &&\n@@ -99,6 +110,7 @@ test_expect_success '--no-guess suppresses branch auto-vivification' '\n \n test_expect_success 'setup more remotes with unconventional refspecs' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \tgit init repo_c &&\n \t(\n \t\tcd repo_c &&\n@@ -128,27 +140,33 @@ test_expect_success 'setup more remotes with unconventional refspecs' '\n \n test_expect_success 'checkout of branch from multiple remotes fails #2' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D bar &&\n \n \ttest_must_fail git checkout bar &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/bar &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch from multiple remotes fails #3' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D baz &&\n \n \ttest_must_fail git checkout baz &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/baz &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch from a single remote succeeds #3' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \tgit checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_branch spam &&\n \ttest_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&\n \ttest_branch_upstream spam repo_c spam\n@@ -156,9 +174,11 @@ test_expect_success 'checkout of branch from a single remote succeeds #3' '\n \n test_expect_success 'checkout of branch from a single remote succeeds #4' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D eggs &&\n \n \tgit checkout eggs &&\n+\tstatus_uno_is_clean &&\n \ttest_branch eggs &&\n \ttest_cmp_rev refs/repo_d/eggs HEAD &&\n \ttest_branch_upstream eggs repo_d eggs\n@@ -166,32 +186,38 @@ test_expect_success 'checkout of branch from a single remote succeeds #4' '\n \n test_expect_success 'checkout of branch with a file having the same name fails' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \ttest_must_fail git checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/spam &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch with a file in subdir having the same name fails' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \tmkdir sub &&\n \tmv spam sub/spam &&\n \ttest_must_fail git -C sub checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/spam &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \tgit checkout spam -- &&\n+\tstatus_uno_is_clean &&\n \ttest_branch spam &&\n \ttest_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&\n \ttest_branch_upstream spam repo_c spam\n@@ -200,6 +226,7 @@ test_expect_success 'checkout <branch> -- succeeds, even if a file with the same\n test_expect_success 'loosely defined local base branch is reported correctly' '\n \n \tgit checkout master &&\n+\tstatus_uno_is_clean &&\n \tgit branch strict &&\n \tgit branch loose &&\n \tgit commit --allow-empty -m \"a bit more\" &&\n@@ -210,7 +237,9 @@ test_expect_success 'loosely defined local base branch is reported correctly' '\n \ttest_config branch.loose.merge master &&\n \n \tgit checkout strict | sed -e \"s/strict/BRANCHNAME/g\" >expect &&\n+\tstatus_uno_is_clean &&\n \tgit checkout loose | sed -e \"s/loose/BRANCHNAME/g\" >actual &&\n+\tstatus_uno_is_clean &&\n \n \ttest_cmp expect actual\n '\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349026","messageId":"20180601211015.11919-3-avarab@gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-1-avarab@gmail.com","subject":"[PATCH v5 2/8] checkout.h: wrap the arguments to unique_tracking_name()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T21:10:09Z","receivedAt":"2018-06-01T21:10:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The line was too long already, and will be longer still when a later\nchange adds another argument.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.h | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/checkout.h b/checkout.h\nindex 9980711179..4cd4cd1c23 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -8,6 +8,7 @@\n  * tracking branch.  Return the name of the remote if such a branch\n  * exists, NULL otherwise.\n  */\n-extern const char *unique_tracking_name(const char *name, struct object_id *oid);\n+extern const char *unique_tracking_name(const char *name,\n+\t\t\t\t\tstruct object_id *oid);\n \n #endif /* CHECKOUT_H */\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349027","messageId":"20180601211015.11919-4-avarab@gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-1-avarab@gmail.com","subject":"[PATCH v5 3/8] checkout.[ch]: introduce an *_INIT macro","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T21:10:10Z","receivedAt":"2018-06-01T21:10:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add an *_INIT macro for the tracking_name_data similar to what exists\nelsewhere in the codebase, e.g. OID_ARRAY_INIT in sha1-array.h. This\nwill make it more idiomatic in later changes to add more fields to the\nstruct & its initialization macro.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/checkout.c b/checkout.c\nindex bdefc888ba..80e430cda8 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -10,6 +10,8 @@ struct tracking_name_data {\n \tint unique;\n };\n \n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n+\n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n \tstruct tracking_name_data *cb = cb_data;\n@@ -32,7 +34,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \n const char *unique_tracking_name(const char *name, struct object_id *oid)\n {\n-\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n+\tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349028","messageId":"20180601211015.11919-5-avarab@gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-1-avarab@gmail.com","subject":"[PATCH v5 4/8] checkout.[ch]: change \"unique\" member to \"num_matches\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T21:10:11Z","receivedAt":"2018-06-01T21:10:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Internally track how many matches we find in the check_tracking_name()\ncallback. Nothing uses this now, but it will be made use of in a later\nchange.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/checkout.c b/checkout.c\nindex 80e430cda8..7662a39a62 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -7,10 +7,10 @@ struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n-\tint unique;\n+\tint num_matches;\n };\n \n-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n@@ -23,9 +23,9 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\tfree(query.dst);\n \t\treturn 0;\n \t}\n+\tcb->num_matches++;\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n-\t\tcb->unique = 0;\n \t\treturn 0;\n \t}\n \tcb->dst_ref = query.dst;\n@@ -39,7 +39,7 @@ const char *unique_tracking_name(const char *name, struct object_id *oid)\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tfree(cb_data.src_ref);\n-\tif (cb_data.unique)\n+\tif (cb_data.num_matches == 1)\n \t\treturn cb_data.dst_ref;\n \tfree(cb_data.dst_ref);\n \treturn NULL;\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349029","messageId":"20180601211015.11919-7-avarab@gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-1-avarab@gmail.com","subject":"[PATCH v5 6/8] builtin/checkout.c: use \"ret\" variable for return","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T21:10:13Z","receivedAt":"2018-06-01T21:10:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There is no point in doing this right now, but in later change the\n\"ret\" variable will be inspected. This change makes that meaningful\nchange smaller.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 8 +++++---\n 1 file changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 72457fb7d5..8c93c55cbc 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1265,8 +1265,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr)\n-\t\treturn checkout_paths(&opts, new_branch_info.name);\n-\telse\n+\tif (opts.patch_mode || opts.pathspec.nr) {\n+\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\t\treturn ret;\n+\t} else {\n \t\treturn checkout_branch(&opts, &new_branch_info);\n+\t}\n }\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349030","messageId":"20180601211015.11919-6-avarab@gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-1-avarab@gmail.com","subject":"[PATCH v5 5/8] checkout: pass the \"num_matches\" up to callers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T21:10:12Z","receivedAt":"2018-06-01T21:10:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Pass the previously added \"num_matches\" struct value up to the callers\nof unique_tracking_name(). This will allow callers to optionally print\nbetter error messages in a later change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 10 +++++++---\n builtin/worktree.c |  4 ++--\n checkout.c         |  5 ++++-\n checkout.h         |  3 ++-\n 4 files changed, 15 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2e1d2376d2..72457fb7d5 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -878,7 +878,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n \t\t\t\tstruct checkout_opts *opts,\n-\t\t\t\tstruct object_id *rev)\n+\t\t\t\tstruct object_id *rev,\n+\t\t\t\tint *dwim_remotes_matched)\n {\n \tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n@@ -972,7 +973,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n-\t\t\tconst char *remote = unique_tracking_name(arg, rev);\n+\t\t\tconst char *remote = unique_tracking_name(arg, rev,\n+\t\t\t\t\t\t\t\t  dwim_remotes_matched);\n \t\t\tif (remote) {\n \t\t\t\t*new_branch = arg;\n \t\t\t\targ = remote;\n@@ -1109,6 +1111,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch = 1;\n+\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n \t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n@@ -1219,7 +1222,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts.new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev);\n+\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 5c7d2bb180..a763dbdccb 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -412,7 +412,7 @@ static const char *dwim_branch(const char *path, const char **new_branch)\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n \t\tconst char *remote =\n-\t\t\tunique_tracking_name(*new_branch, &oid);\n+\t\t\tunique_tracking_name(*new_branch, &oid, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -484,7 +484,7 @@ static int add(int ac, const char **av, const char *prefix)\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 7662a39a62..ee3a7e9c05 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -32,12 +32,15 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \treturn 0;\n }\n \n-const char *unique_tracking_name(const char *name, struct object_id *oid)\n+const char *unique_tracking_name(const char *name, struct object_id *oid,\n+\t\t\t\t int *dwim_remotes_matched)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n+\tif (dwim_remotes_matched)\n+\t\t*dwim_remotes_matched = cb_data.num_matches;\n \tfree(cb_data.src_ref);\n \tif (cb_data.num_matches == 1)\n \t\treturn cb_data.dst_ref;\ndiff --git a/checkout.h b/checkout.h\nindex 4cd4cd1c23..6b2073310c 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -9,6 +9,7 @@\n  * exists, NULL otherwise.\n  */\n extern const char *unique_tracking_name(const char *name,\n-\t\t\t\t\tstruct object_id *oid);\n+\t\t\t\t\tstruct object_id *oid,\n+\t\t\t\t\tint *dwim_remotes_matched);\n \n #endif /* CHECKOUT_H */\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349031","messageId":"20180601211015.11919-9-avarab@gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-1-avarab@gmail.com","subject":"[PATCH v5 8/8] checkout & worktree: introduce checkout.defaultRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T21:10:15Z","receivedAt":"2018-06-01T21:10:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Introduce a checkout.defaultRemote setting which can be used to\ndesignate a remote to prefer (via checkout.defaultRemote=origin) when\nrunning e.g. \"git checkout master\" to mean origin/master, even though\nthere's other remotes that have the \"master\" branch.\n\nI want this because it's very handy to use this workflow to checkout a\nrepository and create a topic branch, then get back to a \"master\" as\nretrieved from upstream:\n\n    (\n        cd /tmp &&\n        rm -rf tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git checkout master\n    )\n\nThat will output:\n\n    Branch 'master' set up to track remote branch 'master' from 'origin'.\n    Switched to a new branch 'master'\n\nBut as soon as a new remote is added (e.g. just to inspect something\nfrom someone else) the DWIMery goes away:\n\n    (\n        cd /tmp &&\n        rm -rf tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git remote add avar git@github.com:avar/tbdiff.git &&\n        git fetch avar &&\n        git checkout master\n    )\n\nWill output (without the advice output added earlier in this series):\n\n    error: pathspec 'master' did not match any file(s) known to git.\n\nThe new checkout.defaultRemote config allows me to say that whenever\nthat ambiguity comes up I'd like to prefer \"origin\", and it'll still\nwork as though the only remote I had was \"origin\".\n\nAlso adjust the advice.checkoutAmbiguousRemoteBranchName message to\nmention this new config setting to the user, the full output on my\ngit.git is now (the last paragraph is new):\n\n    $ ./git --exec-path=$PWD checkout master\n    error: pathspec 'master' did not match any file(s) known to git.\n    hint: The argument 'master' matched more than one remote tracking branch.\n    hint: We found 26 remotes with a reference that matched. So we fell back\n    hint: on trying to resolve the argument as a path, but failed there too!\n    hint:\n    hint: If you meant to check out a remote tracking branch on e.g. 'origin'\n    hint: you can do so by fully-qualifying the name with the --track option:\n    hint:\n    hint:     git checkout --track origin/<name>\n    hint:\n    hint: If you'd like to always have checkouts of an ambiguous <name> prefer\n    hint: one remote, e.g. the 'origin' remote, consider setting\n    hint: checkout.defaultRemote=origin in your config.\n\nI considered splitting this into checkout.defaultRemote and\nworktree.defaultRemote, but it's probably less confusing to break our\nown rules that anything shared between config should live in core.*\nthan have two config settings, and I couldn't come up with a short\nname under core.* that made sense (core.defaultRemoteForCheckout?).\n\nSee also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\nfrotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\nto begin with, and 4e85333197 (\"worktree: make add <path> <branch>\ndwim\", 2017-11-26) which added it to git-worktree.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt       | 21 ++++++++++++++++++++-\n Documentation/git-checkout.txt |  9 +++++++++\n Documentation/git-worktree.txt |  9 +++++++++\n builtin/checkout.c             | 12 +++++++++---\n checkout.c                     | 26 ++++++++++++++++++++++++--\n t/t2024-checkout-dwim.sh       | 18 +++++++++++++++++-\n t/t2025-worktree-add.sh        | 21 +++++++++++++++++++++\n 7 files changed, 109 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex dfc0413a84..aef2769211 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -350,7 +350,10 @@ advice.*::\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\n-\t\tchecked out.\n+\t\tchecked out. See the `checkout.defaultRemote`\n+\t\tconfiguration variable for how to set a given remote\n+\t\tto used by default in some situations where this\n+\t\tadvice would be printed.\n \tamWorkDir::\n \t\tAdvice that shows the location of the patch file when\n \t\tlinkgit:git-am[1] fails to apply it.\n@@ -1105,6 +1108,22 @@ browser.<tool>.path::\n \tbrowse HTML help (see `-w` option in linkgit:git-help[1]) or a\n \tworking repository in gitweb (see linkgit:git-instaweb[1]).\n \n+checkout.defaultRemote::\n+\tWhen you run 'git checkout <something>' and only have one\n+\tremote, it may implicitly fall back on checking out and\n+\ttracking e.g. 'origin/<something>'. This stops working as soon\n+\tas you have more than one remote with a '<something>'\n+\treference. This setting allows for setting the name of a\n+\tpreferred remote that should always win when it comes to\n+\tdisambiguation. The typical use-case is to set this to\n+\t`origin`.\n++\n+Currently this is used by linkgit:git-checkout[1] when 'git checkout\n+<something>' will checkout the '<something>' branch on another remote,\n+and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n+remote branch. This setting might be used for other checkout-like\n+commands or functionality in the future.\n+\n clean.requireForce::\n \tA boolean to make git-clean do nothing unless given -f,\n \t-i or -n.   Defaults to true.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ca5fc9c798..9db02928c4 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -38,6 +38,15 @@ equivalent to\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n You could omit <branch>, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex afc6576a14..9c26be40f4 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -60,6 +60,15 @@ with a matching name, treat as equivalent to:\n $ git worktree add --track -b <branch> <path> <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n then, as a convenience, the new worktree is associated with a branch\n (call it `<branch>`) named after `$(basename <path>)`.  If `<branch>`\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4dfb8f1535..b78481dead 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -912,8 +912,10 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t *   (b) If <something> is _not_ a commit, either \"--\" is present\n \t *       or <something> is not a path, no -t or -b was given, and\n \t *       and there is a tracking branch whose name is <something>\n-\t *       in one and only one remote, then this is a short-hand to\n-\t *       fork local <something> from that remote-tracking branch.\n+\t *       in one and only one remote (or if the branch exists on the\n+\t *       remote named in checkout.defaultRemote), then this is a\n+\t *       short-hand to fork local <something> from that\n+\t *       remote-tracking branch.\n \t *\n \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\n \t *\n@@ -1277,7 +1279,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t\t \"If you meant to check out a remote tracking branch on e.g. 'origin'\\n\"\n \t\t\t\t \"you can do so by fully-qualifying the name with the --track option:\\n\"\n \t\t\t\t \"\\n\"\n-\t\t\t\t \"    git checkout --track origin/<name>\"),\n+\t\t\t\t \"    git checkout --track origin/<name>\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t\t\t \"checkout.defaultRemote=origin in your config.\"),\n \t\t\t       argv[0],\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\ndiff --git a/checkout.c b/checkout.c\nindex ee3a7e9c05..c72e9f9773 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -2,15 +2,19 @@\n #include \"remote.h\"\n #include \"refspec.h\"\n #include \"checkout.h\"\n+#include \"config.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n \tint num_matches;\n+\tconst char *default_remote;\n+\tchar *default_dst_ref;\n+\tstruct object_id *default_dst_oid;\n };\n \n-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0, NULL, NULL, NULL }\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n@@ -24,6 +28,12 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\treturn 0;\n \t}\n \tcb->num_matches++;\n+\tif (cb->default_remote && !strcmp(remote->name, cb->default_remote)) {\n+\t\tstruct object_id *dst = xmalloc(sizeof(*cb->default_dst_oid));\n+\t\tcb->default_dst_ref = xstrdup(query.dst);\n+\t\toidcpy(dst, cb->dst_oid);\n+\t\tcb->default_dst_oid = dst;\n+\t}\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -36,14 +46,26 @@ const char *unique_tracking_name(const char *name, struct object_id *oid,\n \t\t\t\t int *dwim_remotes_matched)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n+\tconst char *default_remote = NULL;\n+\tif (!git_config_get_string_const(\"checkout.defaultremote\", &default_remote))\n+\t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\n \tfree(cb_data.src_ref);\n-\tif (cb_data.num_matches == 1)\n+\tfree((char *)default_remote);\n+\tif (cb_data.num_matches == 1) {\n+\t\tfree(cb_data.default_dst_ref);\n+\t\tfree(cb_data.default_dst_oid);\n \t\treturn cb_data.dst_ref;\n+\t}\n \tfree(cb_data.dst_ref);\n+\tif (cb_data.default_dst_ref) {\n+\t\toidcpy(oid, cb_data.default_dst_oid);\n+\t\tfree(cb_data.default_dst_oid);\n+\t\treturn cb_data.default_dst_ref;\n+\t}\n \treturn NULL;\n }\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex fef263a858..1495c248a7 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -87,7 +87,23 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'\n \t\tcheckout foo 2>stderr &&\n \ttest_branch master &&\n \tstatus_uno_is_clean &&\n-\ttest_i18ngrep ! \"^hint: \" stderr\n+\ttest_i18ngrep ! \"^hint: \" stderr &&\n+\t# Make sure the likes of checkout -p don not print this hint\n+\tgit checkout -p foo 2>stderr &&\n+\ttest_i18ngrep ! \"^hint: \" stderr &&\n+\tstatus_uno_is_clean\n+'\n+\n+test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.defaultRemote #1' '\n+\tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n+\ttest_might_fail git branch -D foo &&\n+\n+\tgit -c checkout.defaultRemote=repo_a checkout foo &&\n+\tstatus_uno_is_clean &&\n+\ttest_branch foo &&\n+\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n+\ttest_branch_upstream foo repo_a foo\n '\n \n test_expect_success 'checkout of branch from a single remote succeeds #1' '\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex d2e49f7632..be6e093142 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -402,6 +402,27 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n \t)\n '\n \n+test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n+\ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n+\tsetup_remote_repo repo_upstream repo_dwim &&\n+\tgit init repo_dwim &&\n+\t(\n+\t\tcd repo_dwim &&\n+\t\tgit remote add repo_upstream2 ../repo_upstream &&\n+\t\tgit fetch repo_upstream2 &&\n+\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n+\t\t>status.expect &&\n+\t\tgit status -uno --porcelain >status.actual &&\n+\t\ttest_cmp status.expect status.actual\n+\t) &&\n+\t(\n+\t\tcd foo &&\n+\t\ttest_branch_upstream foo repo_upstream foo &&\n+\t\ttest_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo\n+\t)\n+'\n+\n test_expect_success 'git worktree add does not match remote' '\n \ttest_when_finished rm -rf repo_a repo_b foo &&\n \tsetup_remote_repo repo_a repo_b &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349032","messageId":"20180601211015.11919-8-avarab@gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-1-avarab@gmail.com","subject":"[PATCH v5 7/8] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-01T21:10:14Z","receivedAt":"2018-06-01T21:10:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As the \"checkout\" documentation describes:\n\n    If <branch> is not found but there does exist a tracking branch in\n    exactly one remote (call it <remote>) with a matching name, treat\n    as equivalent to [...] <remote>/<branch.\n\nThis is a really useful feature. The problem is that when you and\nanother remote (e.g. a fork) git won't find a unique branch name\nanymore, and will instead print this nondescript message:\n\n    $ git checkout master\n    error: pathspec 'master' did not match any file(s) known to git\n\nNow it will, on my git.git checkout, print:\n\n    $ ./git --exec-path=$PWD checkout master\n    error: pathspec 'master' did not match any file(s) known to git.\n    hint: The argument 'master' matched more than one remote tracking branch.\n    hint: We found 26 remotes with a reference that matched. So we fell back\n    hint: on trying to resolve the argument as a path, but failed there too!\n    hint:\n    hint: If you meant to check out a remote tracking branch on e.g. 'origin'\n    hint: you can do so by fully-qualifying the name with the --track option:\n    hint:\n    hint:     git checkout --track origin/<name>\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt |  7 +++++++\n advice.c                 |  2 ++\n advice.h                 |  1 +\n builtin/checkout.c       | 13 +++++++++++++\n t/t2024-checkout-dwim.sh | 14 ++++++++++++++\n 5 files changed, 37 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ab641bf5a9..dfc0413a84 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -344,6 +344,13 @@ advice.*::\n \t\tAdvice shown when you used linkgit:git-checkout[1] to\n \t\tmove to the detach HEAD state, to instruct how to create\n \t\ta local branch after the fact.\n+\tcheckoutAmbiguousRemoteBranchName::\n+\t\tAdvice shown when the argument to\n+\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tremote tracking branch on more than one remote in\n+\t\tsituations where an unambiguous argument would have\n+\t\totherwise caused a remote-tracking branch to be\n+\t\tchecked out.\n \tamWorkDir::\n \t\tAdvice that shows the location of the patch file when\n \t\tlinkgit:git-am[1] fails to apply it.\ndiff --git a/advice.c b/advice.c\nindex 370a56d054..75e7dede90 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -21,6 +21,7 @@ int advice_add_embedded_repo = 1;\n int advice_ignored_hook = 1;\n int advice_waiting_for_editor = 1;\n int advice_graft_file_deprecated = 1;\n+int advice_checkout_ambiguous_remote_branch_name = 1;\n \n static int advice_use_color = -1;\n static char advice_colors[][COLOR_MAXLEN] = {\n@@ -72,6 +73,7 @@ static struct {\n \t{ \"ignoredhook\", &advice_ignored_hook },\n \t{ \"waitingforeditor\", &advice_waiting_for_editor },\n \t{ \"graftfiledeprecated\", &advice_graft_file_deprecated },\n+\t{ \"checkoutambiguousremotebranchname\", &advice_checkout_ambiguous_remote_branch_name },\n \n \t/* make this an alias for backward compatibility */\n \t{ \"pushnonfastforward\", &advice_push_update_rejected }\ndiff --git a/advice.h b/advice.h\nindex 9f5064e82a..4d11d51d43 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -22,6 +22,7 @@ extern int advice_add_embedded_repo;\n extern int advice_ignored_hook;\n extern int advice_waiting_for_editor;\n extern int advice_graft_file_deprecated;\n+extern int advice_checkout_ambiguous_remote_branch_name;\n \n int git_default_advice_config(const char *var, const char *value);\n __attribute__((format (printf, 1, 2)))\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8c93c55cbc..4dfb8f1535 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -22,6 +22,7 @@\n #include \"resolve-undo.h\"\n #include \"submodule-config.h\"\n #include \"submodule.h\"\n+#include \"advice.h\"\n \n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n@@ -1267,6 +1268,18 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tUNLEAK(opts);\n \tif (opts.patch_mode || opts.pathspec.nr) {\n \t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\t\tif (ret && dwim_remotes_matched > 1 &&\n+\t\t    advice_checkout_ambiguous_remote_branch_name)\n+\t\t\tadvise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n+\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n+\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"If you meant to check out a remote tracking branch on e.g. 'origin'\\n\"\n+\t\t\t\t \"you can do so by fully-qualifying the name with the --track option:\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"    git checkout --track origin/<name>\"),\n+\t\t\t       argv[0],\n+\t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n \t\treturn checkout_branch(&opts, &new_branch_info);\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex ed32828105..fef263a858 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -76,6 +76,20 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_branch master\n '\n \n+test_expect_success 'checkout of branch from multiple remotes fails with advice' '\n+\tgit checkout -B master &&\n+\ttest_might_fail git branch -D foo &&\n+\ttest_must_fail git checkout foo 2>stderr &&\n+\ttest_branch master &&\n+\tstatus_uno_is_clean &&\n+\ttest_i18ngrep \"^hint: \" stderr &&\n+\ttest_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \\\n+\t\tcheckout foo 2>stderr &&\n+\ttest_branch master &&\n+\tstatus_uno_is_clean &&\n+\ttest_i18ngrep ! \"^hint: \" stderr\n+'\n+\n test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \tgit checkout -B master &&\n \ttest_might_fail git branch -D bar &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349034","messageId":"CAPig+cR-4ni9nuKp8buvBOKe-43DM7g5Y_pfEcQDuM5n9xuhVg@mail.gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-4-avarab@gmail.com","subject":"Re: [PATCH v5 3/8] checkout.[ch]: introduce an *_INIT macro","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-06-01T22:40:08Z","receivedAt":"2018-06-01T22:40:12Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Jun 1, 2018 at 5:10 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> checkout.[ch]: introduce an *_INIT macro\n\n   checkout.c: introduce...\n\n> Add an *_INIT macro for the tracking_name_data similar to what exists\n> elsewhere in the codebase, e.g. OID_ARRAY_INIT in sha1-array.h. This\n> will make it more idiomatic in later changes to add more fields to the\n> struct & its initialization macro.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"},{"id":"349035","messageId":"CAPig+cTVRk-fGZiGDx=D6JmyDUDXFEaQ=JTBYX97e9VJOvxafQ@mail.gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-5-avarab@gmail.com","subject":"Re: [PATCH v5 4/8] checkout.[ch]: change \"unique\" member to \"num_matches\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-06-01T22:41:10Z","receivedAt":"2018-06-01T22:41:14Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Jun 1, 2018 at 5:10 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> checkout.[ch]: change \"unique\" member to \"num_matches\"\n\n    checkout.c: change...\n\n> Internally track how many matches we find in the check_tracking_name()\n> callback. Nothing uses this now, but it will be made use of in a later\n> change.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n"},{"id":"349036","messageId":"CAPig+cRGDi-FX1Pr-7dt7G1fbP=ZabFNimuMr-OheBabKAXHLA@mail.gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-8-avarab@gmail.com","subject":"Re: [PATCH v5 7/8] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-06-01T22:52:30Z","receivedAt":"2018-06-01T22:52:36Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Jun 1, 2018 at 5:10 PM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> As the \"checkout\" documentation describes:\n>\n>     If <branch> is not found but there does exist a tracking branch in\n>     exactly one remote (call it <remote>) with a matching name, treat\n>     as equivalent to [...] <remote>/<branch.\n>\n> This is a really useful feature. The problem is that when you and\n\ns/and/add/\n\n> another remote (e.g. a fork) git won't find a unique branch name\n\nMissing comma: s/)/),/\n\n> anymore, and will instead print this nondescript message:\n\nPerhaps s/nondescript/unhelpful/ ?\n\n>     $ git checkout master\n>     error: pathspec 'master' did not match any file(s) known to git\n>\n> Now it will, on my git.git checkout, print:\n>\n>     $ ./git --exec-path=$PWD checkout master\n>     error: pathspec 'master' did not match any file(s) known to git.\n>     hint: The argument 'master' matched more than one remote tracking branch.\n>     hint: We found 26 remotes with a reference that matched. So we fell back\n>     hint: on trying to resolve the argument as a path, but failed there too!\n>     hint:\n>     hint: If you meant to check out a remote tracking branch on e.g. 'origin'\n\nMissing commas: s/on e.g. 'origin'/on, e.g. 'origin',/\n\n>     hint: you can do so by fully-qualifying the name with the --track option:\n\ns/fully-qualifying/fully qualifying/\n\n>     hint:\n>     hint:     git checkout --track origin/<name>\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAside from the s/and/add/ botch, all of the above are tiny nits which\ndon't actually impact the meaning of the commit message, thus not\nreally important.\n\n> ---\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> @@ -1267,6 +1268,18 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n> +               if (ret && dwim_remotes_matched > 1 &&\n> +                   advice_checkout_ambiguous_remote_branch_name)\n> +                       advise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n\nYou could drop \"The argument\" prefix, without hurting the meaning at\nall, in order to gain a bit more horizontal space for the '%s'\ninterpolation. (Not worth a re-roll.)\n\n> +                                \"We found %d remotes with a reference that matched. So we fell back\\n\"\n> +                                \"on trying to resolve the argument as a path, but failed there too!\\n\"\n> +                                \"\\n\"\n> +                                \"If you meant to check out a remote tracking branch on e.g. 'origin'\\n\"\n> +                                \"you can do so by fully-qualifying the name with the --track option:\\n\"\n> +                                \"\\n\"\n> +                                \"    git checkout --track origin/<name>\"),\n> +                              argv[0],\n> +                              dwim_remotes_matched);\n"},{"id":"349078","messageId":"20180602115042.18167-1-avarab@gmail.com","threadId":"48399","inReplyTo":"20180601211015.11919-1-avarab@gmail.com","subject":"[PATCH v6 0/8] ambiguous checkout UI & checkout.defaultRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-02T11:50:34Z","receivedAt":"2018-06-02T11:51:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Typo & grammar fixes suggested by Eric Sunshine. tbdiff from v5:\n    \n    1: ab4529d9f5 = 1: ab4529d9f5 checkout tests: index should be clean after dwim checkout\n    2: c8bbece403 = 2: c8bbece403 checkout.h: wrap the arguments to unique_tracking_name()\n    3: 4fc5ab27fa ! 3: 881fe63f4f checkout.c: introduce an *_INIT macro\n        @@ -1,6 +1,6 @@\n         Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         \n        -    checkout.[ch]: introduce an *_INIT macro\n        +    checkout.c: introduce an *_INIT macro\n             \n             Add an *_INIT macro for the tracking_name_data similar to what exists\n             elsewhere in the codebase, e.g. OID_ARRAY_INIT in sha1-array.h. This\n    4: fbce6df584 ! 4: 72ddaeddd3 checkout.c]: change \"unique\" member to \"num_matches\"\n        @@ -1,6 +1,6 @@\n         Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n         \n        -    checkout.[ch]: change \"unique\" member to \"num_matches\"\n        +    checkout.c]: change \"unique\" member to \"num_matches\"\n             \n             Internally track how many matches we find in the check_tracking_name()\n             callback. Nothing uses this now, but it will be made use of in a later\n    5: 6e016d43d7 = 5: 5e8c82680b checkout: pass the \"num_matches\" up to callers\n    6: 07b11b133d = 6: 07e667f80a builtin/checkout.c: use \"ret\" variable for return\n    7: 97e84f6e1c ! 7: 0a148182e6 checkout: add advice for ambiguous \"checkout <branch>\"\n        @@ -8,9 +8,9 @@\n                 exactly one remote (call it <remote>) with a matching name, treat\n                 as equivalent to [...] <remote>/<branch.\n             \n        -    This is a really useful feature. The problem is that when you and\n        -    another remote (e.g. a fork) git won't find a unique branch name\n        -    anymore, and will instead print this nondescript message:\n        +    This is a really useful feature. The problem is that when you add\n        +    another remote (e.g. a fork), git won't find a unique branch name\n        +    anymore, and will instead print this unhelpful message:\n             \n                 $ git checkout master\n                 error: pathspec 'master' did not match any file(s) known to git\n        @@ -19,12 +19,12 @@\n             \n                 $ ./git --exec-path=$PWD checkout master\n                 error: pathspec 'master' did not match any file(s) known to git.\n        -        hint: The argument 'master' matched more than one remote tracking branch.\n        +        hint: 'master' matched more than one remote tracking branch.\n                 hint: We found 26 remotes with a reference that matched. So we fell back\n                 hint: on trying to resolve the argument as a path, but failed there too!\n                 hint:\n        -        hint: If you meant to check out a remote tracking branch on e.g. 'origin'\n        -        hint: you can do so by fully-qualifying the name with the --track option:\n        +        hint: If you meant to check out a remote tracking branch on, e.g. 'origin',\n        +        hint: you can do so by fully qualifying the name with the --track option:\n                 hint:\n                 hint:     git checkout --track origin/<name>\n             \n        @@ -97,12 +97,12 @@\n          \t\tint ret = checkout_paths(&opts, new_branch_info.name);\n         +\t\tif (ret && dwim_remotes_matched > 1 &&\n         +\t\t    advice_checkout_ambiguous_remote_branch_name)\n        -+\t\t\tadvise(_(\"The argument '%s' matched more than one remote tracking branch.\\n\"\n        ++\t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n         +\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n         +\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n         +\t\t\t\t \"\\n\"\n        -+\t\t\t\t \"If you meant to check out a remote tracking branch on e.g. 'origin'\\n\"\n        -+\t\t\t\t \"you can do so by fully-qualifying the name with the --track option:\\n\"\n        ++\t\t\t\t \"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n        ++\t\t\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n         +\t\t\t\t \"\\n\"\n         +\t\t\t\t \"    git checkout --track origin/<name>\"),\n         +\t\t\t       argv[0],\n    8: a5cc070ebf ! 8: f3a52a26a2 checkout & worktree: introduce checkout.defaultRemote\n        @@ -175,8 +175,8 @@\n          \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\n          \t *\n         @@\n        - \t\t\t\t \"If you meant to check out a remote tracking branch on e.g. 'origin'\\n\"\n        - \t\t\t\t \"you can do so by fully-qualifying the name with the --track option:\\n\"\n        + \t\t\t\t \"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n        + \t\t\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n          \t\t\t\t \"\\n\"\n         -\t\t\t\t \"    git checkout --track origin/<name>\"),\n         +\t\t\t\t \"    git checkout --track origin/<name>\\n\"\n\nÆvar Arnfjörð Bjarmason (8):\n  checkout tests: index should be clean after dwim checkout\n  checkout.h: wrap the arguments to unique_tracking_name()\n  checkout.c: introduce an *_INIT macro\n  checkout.c]: change \"unique\" member to \"num_matches\"\n  checkout: pass the \"num_matches\" up to callers\n  builtin/checkout.c: use \"ret\" variable for return\n  checkout: add advice for ambiguous \"checkout <branch>\"\n  checkout & worktree: introduce checkout.defaultRemote\n\n Documentation/config.txt       | 26 +++++++++++++++\n Documentation/git-checkout.txt |  9 ++++++\n Documentation/git-worktree.txt |  9 ++++++\n advice.c                       |  2 ++\n advice.h                       |  1 +\n builtin/checkout.c             | 41 ++++++++++++++++++-----\n builtin/worktree.c             |  4 +--\n checkout.c                     | 37 ++++++++++++++++++---\n checkout.h                     |  4 ++-\n t/t2024-checkout-dwim.sh       | 59 ++++++++++++++++++++++++++++++++++\n t/t2025-worktree-add.sh        | 21 ++++++++++++\n 11 files changed, 197 insertions(+), 16 deletions(-)\n\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349079","messageId":"20180602115042.18167-2-avarab@gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-1-avarab@gmail.com","subject":"[PATCH v6 1/8] checkout tests: index should be clean after dwim checkout","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-02T11:50:35Z","receivedAt":"2018-06-02T11:51:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Assert that whenever there's a DWIM checkout that the index should be\nclean afterwards, in addition to the correct branch being checked-out.\n\nThe way the DWIM checkout code in checkout.[ch] works is by looping\nover all remotes, and for each remote trying to find if a given\nreference name only exists on that remote, or if it exists anywhere\nelse.\n\nThis is done by starting out with a `unique = 1` tracking variable in\na struct shared by the entire loop, which will get set to `0` if the\ndata reference is not unique.\n\nThus if we find a match we know the dst_oid member of\ntracking_name_data must be correct, since it's associated with the\nonly reference on the only remote that could have matched our query.\n\nBut if there was ever a mismatch there for some reason we might end up\nwith the correct branch checked out, but at the wrong oid, which would\nshow whatever the difference between the two staged in the\nindex (checkout branch A, stage changes from the state of branch B).\n\nSo let's amend the tests (mostly added in) 399e4a1c56 (\"t2024: Add\ntests verifying current DWIM behavior of 'git checkout <branch>'\",\n2013-04-21) to always assert that \"status\" is clean after we run\n\"checkout\", that's being done with \"-uno\" because there's going to be\nsome untracked files related to the test itself which we don't care\nabout.\n\nIn all these tests (DWIM or otherwise) we start with a clean index, so\nthese tests are asserting that that's still the case after the\n\"checkout\", failed or otherwise.\n\nThen if we ever run into this sort of regression, either in the\nexisting code or with a new feature, we'll know.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t2024-checkout-dwim.sh | 29 +++++++++++++++++++++++++++++\n 1 file changed, 29 insertions(+)\n\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 3e5ac81bd2..ed32828105 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -23,6 +23,12 @@ test_branch_upstream () {\n \ttest_cmp expect.upstream actual.upstream\n }\n \n+status_uno_is_clean() {\n+\t>status.expect &&\n+\tgit status -uno --porcelain >status.actual &&\n+\ttest_cmp status.expect status.actual\n+}\n+\n test_expect_success 'setup' '\n \ttest_commit my_master &&\n \tgit init repo_a &&\n@@ -55,6 +61,7 @@ test_expect_success 'checkout of non-existing branch fails' '\n \ttest_might_fail git branch -D xyzzy &&\n \n \ttest_must_fail git checkout xyzzy &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/xyzzy &&\n \ttest_branch master\n '\n@@ -64,6 +71,7 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_might_fail git branch -D foo &&\n \n \ttest_must_fail git checkout foo &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/foo &&\n \ttest_branch master\n '\n@@ -73,6 +81,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \ttest_might_fail git branch -D bar &&\n \n \tgit checkout bar &&\n+\tstatus_uno_is_clean &&\n \ttest_branch bar &&\n \ttest_cmp_rev remotes/repo_a/bar HEAD &&\n \ttest_branch_upstream bar repo_a bar\n@@ -83,6 +92,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '\n \ttest_might_fail git branch -D baz &&\n \n \tgit checkout baz &&\n+\tstatus_uno_is_clean &&\n \ttest_branch baz &&\n \ttest_cmp_rev remotes/other_b/baz HEAD &&\n \ttest_branch_upstream baz repo_b baz\n@@ -90,6 +100,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '\n \n test_expect_success '--no-guess suppresses branch auto-vivification' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D bar &&\n \n \ttest_must_fail git checkout --no-guess bar &&\n@@ -99,6 +110,7 @@ test_expect_success '--no-guess suppresses branch auto-vivification' '\n \n test_expect_success 'setup more remotes with unconventional refspecs' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \tgit init repo_c &&\n \t(\n \t\tcd repo_c &&\n@@ -128,27 +140,33 @@ test_expect_success 'setup more remotes with unconventional refspecs' '\n \n test_expect_success 'checkout of branch from multiple remotes fails #2' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D bar &&\n \n \ttest_must_fail git checkout bar &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/bar &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch from multiple remotes fails #3' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D baz &&\n \n \ttest_must_fail git checkout baz &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/baz &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch from a single remote succeeds #3' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \tgit checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_branch spam &&\n \ttest_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&\n \ttest_branch_upstream spam repo_c spam\n@@ -156,9 +174,11 @@ test_expect_success 'checkout of branch from a single remote succeeds #3' '\n \n test_expect_success 'checkout of branch from a single remote succeeds #4' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D eggs &&\n \n \tgit checkout eggs &&\n+\tstatus_uno_is_clean &&\n \ttest_branch eggs &&\n \ttest_cmp_rev refs/repo_d/eggs HEAD &&\n \ttest_branch_upstream eggs repo_d eggs\n@@ -166,32 +186,38 @@ test_expect_success 'checkout of branch from a single remote succeeds #4' '\n \n test_expect_success 'checkout of branch with a file having the same name fails' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \ttest_must_fail git checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/spam &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch with a file in subdir having the same name fails' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \tmkdir sub &&\n \tmv spam sub/spam &&\n \ttest_must_fail git -C sub checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/spam &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \tgit checkout spam -- &&\n+\tstatus_uno_is_clean &&\n \ttest_branch spam &&\n \ttest_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&\n \ttest_branch_upstream spam repo_c spam\n@@ -200,6 +226,7 @@ test_expect_success 'checkout <branch> -- succeeds, even if a file with the same\n test_expect_success 'loosely defined local base branch is reported correctly' '\n \n \tgit checkout master &&\n+\tstatus_uno_is_clean &&\n \tgit branch strict &&\n \tgit branch loose &&\n \tgit commit --allow-empty -m \"a bit more\" &&\n@@ -210,7 +237,9 @@ test_expect_success 'loosely defined local base branch is reported correctly' '\n \ttest_config branch.loose.merge master &&\n \n \tgit checkout strict | sed -e \"s/strict/BRANCHNAME/g\" >expect &&\n+\tstatus_uno_is_clean &&\n \tgit checkout loose | sed -e \"s/loose/BRANCHNAME/g\" >actual &&\n+\tstatus_uno_is_clean &&\n \n \ttest_cmp expect actual\n '\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349080","messageId":"20180602115042.18167-3-avarab@gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-1-avarab@gmail.com","subject":"[PATCH v6 2/8] checkout.h: wrap the arguments to unique_tracking_name()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-02T11:50:36Z","receivedAt":"2018-06-02T11:51:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The line was too long already, and will be longer still when a later\nchange adds another argument.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.h | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/checkout.h b/checkout.h\nindex 9980711179..4cd4cd1c23 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -8,6 +8,7 @@\n  * tracking branch.  Return the name of the remote if such a branch\n  * exists, NULL otherwise.\n  */\n-extern const char *unique_tracking_name(const char *name, struct object_id *oid);\n+extern const char *unique_tracking_name(const char *name,\n+\t\t\t\t\tstruct object_id *oid);\n \n #endif /* CHECKOUT_H */\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349081","messageId":"20180602115042.18167-4-avarab@gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-1-avarab@gmail.com","subject":"[PATCH v6 3/8] checkout.c: introduce an *_INIT macro","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-02T11:50:37Z","receivedAt":"2018-06-02T11:51:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add an *_INIT macro for the tracking_name_data similar to what exists\nelsewhere in the codebase, e.g. OID_ARRAY_INIT in sha1-array.h. This\nwill make it more idiomatic in later changes to add more fields to the\nstruct & its initialization macro.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/checkout.c b/checkout.c\nindex bdefc888ba..80e430cda8 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -10,6 +10,8 @@ struct tracking_name_data {\n \tint unique;\n };\n \n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n+\n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n \tstruct tracking_name_data *cb = cb_data;\n@@ -32,7 +34,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \n const char *unique_tracking_name(const char *name, struct object_id *oid)\n {\n-\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n+\tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349082","messageId":"20180602115042.18167-5-avarab@gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-1-avarab@gmail.com","subject":"[PATCH v6 4/8] checkout.c]: change \"unique\" member to \"num_matches\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-02T11:50:38Z","receivedAt":"2018-06-02T11:51:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Internally track how many matches we find in the check_tracking_name()\ncallback. Nothing uses this now, but it will be made use of in a later\nchange.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/checkout.c b/checkout.c\nindex 80e430cda8..7662a39a62 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -7,10 +7,10 @@ struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n-\tint unique;\n+\tint num_matches;\n };\n \n-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n@@ -23,9 +23,9 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\tfree(query.dst);\n \t\treturn 0;\n \t}\n+\tcb->num_matches++;\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n-\t\tcb->unique = 0;\n \t\treturn 0;\n \t}\n \tcb->dst_ref = query.dst;\n@@ -39,7 +39,7 @@ const char *unique_tracking_name(const char *name, struct object_id *oid)\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tfree(cb_data.src_ref);\n-\tif (cb_data.unique)\n+\tif (cb_data.num_matches == 1)\n \t\treturn cb_data.dst_ref;\n \tfree(cb_data.dst_ref);\n \treturn NULL;\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349083","messageId":"20180602115042.18167-7-avarab@gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-1-avarab@gmail.com","subject":"[PATCH v6 6/8] builtin/checkout.c: use \"ret\" variable for return","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-02T11:50:40Z","receivedAt":"2018-06-02T11:51:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There is no point in doing this right now, but in later change the\n\"ret\" variable will be inspected. This change makes that meaningful\nchange smaller.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 8 +++++---\n 1 file changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 72457fb7d5..8c93c55cbc 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1265,8 +1265,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr)\n-\t\treturn checkout_paths(&opts, new_branch_info.name);\n-\telse\n+\tif (opts.patch_mode || opts.pathspec.nr) {\n+\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\t\treturn ret;\n+\t} else {\n \t\treturn checkout_branch(&opts, &new_branch_info);\n+\t}\n }\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349084","messageId":"20180602115042.18167-6-avarab@gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-1-avarab@gmail.com","subject":"[PATCH v6 5/8] checkout: pass the \"num_matches\" up to callers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-02T11:50:39Z","receivedAt":"2018-06-02T11:51:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Pass the previously added \"num_matches\" struct value up to the callers\nof unique_tracking_name(). This will allow callers to optionally print\nbetter error messages in a later change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 10 +++++++---\n builtin/worktree.c |  4 ++--\n checkout.c         |  5 ++++-\n checkout.h         |  3 ++-\n 4 files changed, 15 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2e1d2376d2..72457fb7d5 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -878,7 +878,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n \t\t\t\tstruct checkout_opts *opts,\n-\t\t\t\tstruct object_id *rev)\n+\t\t\t\tstruct object_id *rev,\n+\t\t\t\tint *dwim_remotes_matched)\n {\n \tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n@@ -972,7 +973,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n-\t\t\tconst char *remote = unique_tracking_name(arg, rev);\n+\t\t\tconst char *remote = unique_tracking_name(arg, rev,\n+\t\t\t\t\t\t\t\t  dwim_remotes_matched);\n \t\t\tif (remote) {\n \t\t\t\t*new_branch = arg;\n \t\t\t\targ = remote;\n@@ -1109,6 +1111,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch = 1;\n+\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n \t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n@@ -1219,7 +1222,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts.new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev);\n+\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 5c7d2bb180..a763dbdccb 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -412,7 +412,7 @@ static const char *dwim_branch(const char *path, const char **new_branch)\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n \t\tconst char *remote =\n-\t\t\tunique_tracking_name(*new_branch, &oid);\n+\t\t\tunique_tracking_name(*new_branch, &oid, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -484,7 +484,7 @@ static int add(int ac, const char **av, const char *prefix)\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 7662a39a62..ee3a7e9c05 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -32,12 +32,15 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \treturn 0;\n }\n \n-const char *unique_tracking_name(const char *name, struct object_id *oid)\n+const char *unique_tracking_name(const char *name, struct object_id *oid,\n+\t\t\t\t int *dwim_remotes_matched)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n+\tif (dwim_remotes_matched)\n+\t\t*dwim_remotes_matched = cb_data.num_matches;\n \tfree(cb_data.src_ref);\n \tif (cb_data.num_matches == 1)\n \t\treturn cb_data.dst_ref;\ndiff --git a/checkout.h b/checkout.h\nindex 4cd4cd1c23..6b2073310c 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -9,6 +9,7 @@\n  * exists, NULL otherwise.\n  */\n extern const char *unique_tracking_name(const char *name,\n-\t\t\t\t\tstruct object_id *oid);\n+\t\t\t\t\tstruct object_id *oid,\n+\t\t\t\t\tint *dwim_remotes_matched);\n \n #endif /* CHECKOUT_H */\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349085","messageId":"20180602115042.18167-8-avarab@gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-1-avarab@gmail.com","subject":"[PATCH v6 7/8] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-02T11:50:41Z","receivedAt":"2018-06-02T11:51:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As the \"checkout\" documentation describes:\n\n    If <branch> is not found but there does exist a tracking branch in\n    exactly one remote (call it <remote>) with a matching name, treat\n    as equivalent to [...] <remote>/<branch.\n\nThis is a really useful feature. The problem is that when you add\nanother remote (e.g. a fork), git won't find a unique branch name\nanymore, and will instead print this unhelpful message:\n\n    $ git checkout master\n    error: pathspec 'master' did not match any file(s) known to git\n\nNow it will, on my git.git checkout, print:\n\n    $ ./git --exec-path=$PWD checkout master\n    error: pathspec 'master' did not match any file(s) known to git.\n    hint: 'master' matched more than one remote tracking branch.\n    hint: We found 26 remotes with a reference that matched. So we fell back\n    hint: on trying to resolve the argument as a path, but failed there too!\n    hint:\n    hint: If you meant to check out a remote tracking branch on, e.g. 'origin',\n    hint: you can do so by fully qualifying the name with the --track option:\n    hint:\n    hint:     git checkout --track origin/<name>\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt |  7 +++++++\n advice.c                 |  2 ++\n advice.h                 |  1 +\n builtin/checkout.c       | 13 +++++++++++++\n t/t2024-checkout-dwim.sh | 14 ++++++++++++++\n 5 files changed, 37 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ab641bf5a9..dfc0413a84 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -344,6 +344,13 @@ advice.*::\n \t\tAdvice shown when you used linkgit:git-checkout[1] to\n \t\tmove to the detach HEAD state, to instruct how to create\n \t\ta local branch after the fact.\n+\tcheckoutAmbiguousRemoteBranchName::\n+\t\tAdvice shown when the argument to\n+\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tremote tracking branch on more than one remote in\n+\t\tsituations where an unambiguous argument would have\n+\t\totherwise caused a remote-tracking branch to be\n+\t\tchecked out.\n \tamWorkDir::\n \t\tAdvice that shows the location of the patch file when\n \t\tlinkgit:git-am[1] fails to apply it.\ndiff --git a/advice.c b/advice.c\nindex 370a56d054..75e7dede90 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -21,6 +21,7 @@ int advice_add_embedded_repo = 1;\n int advice_ignored_hook = 1;\n int advice_waiting_for_editor = 1;\n int advice_graft_file_deprecated = 1;\n+int advice_checkout_ambiguous_remote_branch_name = 1;\n \n static int advice_use_color = -1;\n static char advice_colors[][COLOR_MAXLEN] = {\n@@ -72,6 +73,7 @@ static struct {\n \t{ \"ignoredhook\", &advice_ignored_hook },\n \t{ \"waitingforeditor\", &advice_waiting_for_editor },\n \t{ \"graftfiledeprecated\", &advice_graft_file_deprecated },\n+\t{ \"checkoutambiguousremotebranchname\", &advice_checkout_ambiguous_remote_branch_name },\n \n \t/* make this an alias for backward compatibility */\n \t{ \"pushnonfastforward\", &advice_push_update_rejected }\ndiff --git a/advice.h b/advice.h\nindex 9f5064e82a..4d11d51d43 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -22,6 +22,7 @@ extern int advice_add_embedded_repo;\n extern int advice_ignored_hook;\n extern int advice_waiting_for_editor;\n extern int advice_graft_file_deprecated;\n+extern int advice_checkout_ambiguous_remote_branch_name;\n \n int git_default_advice_config(const char *var, const char *value);\n __attribute__((format (printf, 1, 2)))\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8c93c55cbc..baa027455a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -22,6 +22,7 @@\n #include \"resolve-undo.h\"\n #include \"submodule-config.h\"\n #include \"submodule.h\"\n+#include \"advice.h\"\n \n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n@@ -1267,6 +1268,18 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tUNLEAK(opts);\n \tif (opts.patch_mode || opts.pathspec.nr) {\n \t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\t\tif (ret && dwim_remotes_matched > 1 &&\n+\t\t    advice_checkout_ambiguous_remote_branch_name)\n+\t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n+\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n+\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\t\t\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"    git checkout --track origin/<name>\"),\n+\t\t\t       argv[0],\n+\t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n \t\treturn checkout_branch(&opts, &new_branch_info);\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex ed32828105..fef263a858 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -76,6 +76,20 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_branch master\n '\n \n+test_expect_success 'checkout of branch from multiple remotes fails with advice' '\n+\tgit checkout -B master &&\n+\ttest_might_fail git branch -D foo &&\n+\ttest_must_fail git checkout foo 2>stderr &&\n+\ttest_branch master &&\n+\tstatus_uno_is_clean &&\n+\ttest_i18ngrep \"^hint: \" stderr &&\n+\ttest_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \\\n+\t\tcheckout foo 2>stderr &&\n+\ttest_branch master &&\n+\tstatus_uno_is_clean &&\n+\ttest_i18ngrep ! \"^hint: \" stderr\n+'\n+\n test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \tgit checkout -B master &&\n \ttest_might_fail git branch -D bar &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349086","messageId":"20180602115042.18167-9-avarab@gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-1-avarab@gmail.com","subject":"[PATCH v6 8/8] checkout & worktree: introduce checkout.defaultRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-02T11:50:42Z","receivedAt":"2018-06-02T11:51:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Introduce a checkout.defaultRemote setting which can be used to\ndesignate a remote to prefer (via checkout.defaultRemote=origin) when\nrunning e.g. \"git checkout master\" to mean origin/master, even though\nthere's other remotes that have the \"master\" branch.\n\nI want this because it's very handy to use this workflow to checkout a\nrepository and create a topic branch, then get back to a \"master\" as\nretrieved from upstream:\n\n    (\n        cd /tmp &&\n        rm -rf tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git checkout master\n    )\n\nThat will output:\n\n    Branch 'master' set up to track remote branch 'master' from 'origin'.\n    Switched to a new branch 'master'\n\nBut as soon as a new remote is added (e.g. just to inspect something\nfrom someone else) the DWIMery goes away:\n\n    (\n        cd /tmp &&\n        rm -rf tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git remote add avar git@github.com:avar/tbdiff.git &&\n        git fetch avar &&\n        git checkout master\n    )\n\nWill output (without the advice output added earlier in this series):\n\n    error: pathspec 'master' did not match any file(s) known to git.\n\nThe new checkout.defaultRemote config allows me to say that whenever\nthat ambiguity comes up I'd like to prefer \"origin\", and it'll still\nwork as though the only remote I had was \"origin\".\n\nAlso adjust the advice.checkoutAmbiguousRemoteBranchName message to\nmention this new config setting to the user, the full output on my\ngit.git is now (the last paragraph is new):\n\n    $ ./git --exec-path=$PWD checkout master\n    error: pathspec 'master' did not match any file(s) known to git.\n    hint: The argument 'master' matched more than one remote tracking branch.\n    hint: We found 26 remotes with a reference that matched. So we fell back\n    hint: on trying to resolve the argument as a path, but failed there too!\n    hint:\n    hint: If you meant to check out a remote tracking branch on e.g. 'origin'\n    hint: you can do so by fully-qualifying the name with the --track option:\n    hint:\n    hint:     git checkout --track origin/<name>\n    hint:\n    hint: If you'd like to always have checkouts of an ambiguous <name> prefer\n    hint: one remote, e.g. the 'origin' remote, consider setting\n    hint: checkout.defaultRemote=origin in your config.\n\nI considered splitting this into checkout.defaultRemote and\nworktree.defaultRemote, but it's probably less confusing to break our\nown rules that anything shared between config should live in core.*\nthan have two config settings, and I couldn't come up with a short\nname under core.* that made sense (core.defaultRemoteForCheckout?).\n\nSee also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\nfrotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\nto begin with, and 4e85333197 (\"worktree: make add <path> <branch>\ndwim\", 2017-11-26) which added it to git-worktree.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt       | 21 ++++++++++++++++++++-\n Documentation/git-checkout.txt |  9 +++++++++\n Documentation/git-worktree.txt |  9 +++++++++\n builtin/checkout.c             | 12 +++++++++---\n checkout.c                     | 26 ++++++++++++++++++++++++--\n t/t2024-checkout-dwim.sh       | 18 +++++++++++++++++-\n t/t2025-worktree-add.sh        | 21 +++++++++++++++++++++\n 7 files changed, 109 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex dfc0413a84..aef2769211 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -350,7 +350,10 @@ advice.*::\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\n-\t\tchecked out.\n+\t\tchecked out. See the `checkout.defaultRemote`\n+\t\tconfiguration variable for how to set a given remote\n+\t\tto used by default in some situations where this\n+\t\tadvice would be printed.\n \tamWorkDir::\n \t\tAdvice that shows the location of the patch file when\n \t\tlinkgit:git-am[1] fails to apply it.\n@@ -1105,6 +1108,22 @@ browser.<tool>.path::\n \tbrowse HTML help (see `-w` option in linkgit:git-help[1]) or a\n \tworking repository in gitweb (see linkgit:git-instaweb[1]).\n \n+checkout.defaultRemote::\n+\tWhen you run 'git checkout <something>' and only have one\n+\tremote, it may implicitly fall back on checking out and\n+\ttracking e.g. 'origin/<something>'. This stops working as soon\n+\tas you have more than one remote with a '<something>'\n+\treference. This setting allows for setting the name of a\n+\tpreferred remote that should always win when it comes to\n+\tdisambiguation. The typical use-case is to set this to\n+\t`origin`.\n++\n+Currently this is used by linkgit:git-checkout[1] when 'git checkout\n+<something>' will checkout the '<something>' branch on another remote,\n+and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n+remote branch. This setting might be used for other checkout-like\n+commands or functionality in the future.\n+\n clean.requireForce::\n \tA boolean to make git-clean do nothing unless given -f,\n \t-i or -n.   Defaults to true.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ca5fc9c798..9db02928c4 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -38,6 +38,15 @@ equivalent to\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n You could omit <branch>, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex afc6576a14..9c26be40f4 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -60,6 +60,15 @@ with a matching name, treat as equivalent to:\n $ git worktree add --track -b <branch> <path> <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n then, as a convenience, the new worktree is associated with a branch\n (call it `<branch>`) named after `$(basename <path>)`.  If `<branch>`\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex baa027455a..5b357e922a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -912,8 +912,10 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t *   (b) If <something> is _not_ a commit, either \"--\" is present\n \t *       or <something> is not a path, no -t or -b was given, and\n \t *       and there is a tracking branch whose name is <something>\n-\t *       in one and only one remote, then this is a short-hand to\n-\t *       fork local <something> from that remote-tracking branch.\n+\t *       in one and only one remote (or if the branch exists on the\n+\t *       remote named in checkout.defaultRemote), then this is a\n+\t *       short-hand to fork local <something> from that\n+\t *       remote-tracking branch.\n \t *\n \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\n \t *\n@@ -1277,7 +1279,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t\t \"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n \t\t\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n \t\t\t\t \"\\n\"\n-\t\t\t\t \"    git checkout --track origin/<name>\"),\n+\t\t\t\t \"    git checkout --track origin/<name>\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t\t\t \"checkout.defaultRemote=origin in your config.\"),\n \t\t\t       argv[0],\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\ndiff --git a/checkout.c b/checkout.c\nindex ee3a7e9c05..c72e9f9773 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -2,15 +2,19 @@\n #include \"remote.h\"\n #include \"refspec.h\"\n #include \"checkout.h\"\n+#include \"config.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n \tint num_matches;\n+\tconst char *default_remote;\n+\tchar *default_dst_ref;\n+\tstruct object_id *default_dst_oid;\n };\n \n-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0, NULL, NULL, NULL }\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n@@ -24,6 +28,12 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\treturn 0;\n \t}\n \tcb->num_matches++;\n+\tif (cb->default_remote && !strcmp(remote->name, cb->default_remote)) {\n+\t\tstruct object_id *dst = xmalloc(sizeof(*cb->default_dst_oid));\n+\t\tcb->default_dst_ref = xstrdup(query.dst);\n+\t\toidcpy(dst, cb->dst_oid);\n+\t\tcb->default_dst_oid = dst;\n+\t}\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -36,14 +46,26 @@ const char *unique_tracking_name(const char *name, struct object_id *oid,\n \t\t\t\t int *dwim_remotes_matched)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n+\tconst char *default_remote = NULL;\n+\tif (!git_config_get_string_const(\"checkout.defaultremote\", &default_remote))\n+\t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\n \tfree(cb_data.src_ref);\n-\tif (cb_data.num_matches == 1)\n+\tfree((char *)default_remote);\n+\tif (cb_data.num_matches == 1) {\n+\t\tfree(cb_data.default_dst_ref);\n+\t\tfree(cb_data.default_dst_oid);\n \t\treturn cb_data.dst_ref;\n+\t}\n \tfree(cb_data.dst_ref);\n+\tif (cb_data.default_dst_ref) {\n+\t\toidcpy(oid, cb_data.default_dst_oid);\n+\t\tfree(cb_data.default_dst_oid);\n+\t\treturn cb_data.default_dst_ref;\n+\t}\n \treturn NULL;\n }\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex fef263a858..1495c248a7 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -87,7 +87,23 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'\n \t\tcheckout foo 2>stderr &&\n \ttest_branch master &&\n \tstatus_uno_is_clean &&\n-\ttest_i18ngrep ! \"^hint: \" stderr\n+\ttest_i18ngrep ! \"^hint: \" stderr &&\n+\t# Make sure the likes of checkout -p don not print this hint\n+\tgit checkout -p foo 2>stderr &&\n+\ttest_i18ngrep ! \"^hint: \" stderr &&\n+\tstatus_uno_is_clean\n+'\n+\n+test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.defaultRemote #1' '\n+\tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n+\ttest_might_fail git branch -D foo &&\n+\n+\tgit -c checkout.defaultRemote=repo_a checkout foo &&\n+\tstatus_uno_is_clean &&\n+\ttest_branch foo &&\n+\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n+\ttest_branch_upstream foo repo_a foo\n '\n \n test_expect_success 'checkout of branch from a single remote succeeds #1' '\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex d2e49f7632..be6e093142 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -402,6 +402,27 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n \t)\n '\n \n+test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n+\ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n+\tsetup_remote_repo repo_upstream repo_dwim &&\n+\tgit init repo_dwim &&\n+\t(\n+\t\tcd repo_dwim &&\n+\t\tgit remote add repo_upstream2 ../repo_upstream &&\n+\t\tgit fetch repo_upstream2 &&\n+\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n+\t\t>status.expect &&\n+\t\tgit status -uno --porcelain >status.actual &&\n+\t\ttest_cmp status.expect status.actual\n+\t) &&\n+\t(\n+\t\tcd foo &&\n+\t\ttest_branch_upstream foo repo_upstream foo &&\n+\t\ttest_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo\n+\t)\n+'\n+\n test_expect_success 'git worktree add does not match remote' '\n \ttest_when_finished rm -rf repo_a repo_b foo &&\n \tsetup_remote_repo repo_a repo_b &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349117","messageId":"CAPig+cQRm+KM+JLxR0ch2_ydMFg2xg9FFAA-=VkfqgtSkV6q0w@mail.gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-9-avarab@gmail.com","subject":"Re: [PATCH v6 8/8] checkout & worktree: introduce checkout.defaultRemote","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-06-03T07:58:01Z","receivedAt":"2018-06-03T07:58:06Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Sat, Jun 2, 2018 at 7:50 AM, Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> Introduce a checkout.defaultRemote setting which can be used to\n> designate a remote to prefer (via checkout.defaultRemote=origin) when\n> running e.g. \"git checkout master\" to mean origin/master, even though\n> there's other remotes that have the \"master\" branch.\n> [...]\n> Also adjust the advice.checkoutAmbiguousRemoteBranchName message to\n> mention this new config setting to the user, the full output on my\n> git.git is now (the last paragraph is new):\n>\n>     $ ./git --exec-path=$PWD checkout master\n>     error: pathspec 'master' did not match any file(s) known to git.\n>     hint: The argument 'master' matched more than one remote tracking branch.\n\nIn v6, the \"The argument\" prefix has been dropped from the hint, so\nthis commit message needs a tweak to match.\n\n>     hint: We found 26 remotes with a reference that matched. So we fell back\n>     hint: on trying to resolve the argument as a path, but failed there too!\n> [...]\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n> diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\n> @@ -87,7 +87,23 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'\n> -       test_i18ngrep ! \"^hint: \" stderr\n> +       test_i18ngrep ! \"^hint: \" stderr &&\n> +       # Make sure the likes of checkout -p don not print this hint\n\ns/don/do/\n\n> +       git checkout -p foo 2>stderr &&\n> +       test_i18ngrep ! \"^hint: \" stderr &&\n> +       status_uno_is_clean\n> +'\n"},{"id":"349199","messageId":"xmqqlgbv71oi.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"871sdqal2g.fsf@evledraar.gmail.com","subject":"Re: [PATCH v4 8/9] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-06-04T01:58:21Z","receivedAt":"2018-06-04T01:58:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> I.e. I was trying to avoid printing out the \"error: pathspec 'master'\n> did not match any file(s) known to git.\" error altogether. That's still\n> arguably a good direction, since we *know* \"master\" would have otherwise\n> matched a remote branch, so that's probably a more informative message\n> than falling back to checking out pathspecs and failing, and complaining\n> about there being no such pathspec.\n\nThat ideal behaviour makes sense.\n\n> But it was a pain to handle the various edge cases, e.g.:\n>\n>     $ ./git --exec-path=$PWD checkout x y z\n>     error: pathspec 'x' did not match any file(s) known to git.\n>     error: pathspec 'y' did not match any file(s) known to git.\n>     error: pathspec 'z' did not match any file(s) known to git.\n\nLet's take a detour to a tangent, as this example does not have\nanything to do with the remote-tracking auto-dwimming. \n\nIdeally what do we want to say in this case?  What's allowed for 'x'\n(2 possibilities) is different from whats allowed for 'y' and 'z'\n(only 1 possibility)---do we want to complain that 'x' is not a rev\nnoris a file (we do not say 'x' could be a misspelt rev name right\nnow), and then 'y' and 'z' are not files (which is what we do now)?\n\nThat might be an improvement.  I dunno.  In any case, that is a\ntangent that we do not have to address with these patches.\n\nIn contrast, the command line without y and z gives three\npossibilities to 'x'.  'x' is not a rev, is not a remote-tracking\nbranch name that only a single remote has, and is not a file.  Now,\nif we are going to mention that we failed to interpret it as the\nlatter two, perhaps we should also mention that it was not a rev\n(which could have been misspelt)?\n\n> So I decided just to let checkout_paths() to its thing and then print\n> out an error about dwim branches if applicable if it failed.\n\nYeah, I think I get it now.  If you want to silence the \"error\" from\nreport_path_error() and replace it with something else, you would\nneed to change checout_paths(), as this function is sort-of used as\nthe last ditch effort after all else failed, and right now it is not\naware of what exactly all these other failed efforts were.\n\nThanks.  I'm looking at v6 reroll for queuing.\n\n\n\n"},{"id":"349340","messageId":"20180605144049.26488-1-avarab@gmail.com","threadId":"48399","inReplyTo":"20180602115042.18167-1-avarab@gmail.com","subject":"[PATCH v7 0/8] ambiguous checkout UI & checkout.defaultRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T14:40:41Z","receivedAt":"2018-06-05T14:41:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fixes issues noted with v6, hopefully ready for queuing. A tbdiff with\nv6:\n\n1: ab4529d9f5 = 1: 2ca81c76fc checkout tests: index should be clean after dwim checkout\n2: c8bbece403 = 2: 19b14a1c75 checkout.h: wrap the arguments to unique_tracking_name()\n3: 881fe63f4f = 3: 8bc6a9c052 checkout.c: introduce an *_INIT macro\n4: 72ddaeddd3 ! 4: 34f3b67f9b checkout.c: change \"unique\" member to \"num_matches\"\n    @@ -1,6 +1,6 @@\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    -    checkout.c]: change \"unique\" member to \"num_matches\"\n    +    checkout.c: change \"unique\" member to \"num_matches\"\n         \n         Internally track how many matches we find in the check_tracking_name()\n         callback. Nothing uses this now, but it will be made use of in a later\n5: 5e8c82680b = 5: 7d81c06a23 checkout: pass the \"num_matches\" up to callers\n6: 07e667f80a = 6: e86636ad2c builtin/checkout.c: use \"ret\" variable for return\n7: 0a148182e6 ! 7: c2130b347c checkout: add advice for ambiguous \"checkout <branch>\"\n    @@ -27,6 +27,28 @@\n             hint: you can do so by fully qualifying the name with the --track option:\n             hint:\n             hint:     git checkout --track origin/<name>\n    +    \n    +    Note that the \"error: pathspec[...]\" message is still printed. This is\n    +    because whatever else checkout may have tried earlier, its final\n    +    fallback is to try to resolve the argument as a path. E.g. in this\n    +    case:\n    +    \n    +        $ ./git --exec-path=$PWD checkout master pu\n    +        error: pathspec 'master' did not match any file(s) known to git.\n    +        error: pathspec 'pu' did not match any file(s) known to git.\n    +    \n    +    There we don't print the \"hint:\" implicitly due to earlier logic\n    +    around the DWIM fallback. That fallback is only used if it looks like\n    +    we have one argument that might be a branch.\n    +    \n    +    I can't think of an intrinsic reason for why we couldn't in some\n    +    future change skip printing the \"error: pathspec[...]\" error. However,\n    +    to do so we'd need to pass something down to checkout_paths() to make\n    +    it suppress printing an error on its own, and for us to be confident\n    +    that we're not silencing cases where those errors are meaningful.\n    +    \n    +    I don't think that's worth it since determining whether that's the\n    +    case could easily change due to future changes in the checkout logic.\n         \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n8: f3a52a26a2 ! 8: f1ac0f7351 checkout & worktree: introduce checkout.defaultRemote\n    @@ -53,12 +53,12 @@\n         \n             $ ./git --exec-path=$PWD checkout master\n             error: pathspec 'master' did not match any file(s) known to git.\n    -        hint: The argument 'master' matched more than one remote tracking branch.\n    +        hint: 'master' matched more than one remote tracking branch.\n             hint: We found 26 remotes with a reference that matched. So we fell back\n             hint: on trying to resolve the argument as a path, but failed there too!\n             hint:\n    -        hint: If you meant to check out a remote tracking branch on e.g. 'origin'\n    -        hint: you can do so by fully-qualifying the name with the --track option:\n    +        hint: If you meant to check out a remote tracking branch on, e.g. 'origin',\n    +        hint: you can do so by fully qualifying the name with the --track option:\n             hint:\n             hint:     git checkout --track origin/<name>\n             hint:\n    @@ -263,7 +263,7 @@\n      \tstatus_uno_is_clean &&\n     -\ttest_i18ngrep ! \"^hint: \" stderr\n     +\ttest_i18ngrep ! \"^hint: \" stderr &&\n    -+\t# Make sure the likes of checkout -p don not print this hint\n    ++\t# Make sure the likes of checkout -p do not print this hint\n     +\tgit checkout -p foo 2>stderr &&\n     +\ttest_i18ngrep ! \"^hint: \" stderr &&\n     +\tstatus_uno_is_clean\n\nÆvar Arnfjörð Bjarmason (8):\n  checkout tests: index should be clean after dwim checkout\n  checkout.h: wrap the arguments to unique_tracking_name()\n  checkout.c: introduce an *_INIT macro\n  checkout.c: change \"unique\" member to \"num_matches\"\n  checkout: pass the \"num_matches\" up to callers\n  builtin/checkout.c: use \"ret\" variable for return\n  checkout: add advice for ambiguous \"checkout <branch>\"\n  checkout & worktree: introduce checkout.defaultRemote\n\n Documentation/config.txt       | 26 +++++++++++++++\n Documentation/git-checkout.txt |  9 ++++++\n Documentation/git-worktree.txt |  9 ++++++\n advice.c                       |  2 ++\n advice.h                       |  1 +\n builtin/checkout.c             | 41 ++++++++++++++++++-----\n builtin/worktree.c             |  4 +--\n checkout.c                     | 37 ++++++++++++++++++---\n checkout.h                     |  4 ++-\n t/t2024-checkout-dwim.sh       | 59 ++++++++++++++++++++++++++++++++++\n t/t2025-worktree-add.sh        | 21 ++++++++++++\n 11 files changed, 197 insertions(+), 16 deletions(-)\n\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349341","messageId":"20180605144049.26488-2-avarab@gmail.com","threadId":"48399","inReplyTo":"20180605144049.26488-1-avarab@gmail.com","subject":"[PATCH v7 1/8] checkout tests: index should be clean after dwim checkout","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T14:40:42Z","receivedAt":"2018-06-05T14:41:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Assert that whenever there's a DWIM checkout that the index should be\nclean afterwards, in addition to the correct branch being checked-out.\n\nThe way the DWIM checkout code in checkout.[ch] works is by looping\nover all remotes, and for each remote trying to find if a given\nreference name only exists on that remote, or if it exists anywhere\nelse.\n\nThis is done by starting out with a `unique = 1` tracking variable in\na struct shared by the entire loop, which will get set to `0` if the\ndata reference is not unique.\n\nThus if we find a match we know the dst_oid member of\ntracking_name_data must be correct, since it's associated with the\nonly reference on the only remote that could have matched our query.\n\nBut if there was ever a mismatch there for some reason we might end up\nwith the correct branch checked out, but at the wrong oid, which would\nshow whatever the difference between the two staged in the\nindex (checkout branch A, stage changes from the state of branch B).\n\nSo let's amend the tests (mostly added in) 399e4a1c56 (\"t2024: Add\ntests verifying current DWIM behavior of 'git checkout <branch>'\",\n2013-04-21) to always assert that \"status\" is clean after we run\n\"checkout\", that's being done with \"-uno\" because there's going to be\nsome untracked files related to the test itself which we don't care\nabout.\n\nIn all these tests (DWIM or otherwise) we start with a clean index, so\nthese tests are asserting that that's still the case after the\n\"checkout\", failed or otherwise.\n\nThen if we ever run into this sort of regression, either in the\nexisting code or with a new feature, we'll know.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/t2024-checkout-dwim.sh | 29 +++++++++++++++++++++++++++++\n 1 file changed, 29 insertions(+)\n\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 3e5ac81bd2..ed32828105 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -23,6 +23,12 @@ test_branch_upstream () {\n \ttest_cmp expect.upstream actual.upstream\n }\n \n+status_uno_is_clean() {\n+\t>status.expect &&\n+\tgit status -uno --porcelain >status.actual &&\n+\ttest_cmp status.expect status.actual\n+}\n+\n test_expect_success 'setup' '\n \ttest_commit my_master &&\n \tgit init repo_a &&\n@@ -55,6 +61,7 @@ test_expect_success 'checkout of non-existing branch fails' '\n \ttest_might_fail git branch -D xyzzy &&\n \n \ttest_must_fail git checkout xyzzy &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/xyzzy &&\n \ttest_branch master\n '\n@@ -64,6 +71,7 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_might_fail git branch -D foo &&\n \n \ttest_must_fail git checkout foo &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/foo &&\n \ttest_branch master\n '\n@@ -73,6 +81,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \ttest_might_fail git branch -D bar &&\n \n \tgit checkout bar &&\n+\tstatus_uno_is_clean &&\n \ttest_branch bar &&\n \ttest_cmp_rev remotes/repo_a/bar HEAD &&\n \ttest_branch_upstream bar repo_a bar\n@@ -83,6 +92,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '\n \ttest_might_fail git branch -D baz &&\n \n \tgit checkout baz &&\n+\tstatus_uno_is_clean &&\n \ttest_branch baz &&\n \ttest_cmp_rev remotes/other_b/baz HEAD &&\n \ttest_branch_upstream baz repo_b baz\n@@ -90,6 +100,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '\n \n test_expect_success '--no-guess suppresses branch auto-vivification' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D bar &&\n \n \ttest_must_fail git checkout --no-guess bar &&\n@@ -99,6 +110,7 @@ test_expect_success '--no-guess suppresses branch auto-vivification' '\n \n test_expect_success 'setup more remotes with unconventional refspecs' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \tgit init repo_c &&\n \t(\n \t\tcd repo_c &&\n@@ -128,27 +140,33 @@ test_expect_success 'setup more remotes with unconventional refspecs' '\n \n test_expect_success 'checkout of branch from multiple remotes fails #2' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D bar &&\n \n \ttest_must_fail git checkout bar &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/bar &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch from multiple remotes fails #3' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D baz &&\n \n \ttest_must_fail git checkout baz &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/baz &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch from a single remote succeeds #3' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \tgit checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_branch spam &&\n \ttest_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&\n \ttest_branch_upstream spam repo_c spam\n@@ -156,9 +174,11 @@ test_expect_success 'checkout of branch from a single remote succeeds #3' '\n \n test_expect_success 'checkout of branch from a single remote succeeds #4' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D eggs &&\n \n \tgit checkout eggs &&\n+\tstatus_uno_is_clean &&\n \ttest_branch eggs &&\n \ttest_cmp_rev refs/repo_d/eggs HEAD &&\n \ttest_branch_upstream eggs repo_d eggs\n@@ -166,32 +186,38 @@ test_expect_success 'checkout of branch from a single remote succeeds #4' '\n \n test_expect_success 'checkout of branch with a file having the same name fails' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \ttest_must_fail git checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/spam &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout of branch with a file in subdir having the same name fails' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \tmkdir sub &&\n \tmv spam sub/spam &&\n \ttest_must_fail git -C sub checkout spam &&\n+\tstatus_uno_is_clean &&\n \ttest_must_fail git rev-parse --verify refs/heads/spam &&\n \ttest_branch master\n '\n \n test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '\n \tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n \ttest_might_fail git branch -D spam &&\n \n \t>spam &&\n \tgit checkout spam -- &&\n+\tstatus_uno_is_clean &&\n \ttest_branch spam &&\n \ttest_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&\n \ttest_branch_upstream spam repo_c spam\n@@ -200,6 +226,7 @@ test_expect_success 'checkout <branch> -- succeeds, even if a file with the same\n test_expect_success 'loosely defined local base branch is reported correctly' '\n \n \tgit checkout master &&\n+\tstatus_uno_is_clean &&\n \tgit branch strict &&\n \tgit branch loose &&\n \tgit commit --allow-empty -m \"a bit more\" &&\n@@ -210,7 +237,9 @@ test_expect_success 'loosely defined local base branch is reported correctly' '\n \ttest_config branch.loose.merge master &&\n \n \tgit checkout strict | sed -e \"s/strict/BRANCHNAME/g\" >expect &&\n+\tstatus_uno_is_clean &&\n \tgit checkout loose | sed -e \"s/loose/BRANCHNAME/g\" >actual &&\n+\tstatus_uno_is_clean &&\n \n \ttest_cmp expect actual\n '\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349342","messageId":"20180605144049.26488-4-avarab@gmail.com","threadId":"48399","inReplyTo":"20180605144049.26488-1-avarab@gmail.com","subject":"[PATCH v7 3/8] checkout.c: introduce an *_INIT macro","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T14:40:44Z","receivedAt":"2018-06-05T14:41:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add an *_INIT macro for the tracking_name_data similar to what exists\nelsewhere in the codebase, e.g. OID_ARRAY_INIT in sha1-array.h. This\nwill make it more idiomatic in later changes to add more fields to the\nstruct & its initialization macro.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/checkout.c b/checkout.c\nindex bdefc888ba..80e430cda8 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -10,6 +10,8 @@ struct tracking_name_data {\n \tint unique;\n };\n \n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n+\n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n \tstruct tracking_name_data *cb = cb_data;\n@@ -32,7 +34,7 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \n const char *unique_tracking_name(const char *name, struct object_id *oid)\n {\n-\tstruct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };\n+\tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349343","messageId":"20180605144049.26488-3-avarab@gmail.com","threadId":"48399","inReplyTo":"20180605144049.26488-1-avarab@gmail.com","subject":"[PATCH v7 2/8] checkout.h: wrap the arguments to unique_tracking_name()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T14:40:43Z","receivedAt":"2018-06-05T14:41:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The line was too long already, and will be longer still when a later\nchange adds another argument.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.h | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/checkout.h b/checkout.h\nindex 9980711179..4cd4cd1c23 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -8,6 +8,7 @@\n  * tracking branch.  Return the name of the remote if such a branch\n  * exists, NULL otherwise.\n  */\n-extern const char *unique_tracking_name(const char *name, struct object_id *oid);\n+extern const char *unique_tracking_name(const char *name,\n+\t\t\t\t\tstruct object_id *oid);\n \n #endif /* CHECKOUT_H */\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349344","messageId":"20180605144049.26488-5-avarab@gmail.com","threadId":"48399","inReplyTo":"20180605144049.26488-1-avarab@gmail.com","subject":"[PATCH v7 4/8] checkout.c: change \"unique\" member to \"num_matches\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T14:40:45Z","receivedAt":"2018-06-05T14:41:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Internally track how many matches we find in the check_tracking_name()\ncallback. Nothing uses this now, but it will be made use of in a later\nchange.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n checkout.c | 8 ++++----\n 1 file changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/checkout.c b/checkout.c\nindex 80e430cda8..7662a39a62 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -7,10 +7,10 @@ struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n-\tint unique;\n+\tint num_matches;\n };\n \n-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 1 }\n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n@@ -23,9 +23,9 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\tfree(query.dst);\n \t\treturn 0;\n \t}\n+\tcb->num_matches++;\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n-\t\tcb->unique = 0;\n \t\treturn 0;\n \t}\n \tcb->dst_ref = query.dst;\n@@ -39,7 +39,7 @@ const char *unique_tracking_name(const char *name, struct object_id *oid)\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tfree(cb_data.src_ref);\n-\tif (cb_data.unique)\n+\tif (cb_data.num_matches == 1)\n \t\treturn cb_data.dst_ref;\n \tfree(cb_data.dst_ref);\n \treturn NULL;\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349345","messageId":"20180605144049.26488-7-avarab@gmail.com","threadId":"48399","inReplyTo":"20180605144049.26488-1-avarab@gmail.com","subject":"[PATCH v7 6/8] builtin/checkout.c: use \"ret\" variable for return","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T14:40:47Z","receivedAt":"2018-06-05T14:41:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"There is no point in doing this right now, but in later change the\n\"ret\" variable will be inspected. This change makes that meaningful\nchange smaller.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 8 +++++---\n 1 file changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 72457fb7d5..8c93c55cbc 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1265,8 +1265,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t}\n \n \tUNLEAK(opts);\n-\tif (opts.patch_mode || opts.pathspec.nr)\n-\t\treturn checkout_paths(&opts, new_branch_info.name);\n-\telse\n+\tif (opts.patch_mode || opts.pathspec.nr) {\n+\t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\t\treturn ret;\n+\t} else {\n \t\treturn checkout_branch(&opts, &new_branch_info);\n+\t}\n }\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349346","messageId":"20180605144049.26488-8-avarab@gmail.com","threadId":"48399","inReplyTo":"20180605144049.26488-1-avarab@gmail.com","subject":"[PATCH v7 7/8] checkout: add advice for ambiguous \"checkout <branch>\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T14:40:48Z","receivedAt":"2018-06-05T14:41:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"As the \"checkout\" documentation describes:\n\n    If <branch> is not found but there does exist a tracking branch in\n    exactly one remote (call it <remote>) with a matching name, treat\n    as equivalent to [...] <remote>/<branch.\n\nThis is a really useful feature. The problem is that when you add\nanother remote (e.g. a fork), git won't find a unique branch name\nanymore, and will instead print this unhelpful message:\n\n    $ git checkout master\n    error: pathspec 'master' did not match any file(s) known to git\n\nNow it will, on my git.git checkout, print:\n\n    $ ./git --exec-path=$PWD checkout master\n    error: pathspec 'master' did not match any file(s) known to git.\n    hint: 'master' matched more than one remote tracking branch.\n    hint: We found 26 remotes with a reference that matched. So we fell back\n    hint: on trying to resolve the argument as a path, but failed there too!\n    hint:\n    hint: If you meant to check out a remote tracking branch on, e.g. 'origin',\n    hint: you can do so by fully qualifying the name with the --track option:\n    hint:\n    hint:     git checkout --track origin/<name>\n\nNote that the \"error: pathspec[...]\" message is still printed. This is\nbecause whatever else checkout may have tried earlier, its final\nfallback is to try to resolve the argument as a path. E.g. in this\ncase:\n\n    $ ./git --exec-path=$PWD checkout master pu\n    error: pathspec 'master' did not match any file(s) known to git.\n    error: pathspec 'pu' did not match any file(s) known to git.\n\nThere we don't print the \"hint:\" implicitly due to earlier logic\naround the DWIM fallback. That fallback is only used if it looks like\nwe have one argument that might be a branch.\n\nI can't think of an intrinsic reason for why we couldn't in some\nfuture change skip printing the \"error: pathspec[...]\" error. However,\nto do so we'd need to pass something down to checkout_paths() to make\nit suppress printing an error on its own, and for us to be confident\nthat we're not silencing cases where those errors are meaningful.\n\nI don't think that's worth it since determining whether that's the\ncase could easily change due to future changes in the checkout logic.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt |  7 +++++++\n advice.c                 |  2 ++\n advice.h                 |  1 +\n builtin/checkout.c       | 13 +++++++++++++\n t/t2024-checkout-dwim.sh | 14 ++++++++++++++\n 5 files changed, 37 insertions(+)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex ab641bf5a9..dfc0413a84 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -344,6 +344,13 @@ advice.*::\n \t\tAdvice shown when you used linkgit:git-checkout[1] to\n \t\tmove to the detach HEAD state, to instruct how to create\n \t\ta local branch after the fact.\n+\tcheckoutAmbiguousRemoteBranchName::\n+\t\tAdvice shown when the argument to\n+\t\tlinkgit:git-checkout[1] ambiguously resolves to a\n+\t\tremote tracking branch on more than one remote in\n+\t\tsituations where an unambiguous argument would have\n+\t\totherwise caused a remote-tracking branch to be\n+\t\tchecked out.\n \tamWorkDir::\n \t\tAdvice that shows the location of the patch file when\n \t\tlinkgit:git-am[1] fails to apply it.\ndiff --git a/advice.c b/advice.c\nindex 370a56d054..75e7dede90 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -21,6 +21,7 @@ int advice_add_embedded_repo = 1;\n int advice_ignored_hook = 1;\n int advice_waiting_for_editor = 1;\n int advice_graft_file_deprecated = 1;\n+int advice_checkout_ambiguous_remote_branch_name = 1;\n \n static int advice_use_color = -1;\n static char advice_colors[][COLOR_MAXLEN] = {\n@@ -72,6 +73,7 @@ static struct {\n \t{ \"ignoredhook\", &advice_ignored_hook },\n \t{ \"waitingforeditor\", &advice_waiting_for_editor },\n \t{ \"graftfiledeprecated\", &advice_graft_file_deprecated },\n+\t{ \"checkoutambiguousremotebranchname\", &advice_checkout_ambiguous_remote_branch_name },\n \n \t/* make this an alias for backward compatibility */\n \t{ \"pushnonfastforward\", &advice_push_update_rejected }\ndiff --git a/advice.h b/advice.h\nindex 9f5064e82a..4d11d51d43 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -22,6 +22,7 @@ extern int advice_add_embedded_repo;\n extern int advice_ignored_hook;\n extern int advice_waiting_for_editor;\n extern int advice_graft_file_deprecated;\n+extern int advice_checkout_ambiguous_remote_branch_name;\n \n int git_default_advice_config(const char *var, const char *value);\n __attribute__((format (printf, 1, 2)))\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 8c93c55cbc..baa027455a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -22,6 +22,7 @@\n #include \"resolve-undo.h\"\n #include \"submodule-config.h\"\n #include \"submodule.h\"\n+#include \"advice.h\"\n \n static const char * const checkout_usage[] = {\n \tN_(\"git checkout [<options>] <branch>\"),\n@@ -1267,6 +1268,18 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tUNLEAK(opts);\n \tif (opts.patch_mode || opts.pathspec.nr) {\n \t\tint ret = checkout_paths(&opts, new_branch_info.name);\n+\t\tif (ret && dwim_remotes_matched > 1 &&\n+\t\t    advice_checkout_ambiguous_remote_branch_name)\n+\t\t\tadvise(_(\"'%s' matched more than one remote tracking branch.\\n\"\n+\t\t\t\t \"We found %d remotes with a reference that matched. So we fell back\\n\"\n+\t\t\t\t \"on trying to resolve the argument as a path, but failed there too!\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\t\t\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"    git checkout --track origin/<name>\"),\n+\t\t\t       argv[0],\n+\t\t\t       dwim_remotes_matched);\n \t\treturn ret;\n \t} else {\n \t\treturn checkout_branch(&opts, &new_branch_info);\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex ed32828105..fef263a858 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -76,6 +76,20 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '\n \ttest_branch master\n '\n \n+test_expect_success 'checkout of branch from multiple remotes fails with advice' '\n+\tgit checkout -B master &&\n+\ttest_might_fail git branch -D foo &&\n+\ttest_must_fail git checkout foo 2>stderr &&\n+\ttest_branch master &&\n+\tstatus_uno_is_clean &&\n+\ttest_i18ngrep \"^hint: \" stderr &&\n+\ttest_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \\\n+\t\tcheckout foo 2>stderr &&\n+\ttest_branch master &&\n+\tstatus_uno_is_clean &&\n+\ttest_i18ngrep ! \"^hint: \" stderr\n+'\n+\n test_expect_success 'checkout of branch from a single remote succeeds #1' '\n \tgit checkout -B master &&\n \ttest_might_fail git branch -D bar &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349347","messageId":"20180605144049.26488-9-avarab@gmail.com","threadId":"48399","inReplyTo":"20180605144049.26488-1-avarab@gmail.com","subject":"[PATCH v7 8/8] checkout & worktree: introduce checkout.defaultRemote","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T14:40:49Z","receivedAt":"2018-06-05T14:41:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Introduce a checkout.defaultRemote setting which can be used to\ndesignate a remote to prefer (via checkout.defaultRemote=origin) when\nrunning e.g. \"git checkout master\" to mean origin/master, even though\nthere's other remotes that have the \"master\" branch.\n\nI want this because it's very handy to use this workflow to checkout a\nrepository and create a topic branch, then get back to a \"master\" as\nretrieved from upstream:\n\n    (\n        cd /tmp &&\n        rm -rf tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git checkout master\n    )\n\nThat will output:\n\n    Branch 'master' set up to track remote branch 'master' from 'origin'.\n    Switched to a new branch 'master'\n\nBut as soon as a new remote is added (e.g. just to inspect something\nfrom someone else) the DWIMery goes away:\n\n    (\n        cd /tmp &&\n        rm -rf tbdiff &&\n        git clone git@github.com:trast/tbdiff.git &&\n        cd tbdiff &&\n        git branch -m topic &&\n        git remote add avar git@github.com:avar/tbdiff.git &&\n        git fetch avar &&\n        git checkout master\n    )\n\nWill output (without the advice output added earlier in this series):\n\n    error: pathspec 'master' did not match any file(s) known to git.\n\nThe new checkout.defaultRemote config allows me to say that whenever\nthat ambiguity comes up I'd like to prefer \"origin\", and it'll still\nwork as though the only remote I had was \"origin\".\n\nAlso adjust the advice.checkoutAmbiguousRemoteBranchName message to\nmention this new config setting to the user, the full output on my\ngit.git is now (the last paragraph is new):\n\n    $ ./git --exec-path=$PWD checkout master\n    error: pathspec 'master' did not match any file(s) known to git.\n    hint: 'master' matched more than one remote tracking branch.\n    hint: We found 26 remotes with a reference that matched. So we fell back\n    hint: on trying to resolve the argument as a path, but failed there too!\n    hint:\n    hint: If you meant to check out a remote tracking branch on, e.g. 'origin',\n    hint: you can do so by fully qualifying the name with the --track option:\n    hint:\n    hint:     git checkout --track origin/<name>\n    hint:\n    hint: If you'd like to always have checkouts of an ambiguous <name> prefer\n    hint: one remote, e.g. the 'origin' remote, consider setting\n    hint: checkout.defaultRemote=origin in your config.\n\nI considered splitting this into checkout.defaultRemote and\nworktree.defaultRemote, but it's probably less confusing to break our\nown rules that anything shared between config should live in core.*\nthan have two config settings, and I couldn't come up with a short\nname under core.* that made sense (core.defaultRemoteForCheckout?).\n\nSee also 70c9ac2f19 (\"DWIM \"git checkout frotz\" to \"git checkout -b\nfrotz origin/frotz\"\", 2009-10-18) which introduced this DWIM feature\nto begin with, and 4e85333197 (\"worktree: make add <path> <branch>\ndwim\", 2017-11-26) which added it to git-worktree.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/config.txt       | 21 ++++++++++++++++++++-\n Documentation/git-checkout.txt |  9 +++++++++\n Documentation/git-worktree.txt |  9 +++++++++\n builtin/checkout.c             | 12 +++++++++---\n checkout.c                     | 26 ++++++++++++++++++++++++--\n t/t2024-checkout-dwim.sh       | 18 +++++++++++++++++-\n t/t2025-worktree-add.sh        | 21 +++++++++++++++++++++\n 7 files changed, 109 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex dfc0413a84..aef2769211 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -350,7 +350,10 @@ advice.*::\n \t\tremote tracking branch on more than one remote in\n \t\tsituations where an unambiguous argument would have\n \t\totherwise caused a remote-tracking branch to be\n-\t\tchecked out.\n+\t\tchecked out. See the `checkout.defaultRemote`\n+\t\tconfiguration variable for how to set a given remote\n+\t\tto used by default in some situations where this\n+\t\tadvice would be printed.\n \tamWorkDir::\n \t\tAdvice that shows the location of the patch file when\n \t\tlinkgit:git-am[1] fails to apply it.\n@@ -1105,6 +1108,22 @@ browser.<tool>.path::\n \tbrowse HTML help (see `-w` option in linkgit:git-help[1]) or a\n \tworking repository in gitweb (see linkgit:git-instaweb[1]).\n \n+checkout.defaultRemote::\n+\tWhen you run 'git checkout <something>' and only have one\n+\tremote, it may implicitly fall back on checking out and\n+\ttracking e.g. 'origin/<something>'. This stops working as soon\n+\tas you have more than one remote with a '<something>'\n+\treference. This setting allows for setting the name of a\n+\tpreferred remote that should always win when it comes to\n+\tdisambiguation. The typical use-case is to set this to\n+\t`origin`.\n++\n+Currently this is used by linkgit:git-checkout[1] when 'git checkout\n+<something>' will checkout the '<something>' branch on another remote,\n+and by linkgit:git-worktree[1] when 'git worktree add' refers to a\n+remote branch. This setting might be used for other checkout-like\n+commands or functionality in the future.\n+\n clean.requireForce::\n \tA boolean to make git-clean do nothing unless given -f,\n \t-i or -n.   Defaults to true.\ndiff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt\nindex ca5fc9c798..9db02928c4 100644\n--- a/Documentation/git-checkout.txt\n+++ b/Documentation/git-checkout.txt\n@@ -38,6 +38,15 @@ equivalent to\n $ git checkout -b <branch> --track <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n You could omit <branch>, in which case the command degenerates to\n \"check out the current branch\", which is a glorified no-op with\n rather expensive side-effects to show only the tracking information,\ndiff --git a/Documentation/git-worktree.txt b/Documentation/git-worktree.txt\nindex afc6576a14..9c26be40f4 100644\n--- a/Documentation/git-worktree.txt\n+++ b/Documentation/git-worktree.txt\n@@ -60,6 +60,15 @@ with a matching name, treat as equivalent to:\n $ git worktree add --track -b <branch> <path> <remote>/<branch>\n ------------\n +\n+If the branch exists in multiple remotes and one of them is named by\n+the `checkout.defaultRemote` configuration variable, we'll use that\n+one for the purposes of disambiguation, even if the `<branch>` isn't\n+unique across all remotes. Set it to\n+e.g. `checkout.defaultRemote=origin` to always checkout remote\n+branches from there if `<branch>` is ambiguous but exists on the\n+'origin' remote. See also `checkout.defaultRemote` in\n+linkgit:git-config[1].\n++\n If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,\n then, as a convenience, the new worktree is associated with a branch\n (call it `<branch>`) named after `$(basename <path>)`.  If `<branch>`\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex baa027455a..5b357e922a 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -912,8 +912,10 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t *   (b) If <something> is _not_ a commit, either \"--\" is present\n \t *       or <something> is not a path, no -t or -b was given, and\n \t *       and there is a tracking branch whose name is <something>\n-\t *       in one and only one remote, then this is a short-hand to\n-\t *       fork local <something> from that remote-tracking branch.\n+\t *       in one and only one remote (or if the branch exists on the\n+\t *       remote named in checkout.defaultRemote), then this is a\n+\t *       short-hand to fork local <something> from that\n+\t *       remote-tracking branch.\n \t *\n \t *   (c) Otherwise, if \"--\" is present, treat it like case (1).\n \t *\n@@ -1277,7 +1279,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\t\t \"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n \t\t\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n \t\t\t\t \"\\n\"\n-\t\t\t\t \"    git checkout --track origin/<name>\"),\n+\t\t\t\t \"    git checkout --track origin/<name>\\n\"\n+\t\t\t\t \"\\n\"\n+\t\t\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t\t\t \"checkout.defaultRemote=origin in your config.\"),\n \t\t\t       argv[0],\n \t\t\t       dwim_remotes_matched);\n \t\treturn ret;\ndiff --git a/checkout.c b/checkout.c\nindex ee3a7e9c05..c72e9f9773 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -2,15 +2,19 @@\n #include \"remote.h\"\n #include \"refspec.h\"\n #include \"checkout.h\"\n+#include \"config.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n \tchar *dst_ref;\n \tstruct object_id *dst_oid;\n \tint num_matches;\n+\tconst char *default_remote;\n+\tchar *default_dst_ref;\n+\tstruct object_id *default_dst_oid;\n };\n \n-#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0 }\n+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0, NULL, NULL, NULL }\n \n static int check_tracking_name(struct remote *remote, void *cb_data)\n {\n@@ -24,6 +28,12 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\treturn 0;\n \t}\n \tcb->num_matches++;\n+\tif (cb->default_remote && !strcmp(remote->name, cb->default_remote)) {\n+\t\tstruct object_id *dst = xmalloc(sizeof(*cb->default_dst_oid));\n+\t\tcb->default_dst_ref = xstrdup(query.dst);\n+\t\toidcpy(dst, cb->dst_oid);\n+\t\tcb->default_dst_oid = dst;\n+\t}\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -36,14 +46,26 @@ const char *unique_tracking_name(const char *name, struct object_id *oid,\n \t\t\t\t int *dwim_remotes_matched)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n+\tconst char *default_remote = NULL;\n+\tif (!git_config_get_string_const(\"checkout.defaultremote\", &default_remote))\n+\t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\n \tfree(cb_data.src_ref);\n-\tif (cb_data.num_matches == 1)\n+\tfree((char *)default_remote);\n+\tif (cb_data.num_matches == 1) {\n+\t\tfree(cb_data.default_dst_ref);\n+\t\tfree(cb_data.default_dst_oid);\n \t\treturn cb_data.dst_ref;\n+\t}\n \tfree(cb_data.dst_ref);\n+\tif (cb_data.default_dst_ref) {\n+\t\toidcpy(oid, cb_data.default_dst_oid);\n+\t\tfree(cb_data.default_dst_oid);\n+\t\treturn cb_data.default_dst_ref;\n+\t}\n \treturn NULL;\n }\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex fef263a858..9f17ac92f1 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -87,7 +87,23 @@ test_expect_success 'checkout of branch from multiple remotes fails with advice'\n \t\tcheckout foo 2>stderr &&\n \ttest_branch master &&\n \tstatus_uno_is_clean &&\n-\ttest_i18ngrep ! \"^hint: \" stderr\n+\ttest_i18ngrep ! \"^hint: \" stderr &&\n+\t# Make sure the likes of checkout -p do not print this hint\n+\tgit checkout -p foo 2>stderr &&\n+\ttest_i18ngrep ! \"^hint: \" stderr &&\n+\tstatus_uno_is_clean\n+'\n+\n+test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.defaultRemote #1' '\n+\tgit checkout -B master &&\n+\tstatus_uno_is_clean &&\n+\ttest_might_fail git branch -D foo &&\n+\n+\tgit -c checkout.defaultRemote=repo_a checkout foo &&\n+\tstatus_uno_is_clean &&\n+\ttest_branch foo &&\n+\ttest_cmp_rev remotes/repo_a/foo HEAD &&\n+\ttest_branch_upstream foo repo_a foo\n '\n \n test_expect_success 'checkout of branch from a single remote succeeds #1' '\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex d2e49f7632..be6e093142 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -402,6 +402,27 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n \t)\n '\n \n+test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n+\ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n+\tsetup_remote_repo repo_upstream repo_dwim &&\n+\tgit init repo_dwim &&\n+\t(\n+\t\tcd repo_dwim &&\n+\t\tgit remote add repo_upstream2 ../repo_upstream &&\n+\t\tgit fetch repo_upstream2 &&\n+\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n+\t\t>status.expect &&\n+\t\tgit status -uno --porcelain >status.actual &&\n+\t\ttest_cmp status.expect status.actual\n+\t) &&\n+\t(\n+\t\tcd foo &&\n+\t\ttest_branch_upstream foo repo_upstream foo &&\n+\t\ttest_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo\n+\t)\n+'\n+\n test_expect_success 'git worktree add does not match remote' '\n \ttest_when_finished rm -rf repo_a repo_b foo &&\n \tsetup_remote_repo repo_a repo_b &&\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349348","messageId":"20180605144049.26488-6-avarab@gmail.com","threadId":"48399","inReplyTo":"20180605144049.26488-1-avarab@gmail.com","subject":"[PATCH v7 5/8] checkout: pass the \"num_matches\" up to callers","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-06-05T14:40:46Z","receivedAt":"2018-06-05T14:41:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Pass the previously added \"num_matches\" struct value up to the callers\nof unique_tracking_name(). This will allow callers to optionally print\nbetter error messages in a later change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 10 +++++++---\n builtin/worktree.c |  4 ++--\n checkout.c         |  5 ++++-\n checkout.h         |  3 ++-\n 4 files changed, 15 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 2e1d2376d2..72457fb7d5 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -878,7 +878,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\t\tint dwim_new_local_branch_ok,\n \t\t\t\tstruct branch_info *new_branch_info,\n \t\t\t\tstruct checkout_opts *opts,\n-\t\t\t\tstruct object_id *rev)\n+\t\t\t\tstruct object_id *rev,\n+\t\t\t\tint *dwim_remotes_matched)\n {\n \tstruct tree **source_tree = &opts->source_tree;\n \tconst char **new_branch = &opts->new_branch;\n@@ -972,7 +973,8 @@ static int parse_branchname_arg(int argc, const char **argv,\n \t\t\trecover_with_dwim = 0;\n \n \t\tif (recover_with_dwim) {\n-\t\t\tconst char *remote = unique_tracking_name(arg, rev);\n+\t\t\tconst char *remote = unique_tracking_name(arg, rev,\n+\t\t\t\t\t\t\t\t  dwim_remotes_matched);\n \t\t\tif (remote) {\n \t\t\t\t*new_branch = arg;\n \t\t\t\targ = remote;\n@@ -1109,6 +1111,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \tstruct branch_info new_branch_info;\n \tchar *conflict_style = NULL;\n \tint dwim_new_local_branch = 1;\n+\tint dwim_remotes_matched = 0;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&opts.quiet, N_(\"suppress progress reporting\")),\n \t\tOPT_STRING('b', NULL, &opts.new_branch, N_(\"branch\"),\n@@ -1219,7 +1222,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t\t\topts.track == BRANCH_TRACK_UNSPECIFIED &&\n \t\t\t!opts.new_branch;\n \t\tint n = parse_branchname_arg(argc, argv, dwim_ok,\n-\t\t\t\t\t     &new_branch_info, &opts, &rev);\n+\t\t\t\t\t     &new_branch_info, &opts, &rev,\n+\t\t\t\t\t     &dwim_remotes_matched);\n \t\targv += n;\n \t\targc -= n;\n \t}\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 5c7d2bb180..a763dbdccb 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -412,7 +412,7 @@ static const char *dwim_branch(const char *path, const char **new_branch)\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n \t\tconst char *remote =\n-\t\t\tunique_tracking_name(*new_branch, &oid);\n+\t\t\tunique_tracking_name(*new_branch, &oid, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -484,7 +484,7 @@ static int add(int ac, const char **av, const char *prefix)\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 7662a39a62..ee3a7e9c05 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -32,12 +32,15 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \treturn 0;\n }\n \n-const char *unique_tracking_name(const char *name, struct object_id *oid)\n+const char *unique_tracking_name(const char *name, struct object_id *oid,\n+\t\t\t\t int *dwim_remotes_matched)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n \tfor_each_remote(check_tracking_name, &cb_data);\n+\tif (dwim_remotes_matched)\n+\t\t*dwim_remotes_matched = cb_data.num_matches;\n \tfree(cb_data.src_ref);\n \tif (cb_data.num_matches == 1)\n \t\treturn cb_data.dst_ref;\ndiff --git a/checkout.h b/checkout.h\nindex 4cd4cd1c23..6b2073310c 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -9,6 +9,7 @@\n  * exists, NULL otherwise.\n  */\n extern const char *unique_tracking_name(const char *name,\n-\t\t\t\t\tstruct object_id *oid);\n+\t\t\t\t\tstruct object_id *oid,\n+\t\t\t\t\tint *dwim_remotes_matched);\n \n #endif /* CHECKOUT_H */\n-- \n2.17.0.290.gded63e768a\n\n"},{"id":"349358","messageId":"20180605154501.13502-1-szeder.dev@gmail.com","threadId":"48399","inReplyTo":"20180605144049.26488-2-avarab@gmail.com","subject":"Re: [PATCH v7 1/8] checkout tests: index should be clean after dwim checkout","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-06-05T15:45:01Z","receivedAt":"2018-06-05T15:45:19Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"> diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\n> index 3e5ac81bd2..ed32828105 100755\n> --- a/t/t2024-checkout-dwim.sh\n> +++ b/t/t2024-checkout-dwim.sh\n> @@ -23,6 +23,12 @@ test_branch_upstream () {\n>  \ttest_cmp expect.upstream actual.upstream\n>  }\n>  \n> +status_uno_is_clean() {\n> +\t>status.expect &&\n> +\tgit status -uno --porcelain >status.actual &&\n> +\ttest_cmp status.expect status.actual\n\nThis function could be written a tad simpler as:\n\n  git status -uno --porcelain >status.actual &&\n  test_must_be_empty status.actual\n\n"},{"id":"353735","messageId":"20180727174811.27360-1-avarab@gmail.com","threadId":"48399","inReplyTo":"20180605154501.13502-1-szeder.dev@gmail.com","subject":"[PATCH] tests: make use of the test_must_be_empty function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2018-07-27T17:48:11Z","receivedAt":"2018-07-27T17:48:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change various tests that use an idiom of the form:\n\n    >expect &&\n    test_cmp expect actual\n\nTo instead use:\n\n    test_must_be_empty actual\n\nThe test_must_be_empty() wrapper was introduced in ca8d148daf (\"test:\ntest_must_be_empty helper\", 2013-06-09). Many of these tests have been\nadded after that time. This was mostly found with, and manually pruned\nfrom:\n\n    git grep '^\\s+>.*expect.* &&$' t\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nAs promised in\nhttps://public-inbox.org/git/CACBZZX4yG5h5kk4NFQz_NzAweMa+Nh3H-39OHtcH4XWsA6FGpg@mail.gmail.com/\nhere's a series to refactor various test_cmp invocations to use\ntest_must_be_empty.\n\nThis patch is on top of \"next\" since one of the things being fixed up\nis a test in my in-flight ab/checkout-default-remote series. It also\napplies cleanly to \"pu\", and the only conflicts with \"master\" are due\nto that series of mine.\n\n t/t0008-ignores.sh                   |  9 ++----\n t/t0030-stripspace.sh                | 17 +++++-----\n t/t0300-credentials.sh               |  3 +-\n t/t1011-read-tree-sparse-checkout.sh |  3 +-\n t/t1306-xdg-files.sh                 |  6 ++--\n t/t1403-show-ref.sh                  | 46 ++++++++++------------------\n t/t1507-rev-parse-upstream.sh        |  3 +-\n t/t2024-checkout-dwim.sh             |  3 +-\n t/t2025-worktree-add.sh              |  3 +-\n t/t2202-add-addremove.sh             |  3 +-\n t/t3001-ls-files-others-exclude.sh   | 15 +++------\n t/t3070-wildmatch.sh                 |  3 +-\n t/t3201-branch-contains.sh           | 15 +++------\n t/t3700-add.sh                       |  3 +-\n t/t3910-mac-os-precompose.sh         |  3 +-\n t/t4010-diff-pathspec.sh             |  3 +-\n t/t4015-diff-whitespace.sh           | 17 +++++-----\n t/t4039-diff-assume-unchanged.sh     |  3 +-\n t/t4200-rerere.sh                    |  9 ++----\n t/t4202-log.sh                       |  6 ++--\n t/t4210-log-i18n.sh                  |  6 ++--\n t/t5310-pack-bitmaps.sh              |  9 ++----\n t/t5313-pack-bounds-checks.sh        |  3 +-\n t/t5505-remote.sh                    |  6 ++--\n t/t5512-ls-remote.sh                 |  6 ++--\n t/t5514-fetch-multiple.sh            |  3 +-\n t/t5533-push-cas.sh                  |  6 ++--\n t/t5612-clone-refspec.sh             |  9 ++----\n t/t6000-rev-list-misc.sh             |  3 +-\n t/t6009-rev-list-parent.sh           |  6 ++--\n t/t6018-rev-list-glob.sh             | 12 +++-----\n t/t6019-rev-list-ancestry-path.sh    |  3 +-\n t/t6022-merge-rename.sh              |  3 +-\n t/t6060-merge-index.sh               |  3 +-\n t/t6300-for-each-ref.sh              |  3 +-\n t/t7004-tag.sh                       | 22 +++++--------\n t/t7006-pager.sh                     |  3 +-\n t/t7030-verify-tag.sh                |  3 +-\n t/t7063-status-untracked-cache.sh    |  3 +-\n t/t7102-reset.sh                     |  6 ++--\n t/t7106-reset-unborn-branch.sh       |  9 ++----\n t/t7401-submodule-summary.sh         |  3 +-\n t/t7502-commit.sh                    |  3 +-\n t/t7610-mergetool.sh                 |  3 +-\n t/t7810-grep.sh                      |  3 +-\n t/t9001-send-email.sh                |  3 +-\n t/t9300-fast-import.sh               |  7 ++---\n 47 files changed, 113 insertions(+), 209 deletions(-)\n\ndiff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh\nindex c03f155a35..1744cee5e9 100755\n--- a/t/t0008-ignores.sh\n+++ b/t/t0008-ignores.sh\n@@ -807,10 +807,9 @@ test_expect_success 'trailing whitespace is ignored' '\n \tcat >expect <<EOF &&\n whitespace/untracked\n EOF\n-\t: >err.expect &&\n \tgit ls-files -o -X ignore whitespace >actual 2>err &&\n \ttest_cmp expect actual &&\n-\ttest_cmp err.expect err\n+\ttest_must_be_empty err\n '\n \n test_expect_success !MINGW 'quoting allows trailing whitespace' '\n@@ -820,10 +819,9 @@ test_expect_success !MINGW 'quoting allows trailing whitespace' '\n \t>whitespace/untracked &&\n \techo \"whitespace/trailing\\\\ \\\\ \" >ignore &&\n \techo whitespace/untracked >expect &&\n-\t: >err.expect &&\n \tgit ls-files -o -X ignore whitespace >actual 2>err &&\n \ttest_cmp expect actual &&\n-\ttest_cmp err.expect err\n+\ttest_must_be_empty err\n '\n \n test_expect_success !MINGW,!CYGWIN 'correct handling of backslashes' '\n@@ -845,10 +843,9 @@ test_expect_success !MINGW,!CYGWIN 'correct handling of backslashes' '\n \twhitespace/trailing 6 \\\\a\\\\Z\n \tEOF\n \techo whitespace/untracked >expect &&\n-\t>err.expect &&\n \tgit ls-files -o -X ignore whitespace >actual 2>err &&\n \ttest_cmp expect actual &&\n-\ttest_cmp err.expect err\n+\ttest_must_be_empty err\n '\n \n test_expect_success 'info/exclude trumps core.excludesfile' '\ndiff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh\nindex bbf3e39e3d..b77948c618 100755\n--- a/t/t0030-stripspace.sh\n+++ b/t/t0030-stripspace.sh\n@@ -110,31 +110,30 @@ test_expect_success \\\n \n test_expect_success \\\n     'only consecutive blank lines should be completely removed' '\n-    > expect &&\n \n     printf \"\\n\" | git stripspace >actual &&\n-    test_cmp expect actual &&\n+    test_must_be_empty actual &&\n \n     printf \"\\n\\n\\n\" | git stripspace >actual &&\n-    test_cmp expect actual &&\n+    test_must_be_empty actual &&\n \n     printf \"$sss\\n$sss\\n$sss\\n\" | git stripspace >actual &&\n-    test_cmp expect actual &&\n+    test_must_be_empty actual &&\n \n     printf \"$sss$sss\\n$sss\\n\\n\" | git stripspace >actual &&\n-    test_cmp expect actual &&\n+    test_must_be_empty actual &&\n \n     printf \"\\n$sss\\n$sss$sss\\n\" | git stripspace >actual &&\n-    test_cmp expect actual &&\n+    test_must_be_empty actual &&\n \n     printf \"$sss$sss$sss$sss\\n\\n\\n\" | git stripspace >actual &&\n-    test_cmp expect actual &&\n+    test_must_be_empty actual &&\n \n     printf \"\\n$sss$sss$sss$sss\\n\\n\" | git stripspace >actual &&\n-    test_cmp expect actual &&\n+    test_must_be_empty actual &&\n \n     printf \"\\n\\n$sss$sss$sss$sss\\n\" | git stripspace >actual &&\n-    test_cmp expect actual\n+    test_must_be_empty actual\n '\n \n test_expect_success \\\ndiff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh\nindex 03bd31e9f2..82eaaea0f4 100755\n--- a/t/t0300-credentials.sh\n+++ b/t/t0300-credentials.sh\n@@ -294,8 +294,7 @@ test_expect_success 'helpers can abort the process' '\n \t\t-c credential.helper=\"!f() { echo quit=1; }; f\" \\\n \t\t-c credential.helper=\"verbatim foo bar\" \\\n \t\tcredential fill >stdout &&\n-\t>expect &&\n-\ttest_cmp expect stdout\n+\ttest_must_be_empty stdout\n '\n \n test_expect_success 'empty helper spec resets helper list' '\ndiff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh\nindex 0c6f48f302..ba71b159ba 100755\n--- a/t/t1011-read-tree-sparse-checkout.sh\n+++ b/t/t1011-read-tree-sparse-checkout.sh\n@@ -227,12 +227,11 @@ test_expect_success 'index removal and worktree narrowing at the same time' '\n '\n \n test_expect_success 'read-tree --reset removes outside worktree' '\n-\t>empty &&\n \techo init.t >.git/info/sparse-checkout &&\n \tgit checkout -f top &&\n \tgit reset --hard removed &&\n \tgit ls-files sub/added >result &&\n-\ttest_cmp empty result\n+\ttest_must_be_empty result\n '\n \n test_expect_success 'print errors when failed to update worktree' '\ndiff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh\nindex 8b14ab187c..21e139a313 100755\n--- a/t/t1306-xdg-files.sh\n+++ b/t/t1306-xdg-files.sh\n@@ -114,11 +114,10 @@ test_expect_success 'Exclusion in a non-XDG global ignore file' '\n '\n \n test_expect_success 'Checking XDG ignore file when HOME is unset' '\n-\t>expected &&\n \t(sane_unset HOME &&\n \t git config --unset core.excludesfile &&\n \t git ls-files --exclude-standard --ignored >actual) &&\n-\ttest_cmp expected actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'Checking attributes in the XDG attributes file' '\n@@ -132,10 +131,9 @@ test_expect_success 'Checking attributes in the XDG attributes file' '\n '\n \n test_expect_success 'Checking XDG attributes when HOME is unset' '\n-\t>expected &&\n \t(sane_unset HOME &&\n \t git check-attr -a f >actual) &&\n-\ttest_cmp expected actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success '$XDG_CONFIG_HOME overrides $HOME/.config/git/attributes' '\ndiff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh\nindex 30354fd26c..5d955c3bff 100755\n--- a/t/t1403-show-ref.sh\n+++ b/t/t1403-show-ref.sh\n@@ -26,26 +26,22 @@ test_expect_success 'show-ref' '\n \tgit show-ref refs/tags/A >actual &&\n \ttest_cmp expect actual &&\n \n-\t>expect &&\n-\n \ttest_must_fail git show-ref D >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'show-ref -q' '\n-\t>expect &&\n-\n \tgit show-ref -q A >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \tgit show-ref -q tags/A >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \tgit show-ref -q refs/tags/A >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \ttest_must_fail git show-ref -q D >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'show-ref --verify' '\n@@ -54,32 +50,28 @@ test_expect_success 'show-ref --verify' '\n \tgit show-ref --verify refs/tags/A >actual &&\n \ttest_cmp expect actual &&\n \n-\t>expect &&\n-\n \ttest_must_fail git show-ref --verify A >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \ttest_must_fail git show-ref --verify tags/A >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \ttest_must_fail git show-ref --verify D >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'show-ref --verify -q' '\n-\t>expect &&\n-\n \tgit show-ref --verify -q refs/tags/A >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \ttest_must_fail git show-ref --verify -q A >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \ttest_must_fail git show-ref --verify -q tags/A >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \ttest_must_fail git show-ref --verify -q D >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'show-ref -d' '\n@@ -113,19 +105,17 @@ test_expect_success 'show-ref -d' '\n \tgit show-ref -d --verify refs/heads/master >actual &&\n \ttest_cmp expect actual &&\n \n-\t>expect &&\n-\n \ttest_must_fail git show-ref -d --verify master >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \ttest_must_fail git show-ref -d --verify heads/master >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \ttest_must_fail git show-ref --verify -d A C >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \ttest_must_fail git show-ref --verify -d tags/A tags/C >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n \n '\n \n@@ -178,10 +168,8 @@ test_expect_success 'show-ref --verify HEAD' '\n \tgit show-ref --verify HEAD >actual &&\n \ttest_cmp expect actual &&\n \n-\t>expect &&\n-\n \tgit show-ref --verify -q HEAD >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'show-ref --verify with dangling ref' '\ndiff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh\nindex 349f6e10af..fa3e499641 100755\n--- a/t/t1507-rev-parse-upstream.sh\n+++ b/t/t1507-rev-parse-upstream.sh\n@@ -138,8 +138,7 @@ test_expect_success 'branch -d other@{u}' '\n \tgit checkout -t -b other master &&\n \tgit branch -d @{u} &&\n \tgit for-each-ref refs/heads/master >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'checkout other@{u}' '\ndiff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh\nindex 26dc3f1fc0..f79dfbbdd6 100755\n--- a/t/t2024-checkout-dwim.sh\n+++ b/t/t2024-checkout-dwim.sh\n@@ -24,9 +24,8 @@ test_branch_upstream () {\n }\n \n status_uno_is_clean () {\n-\t>status.expect &&\n \tgit status -uno --porcelain >status.actual &&\n-\ttest_cmp status.expect status.actual\n+\ttest_must_be_empty status.actual\n }\n \n test_expect_success 'setup' '\ndiff --git a/t/t2025-worktree-add.sh b/t/t2025-worktree-add.sh\nindex be6e093142..166942c1bd 100755\n--- a/t/t2025-worktree-add.sh\n+++ b/t/t2025-worktree-add.sh\n@@ -412,9 +412,8 @@ test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \t\tgit fetch repo_upstream2 &&\n \t\ttest_must_fail git worktree add ../foo foo &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n-\t\t>status.expect &&\n \t\tgit status -uno --porcelain >status.actual &&\n-\t\ttest_cmp status.expect status.actual\n+\t\ttest_must_be_empty status.actual\n \t) &&\n \t(\n \t\tcd foo &&\ndiff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh\nindex 17744e8c57..9ee659098c 100755\n--- a/t/t2202-add-addremove.sh\n+++ b/t/t2202-add-addremove.sh\n@@ -48,8 +48,7 @@ test_expect_success 'Just \"git add\" is a no-op' '\n \t>will-not-be-added &&\n \tgit add &&\n \tgit diff-index --name-status --cached HEAD >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_done\ndiff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh\nindex 3fc484e8c3..3b47647ed5 100755\n--- a/t/t3001-ls-files-others-exclude.sh\n+++ b/t/t3001-ls-files-others-exclude.sh\n@@ -210,8 +210,7 @@ test_expect_success 'subdirectory ignore (toplevel)' '\n \t\tcd top &&\n \t\tgit ls-files -o --exclude-standard\n \t) >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'subdirectory ignore (l1/l2)' '\n@@ -219,8 +218,7 @@ test_expect_success 'subdirectory ignore (l1/l2)' '\n \t\tcd top/l1/l2 &&\n \t\tgit ls-files -o --exclude-standard\n \t) >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'subdirectory ignore (l1)' '\n@@ -228,8 +226,7 @@ test_expect_success 'subdirectory ignore (l1)' '\n \t\tcd top/l1 &&\n \t\tgit ls-files -o --exclude-standard\n \t) >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'show/hide empty ignored directory (setup)' '\n@@ -251,8 +248,7 @@ test_expect_success 'hide empty ignored directory with --no-empty-directory' '\n \t\tcd top &&\n \t\tgit ls-files -o -i --exclude l1 --directory --no-empty-directory\n \t) >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'show/hide empty ignored sub-directory (setup)' '\n@@ -277,8 +273,7 @@ test_expect_success 'hide empty ignored sub-directory with --no-empty-directory'\n \t\tcd top &&\n \t\tgit ls-files -o -i --exclude l1 --directory --no-empty-directory\n \t) >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'pattern matches prefix completely' '\ndiff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh\nindex dce102130f..46aca0af10 100755\n--- a/t/t3070-wildmatch.sh\n+++ b/t/t3070-wildmatch.sh\n@@ -101,8 +101,7 @@ match_with_ls_files() {\n \n \tmatch_stdout_stderr_cmp=\"\n \t\ttr -d '\\0' <actual.raw >actual &&\n-\t\t>expect.err &&\n-\t\ttest_cmp expect.err actual.err &&\n+\t\ttest_must_be_empty actual.err &&\n \t\ttest_cmp expect actual\"\n \n \tif test \"$match_expect\" = 'E'\ndiff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh\nindex 0ef1b6fdcc..0ea4fc4694 100755\n--- a/t/t3201-branch-contains.sh\n+++ b/t/t3201-branch-contains.sh\n@@ -48,16 +48,14 @@ test_expect_success 'branch --contains master' '\n test_expect_success 'branch --no-contains=master' '\n \n \tgit branch --no-contains=master >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n \n '\n \n test_expect_success 'branch --no-contains master' '\n \n \tgit branch --no-contains master >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n \n '\n \n@@ -94,8 +92,7 @@ test_expect_success 'branch --contains with pattern implies --list' '\n test_expect_success 'branch --no-contains with pattern implies --list' '\n \n \tgit branch --no-contains=master master >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n \n '\n \n@@ -123,8 +120,7 @@ test_expect_success 'branch --merged with pattern implies --list' '\n test_expect_success 'side: branch --no-merged' '\n \n \tgit branch --no-merged >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n \n '\n \n@@ -152,8 +148,7 @@ test_expect_success 'master: branch --no-merged' '\n test_expect_success 'branch --no-merged with pattern implies --list' '\n \n \tgit branch --no-merged=master master >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n \n '\n \ndiff --git a/t/t3700-add.sh b/t/t3700-add.sh\nindex 618750167a..37729ba258 100755\n--- a/t/t3700-add.sh\n+++ b/t/t3700-add.sh\n@@ -188,9 +188,8 @@ test_expect_success 'git add --refresh with pathspec' '\n \tgit add foo bar baz && H=$(git rev-parse :foo) && git rm -f foo &&\n \techo \"100644 $H 3\tfoo\" | git update-index --index-info &&\n \ttest-tool chmtime -60 bar baz &&\n-\t>expect &&\n \tgit add --refresh bar >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \tgit diff-files --name-only >actual &&\n \t! grep bar actual&&\ndiff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh\nindex 26dd5b7f78..54ce19e353 100755\n--- a/t/t3910-mac-os-precompose.sh\n+++ b/t/t3910-mac-os-precompose.sh\n@@ -187,9 +187,8 @@ test_expect_failure 'handle existing decomposed filenames' '\n \techo content >\"verbatim.$Adiarnfd\" &&\n \tgit -c core.precomposeunicode=false add \"verbatim.$Adiarnfd\" &&\n \tgit commit -m \"existing decomposed file\" &&\n-\t>expect &&\n \tgit ls-files --exclude-standard -o \"verbatim*\" >untracked &&\n-\ttest_cmp expect untracked\n+\ttest_must_be_empty untracked\n '\n \n # Test if the global core.precomposeunicode stops autosensing\ndiff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh\nindex b7f25071cf..281f8fad0c 100755\n--- a/t/t4010-diff-pathspec.sh\n+++ b/t/t4010-diff-pathspec.sh\n@@ -74,8 +74,7 @@ test_expect_success 'diff-tree pathspec' '\n \ttree2=$(git write-tree) &&\n \techo \"$tree2\" &&\n \tgit diff-tree -r --name-only $tree $tree2 -- pa path1/a >current &&\n-\t>expected &&\n-\ttest_cmp expected current\n+\ttest_must_be_empty current\n '\n \n test_expect_success 'diff-tree with wildcard shows dir also matches' '\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex 41facf7abf..62ed0232d7 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -93,21 +93,20 @@ test_expect_success 'another test, without options' '\n \tgit diff >out &&\n \ttest_cmp expect out &&\n \n-\t>expect &&\n \tgit diff -w >out &&\n-\ttest_cmp expect out &&\n+\ttest_must_be_empty out &&\n \n \tgit diff -w -b >out &&\n-\ttest_cmp expect out &&\n+\ttest_must_be_empty out &&\n \n \tgit diff -w --ignore-space-at-eol >out &&\n-\ttest_cmp expect out &&\n+\ttest_must_be_empty out &&\n \n \tgit diff -w -b --ignore-space-at-eol >out &&\n-\ttest_cmp expect out &&\n+\ttest_must_be_empty out &&\n \n \tgit diff -w --ignore-cr-at-eol >out &&\n-\ttest_cmp expect out &&\n+\ttest_must_be_empty out &&\n \n \ttr \"Q_\" \"\\015 \" <<-\\EOF >expect &&\n \tdiff --git a/x b/x\n@@ -182,8 +181,7 @@ test_expect_success 'ignore-blank-lines: only new lines' '\n \ttest_seq 5 | sed \"/3/i\\\\\n \" >x &&\n \tgit diff --ignore-blank-lines >out &&\n-\t>expect &&\n-\ttest_cmp expect out\n+\ttest_must_be_empty out\n '\n \n test_expect_success 'ignore-blank-lines: only new lines with space' '\n@@ -192,8 +190,7 @@ test_expect_success 'ignore-blank-lines: only new lines with space' '\n \ttest_seq 5 | sed \"/3/i\\\\\n  \" >x &&\n \tgit diff -w --ignore-blank-lines >out &&\n-\t>expect &&\n-\ttest_cmp expect out\n+\ttest_must_be_empty out\n '\n \n test_expect_success 'ignore-blank-lines: after change' '\ndiff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh\nindex 23c0e357a7..53ac44b0f0 100755\n--- a/t/t4039-diff-assume-unchanged.sh\n+++ b/t/t4039-diff-assume-unchanged.sh\n@@ -34,9 +34,8 @@ test_expect_success POSIXPERM 'find-copies-harder is not confused by mode bits'\n \tgit add exec &&\n \tgit commit -m exec &&\n \tgit update-index --assume-unchanged exec &&\n-\t>expect &&\n \tgit diff-files --find-copies-harder -- exec >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_done\ndiff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh\nindex 8417e5a4b1..65da74c766 100755\n--- a/t/t4200-rerere.sh\n+++ b/t/t4200-rerere.sh\n@@ -267,8 +267,7 @@ rerere_gc_custom_expiry_test () {\n \t\tgit -c \"gc.rerereresolved=$right_now\" \\\n \t\t    -c \"gc.rerereunresolved=$right_now\" rerere gc &&\n \t\tfind .git/rr-cache -type f | sort >actual &&\n-\t\t>expect &&\n-\t\ttest_cmp expect actual\n+\t\ttest_must_be_empty actual\n \t'\n }\n \n@@ -536,9 +535,8 @@ test_expect_success 'multiple identical conflicts' '\n \n \t# We resolved file1 and file2\n \tgit rerere &&\n-\t>expect &&\n \tgit rerere remaining >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \t# We must have recorded both of them\n \tcount_pre_post 2 2 &&\n@@ -548,9 +546,8 @@ test_expect_success 'multiple identical conflicts' '\n \ttest_must_fail git merge six.1 &&\n \tgit rerere &&\n \n-\t>expect &&\n \tgit rerere remaining >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \tconcat_insert short 6.1 6.2 >file1.expect &&\n \tconcat_insert long 6.1 6.2 >file2.expect &&\ndiff --git a/t/t4202-log.sh b/t/t4202-log.sh\nindex 25b1f8cc73..0b1cd3408b 100755\n--- a/t/t4202-log.sh\n+++ b/t/t4202-log.sh\n@@ -340,10 +340,9 @@ test_expect_success PCRE 'log -F -E --perl-regexp --grep=<pcre> uses PCRE' '\n '\n \n test_expect_success 'log with grep.patternType configuration' '\n-\t>expect &&\n \tgit -c grep.patterntype=fixed \\\n \tlog -1 --pretty=tformat:%s --grep=s.c.nd >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'log with grep.patternType configuration and command line' '\n@@ -1625,9 +1624,8 @@ test_expect_success 'log diagnoses bogus HEAD' '\n '\n \n test_expect_success 'log does not default to HEAD when rev input is given' '\n-\t>expect &&\n \tgit log --branches=does-not-exist >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'set up --source tests' '\ndiff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh\nindex e585fe6129..7c519436ef 100755\n--- a/t/t4210-log-i18n.sh\n+++ b/t/t4210-log-i18n.sh\n@@ -44,15 +44,13 @@ test_expect_success !MINGW 'log --grep searches in log output encoding (latin1)'\n '\n \n test_expect_success !MINGW 'log --grep does not find non-reencoded values (utf8)' '\n-\t>expect &&\n \tgit log --encoding=utf8 --format=%s --grep=$latin1_e >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'log --grep does not find non-reencoded values (latin1)' '\n-\t>expect &&\n \tgit log --encoding=ISO-8859-1 --format=%s --grep=$utf8_e >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_done\ndiff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh\nindex 2d22a17c4a..6ee4d3f2d9 100755\n--- a/t/t5310-pack-bitmaps.sh\n+++ b/t/t5310-pack-bitmaps.sh\n@@ -309,9 +309,8 @@ test_expect_success 'pack reuse respects --honor-pack-keep' '\n \tdone &&\n \treusable_pack --honor-pack-keep >empty.pack &&\n \tgit index-pack empty.pack &&\n-\t>expect &&\n \tgit show-index <empty.idx >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'pack reuse respects --local' '\n@@ -319,17 +318,15 @@ test_expect_success 'pack reuse respects --local' '\n \ttest_when_finished \"mv alt.git/objects/pack/* .git/objects/pack/\" &&\n \treusable_pack --local >empty.pack &&\n \tgit index-pack empty.pack &&\n-\t>expect &&\n \tgit show-index <empty.idx >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'pack reuse respects --incremental' '\n \treusable_pack --incremental >empty.pack &&\n \tgit index-pack empty.pack &&\n-\t>expect &&\n \tgit show-index <empty.idx >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'truncated bitmap fails gracefully' '\ndiff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh\nindex 4fe4ad9d61..f1708d415e 100755\n--- a/t/t5313-pack-bounds-checks.sh\n+++ b/t/t5313-pack-bounds-checks.sh\n@@ -90,9 +90,8 @@ test_expect_success 'matched bogus object count' '\n \n \t# Unlike above, we should notice early that the .idx is totally\n \t# bogus, and not even enumerate its contents.\n-\t>expect &&\n \tgit cat-file --batch-all-objects --batch-check >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \t# But as before, we can do the same object-access checks.\n \ttest_must_fail git cat-file blob $object &&\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex 11e14a1e0f..6edec8fed7 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -74,8 +74,7 @@ test_expect_success 'add another remote' '\n \t\tgit for-each-ref \"--format=%(refname)\" refs/remotes |\n \t\tsed -e \"/^refs\\/remotes\\/origin\\//d\" \\\n \t\t    -e \"/^refs\\/remotes\\/second\\//d\" >actual &&\n-\t\t>expect &&\n-\t\ttest_cmp expect actual\n+\t\ttest_must_be_empty actual\n \t)\n '\n \n@@ -112,8 +111,7 @@ test_expect_success C_LOCALE_OUTPUT 'remove remote' '\n \t\tcheck_remote_track origin master side &&\n \t\tgit for-each-ref \"--format=%(refname)\" refs/remotes |\n \t\tsed -e \"/^refs\\/remotes\\/origin\\//d\" >actual &&\n-\t\t>expect &&\n-\t\ttest_cmp expect actual\n+\t\ttest_must_be_empty actual\n \t)\n '\n \ndiff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh\nindex ea020040e8..bc5703ff9b 100755\n--- a/t/t5512-ls-remote.sh\n+++ b/t/t5512-ls-remote.sh\n@@ -155,14 +155,12 @@ test_expect_success 'die with non-2 for wrong repository even with --exit-code'\n \n test_expect_success 'Report success even when nothing matches' '\n \tgit ls-remote other.git \"refs/nsn/*\" >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'Report no-match with --exit-code' '\n \ttest_expect_code 2 git ls-remote --exit-code other.git \"refs/nsn/*\" >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'Report match with --exit-code' '\ndiff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh\nindex 4b4b6673b8..0030c92e1a 100755\n--- a/t/t5514-fetch-multiple.sh\n+++ b/t/t5514-fetch-multiple.sh\n@@ -152,7 +152,6 @@ test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '\n '\n \n test_expect_success 'git fetch --all --no-tags' '\n-\t>expect &&\n \tgit clone one test5 &&\n \tgit clone test5 test6 &&\n \t(cd test5 && git tag test-tag) &&\n@@ -161,7 +160,7 @@ test_expect_success 'git fetch --all --no-tags' '\n \t\tgit fetch --all --no-tags &&\n \t\tgit tag >output\n \t) &&\n-\ttest_cmp expect test6/output\n+\ttest_must_be_empty test6/output\n '\n \n test_expect_success 'git fetch --all --tags' '\ndiff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh\nindex d38ecee217..0b0eb1d025 100755\n--- a/t/t5533-push-cas.sh\n+++ b/t/t5533-push-cas.sh\n@@ -142,9 +142,8 @@ test_expect_success 'push to delete (protected, forced)' '\n \t\tcd dst &&\n \t\tgit push --force --force-with-lease=master:master^ origin :master\n \t) &&\n-\t>expect &&\n \tgit ls-remote src refs/heads/master >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'push to delete (allowed)' '\n@@ -154,9 +153,8 @@ test_expect_success 'push to delete (allowed)' '\n \t\tgit push --force-with-lease=master origin :master 2>err &&\n \t\tgrep deleted err\n \t) &&\n-\t>expect &&\n \tgit ls-remote src refs/heads/master >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'cover everything with default force-with-lease (protected)' '\ndiff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh\nindex fac5a73851..5582b3d5fd 100755\n--- a/t/t5612-clone-refspec.sh\n+++ b/t/t5612-clone-refspec.sh\n@@ -97,8 +97,7 @@ test_expect_success 'clone with --no-tags' '\n \t\tgit fetch &&\n \t\tgit for-each-ref refs/tags >../actual\n \t) &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success '--single-branch while HEAD pointing at master' '\n@@ -140,8 +139,7 @@ test_expect_success '--single-branch while HEAD pointing at master and --no-tags\n \t\tgit fetch &&\n \t\tgit for-each-ref refs/tags >../actual\n \t) &&\n-\t>expect &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \ttest_line_count = 0 actual &&\n \t# get tags with --tags overrides tagOpt\n \t(\n@@ -230,8 +228,7 @@ test_expect_success '--single-branch with detached' '\n \t\t    -e \"s|/remotes/origin/|/heads/|\" >../actual\n \t) &&\n \t# nothing\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_done\ndiff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh\nindex 969e4e9e52..fb4d295aa0 100755\n--- a/t/t6000-rev-list-misc.sh\n+++ b/t/t6000-rev-list-misc.sh\n@@ -58,8 +58,7 @@ test_expect_success 'rev-list A..B and rev-list ^A B are the same' '\n \n test_expect_success 'propagate uninteresting flag down correctly' '\n \tgit rev-list --objects ^HEAD^{tree} HEAD^{tree} >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'symleft flag bit is propagated down from tag' '\ndiff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh\nindex 20e3e2554a..916d9692bc 100755\n--- a/t/t6009-rev-list-parent.sh\n+++ b/t/t6009-rev-list-parent.sh\n@@ -31,8 +31,7 @@ test_expect_success setup '\n test_expect_success 'one is ancestor of others and should not be shown' '\n \n \tgit rev-list one --not four >result &&\n-\t>expect &&\n-\ttest_cmp expect result\n+\ttest_must_be_empty result\n \n '\n \n@@ -144,8 +143,7 @@ test_expect_success 'ancestors with the same commit time' '\n \t\ttest_commit t$i\n \tdone &&\n \tgit rev-list t1^! --not t$i >result &&\n-\t>expect &&\n-\ttest_cmp expect result\n+\ttest_must_be_empty result\n '\n \n test_done\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex d3453c583c..02936c2f24 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -256,31 +256,27 @@ test_expect_success 'rev-list accumulates multiple --exclude' '\n '\n \n test_expect_failure 'rev-list should succeed with empty output on empty stdin' '\n-\t>expect &&\n \tgit rev-list --stdin <expect >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'rev-list should succeed with empty output with all refs excluded' '\n-\t>expect &&\n \tgit rev-list --exclude=* --all >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'rev-list should succeed with empty output with empty --all' '\n \t(\n \t\ttest_create_repo empty &&\n \t\tcd empty &&\n-\t\t>expect &&\n \t\tgit rev-list --all >actual &&\n-\t\ttest_cmp expect actual\n+\t\ttest_must_be_empty actual\n \t)\n '\n \n test_expect_success 'rev-list should succeed with empty output with empty glob' '\n-\t>expect &&\n \tgit rev-list --glob=does-not-match-anything >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'shortlog accepts --glob/--tags/--remotes' '\ndiff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh\nindex dabebaee0b..beadaf6cca 100755\n--- a/t/t6019-rev-list-ancestry-path.sh\n+++ b/t/t6019-rev-list-ancestry-path.sh\n@@ -95,10 +95,9 @@ test_expect_success 'rev-list --ancestry-path F...I' '\n \n # G.t is dropped in an \"-s ours\" merge\n test_expect_success 'rev-list G..M -- G.t' '\n-\t>expect &&\n \tgit rev-list --format=%s G..M -- G.t |\n \tsed -e \"/^commit /d\" >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'rev-list --ancestry-path G..M -- G.t' '\ndiff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh\nindex b760c223c6..53cc9b2ffb 100755\n--- a/t/t6022-merge-rename.sh\n+++ b/t/t6022-merge-rename.sh\n@@ -893,8 +893,7 @@ test_expect_success 'do not follow renames for empty files' '\n \tgit mv empty1 empty2 &&\n \tgit commit -m rename &&\n \ttest_must_fail git merge empty-base &&\n-\t>expect &&\n-\ttest_cmp expect empty2\n+\ttest_must_be_empty empty2\n '\n \n test_done\ndiff --git a/t/t6060-merge-index.sh b/t/t6060-merge-index.sh\nindex debadbd299..ddf34f0115 100755\n--- a/t/t6060-merge-index.sh\n+++ b/t/t6060-merge-index.sh\n@@ -44,8 +44,7 @@ test_expect_success 'read-tree does not resolve content merge' '\n test_expect_success 'git merge-index git-merge-one-file resolves' '\n \tgit merge-index git-merge-one-file -a &&\n \tgit diff-files --name-only --diff-filter=U >unmerged &&\n-\t>expect &&\n-\ttest_cmp expect unmerged &&\n+\ttest_must_be_empty unmerged &&\n \ttest_cmp expect-merged file &&\n \tgit cat-file blob :file >file-index &&\n \ttest_cmp expect-merged file-index\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex e0496da812..024f8c06f7 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -796,9 +796,8 @@ test_expect_success ':remotename and :remoteref' '\n '\n \n test_expect_success 'for-each-ref --ignore-case ignores case' '\n-\t>expect &&\n \tgit for-each-ref --format=\"%(refname)\" refs/heads/MASTER >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \techo refs/heads/master >expect &&\n \tgit for-each-ref --format=\"%(refname)\" --ignore-case \\\ndiff --git a/t/t7004-tag.sh b/t/t7004-tag.sh\nindex d7b319e919..93a6694f0e 100755\n--- a/t/t7004-tag.sh\n+++ b/t/t7004-tag.sh\n@@ -693,9 +693,8 @@ test_expect_success \\\n '\n \n test_expect_success 'The -n 100 invocation means -n --list 100, not -n100' '\n-\t>expect &&\n \tgit tag -n 100 >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \tgit tag -m \"A msg\" 100 &&\n \techo \"100             A msg\" >expect &&\n@@ -974,9 +973,8 @@ test_expect_success GPG 'verifying a proper tag with --format pass and format ac\n '\n \n test_expect_success GPG 'verifying a forged tag with --format should fail silently' '\n-\t>expect &&\n \ttest_must_fail git tag -v --format=\"tagname : %(tag)\" \"forged-tag\" >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n # blank and empty messages for signed tags:\n@@ -1382,9 +1380,8 @@ test_expect_success 'message in editor has initial comment: first line' '\n test_expect_success \\\n \t'message in editor has initial comment: remainder' '\n \t# remove commented lines from the remainder -- should be empty\n-\t>rest.expect &&\n \tsed -e 1d -e \"/^#/d\" <actual >rest.actual &&\n-\ttest_cmp rest.expect rest.actual\n+\ttest_must_be_empty rest.actual\n '\n \n get_tag_header reuse $commit commit $time >expect\n@@ -1466,19 +1463,18 @@ test_expect_success 'checking that first commit is in all tags (relative)' \"\n \n # All the --contains tests above, but with --no-contains\n test_expect_success 'checking that first commit is not listed in any tag with --no-contains  (hash)' \"\n-\t>expected &&\n \tgit tag -l --no-contains $hash1 v* >actual &&\n-\ttest_cmp expected actual\n+\ttest_must_be_empty actual\n \"\n \n test_expect_success 'checking that first commit is in all tags (tag)' \"\n \tgit tag -l --no-contains v1.0 v* >actual &&\n-\ttest_cmp expected actual\n+\ttest_must_be_empty actual\n \"\n \n test_expect_success 'checking that first commit is in all tags (relative)' \"\n \tgit tag -l --no-contains HEAD~2 v* >actual &&\n-\ttest_cmp expected actual\n+\ttest_must_be_empty actual\n \"\n \n cat > expected <<EOF\n@@ -1606,9 +1602,8 @@ test_expect_success 'checking that --contains can be used in non-list mode' '\n '\n \n test_expect_success 'checking that initial commit is in all tags with --no-contains' \"\n-\t>expected &&\n \tgit tag -l --no-contains $hash1 v* >actual &&\n-\ttest_cmp expected actual\n+\ttest_must_be_empty actual\n \"\n \n # mixing modes and options:\n@@ -1905,7 +1900,6 @@ test_expect_success 'version sort with very long prerelease suffix' '\n '\n \n test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a deep repo' '\n-\t>expect &&\n \ti=1 &&\n \twhile test $i -lt 8000\n \tdo\n@@ -1920,7 +1914,7 @@ EOF\"\n \tgit checkout master &&\n \tgit tag far-far-away HEAD^ &&\n \trun_with_limited_stack git tag --contains HEAD >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \trun_with_limited_stack git tag --no-contains HEAD >actual &&\n \ttest_line_count \"-gt\" 10 actual\n '\ndiff --git a/t/t7006-pager.sh b/t/t7006-pager.sh\nindex 7541ba5edb..00e09a375c 100755\n--- a/t/t7006-pager.sh\n+++ b/t/t7006-pager.sh\n@@ -626,12 +626,11 @@ test_expect_success TTY 'sub-commands of externals use their own pager' '\n \n test_expect_success TTY 'external command pagers override sub-commands' '\n \tsane_unset PAGER GIT_PAGER &&\n-\t>expect &&\n \t>actual &&\n \ttest_config pager.external false &&\n \ttest_config pager.log \"sed s/^/log:/ >actual\" &&\n \ttest_terminal git --exec-path=. external log --format=%s -1 &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'command with underscores does not complain' '\ndiff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh\nindex 291a1e2b07..1f068714c5 100755\n--- a/t/t7030-verify-tag.sh\n+++ b/t/t7030-verify-tag.sh\n@@ -134,9 +134,8 @@ test_expect_success GPG 'verifying tag with --format' '\n '\n \n test_expect_success GPG 'verifying a forged tag with --format should fail silently' '\n-\t>expect &&\n \ttest_must_fail git verify-tag --format=\"tagname : %(tag)\" $(cat forged1.tag) >actual-forged &&\n-\ttest_cmp expect actual-forged\n+\ttest_must_be_empty actual-forged\n '\n \n test_done\ndiff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh\nindex c61e304e97..6d8256a424 100755\n--- a/t/t7063-status-untracked-cache.sh\n+++ b/t/t7063-status-untracked-cache.sh\n@@ -26,9 +26,8 @@ avoid_racy() {\n }\n \n status_is_clean() {\n-\t>../status.expect &&\n \tgit status --porcelain >../status.actual &&\n-\ttest_cmp ../status.expect ../status.actual\n+\ttest_must_be_empty ../status.actual\n }\n \n test_lazy_prereq UNTRACKED_CACHE '\ndiff --git a/t/t7102-reset.sh b/t/t7102-reset.sh\nindex 95653a08ca..97be0d968d 100755\n--- a/t/t7102-reset.sh\n+++ b/t/t7102-reset.sh\n@@ -549,8 +549,7 @@ test_expect_success 'reset -N keeps removed files as intent-to-add' '\n \n \ttree=$(git write-tree) &&\n \tgit ls-tree $tree new-file >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \n \tgit diff --name-only >actual &&\n \techo new-file >expect &&\n@@ -563,9 +562,8 @@ test_expect_success 'reset --mixed sets up work tree' '\n \t\tcd mixed_worktree &&\n \t\ttest_commit dummy\n \t) &&\n-\t: >expect &&\n \tgit --git-dir=mixed_worktree/.git --work-tree=mixed_worktree reset >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_done\ndiff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh\nindex 0f95f00477..ecb85c3b82 100755\n--- a/t/t7106-reset-unborn-branch.sh\n+++ b/t/t7106-reset-unborn-branch.sh\n@@ -12,9 +12,8 @@ test_expect_success 'reset' '\n \tgit add a b &&\n \tgit reset &&\n \n-\t>expect &&\n \tgit ls-files >actual &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'reset HEAD' '\n@@ -39,9 +38,8 @@ test_expect_success PERL 'reset -p' '\n \techo y >yes &&\n \tgit reset -p <yes >output &&\n \n-\t>expect &&\n \tgit ls-files >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \ttest_i18ngrep \"Unstage\" output\n '\n \n@@ -61,9 +59,8 @@ test_expect_success 'reset --hard' '\n \ttest_when_finished \"echo a >a\" &&\n \tgit reset --hard &&\n \n-\t>expect &&\n \tgit ls-files >actual &&\n-\ttest_cmp expect actual &&\n+\ttest_must_be_empty actual &&\n \ttest_path_is_missing a\n '\n \ndiff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh\nindex 4e4c455502..1cd12b38c5 100755\n--- a/t/t7401-submodule-summary.sh\n+++ b/t/t7401-submodule-summary.sh\n@@ -64,8 +64,7 @@ test_expect_success 'added submodule (subdirectory only)' \"\n \t\tcd sub &&\n \t\tgit submodule summary . >../actual\n \t) &&\n-\t>expected &&\n-\ttest_cmp expected actual\n+\ttest_must_be_empty actual\n \"\n \n test_expect_success 'added submodule (subdirectory with explicit path)' \"\ndiff --git a/t/t7502-commit.sh b/t/t7502-commit.sh\nindex d33a3cb331..ca4a740da0 100755\n--- a/t/t7502-commit.sh\n+++ b/t/t7502-commit.sh\n@@ -393,7 +393,6 @@ EOF\n \n test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' '\n \t>.git/result &&\n-\t>expect &&\n \n \techo >>negative &&\n \t(\n@@ -403,7 +402,7 @@ test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' '\n \t\texport GIT_EDITOR &&\n \t\ttest_must_fail git commit -e -m sample -a\n \t) &&\n-\ttest_cmp expect .git/result\n+\ttest_must_be_empty .git/result\n '\n \n test_expect_success 'do not fire editor if -m <msg> was given' '\ndiff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh\nindex 047156e9d5..b18503de81 100755\n--- a/t/t7610-mergetool.sh\n+++ b/t/t7610-mergetool.sh\n@@ -620,8 +620,7 @@ test_expect_success 'file with no base' '\n \tgit checkout -b test$test_count branch1 &&\n \ttest_must_fail git merge master &&\n \tgit mergetool --no-prompt --tool mybase -- both &&\n-\t>expected &&\n-\ttest_cmp expected both\n+\ttest_must_be_empty both\n '\n \n test_expect_success 'custom commands override built-ins' '\ndiff --git a/t/t7810-grep.sh b/t/t7810-grep.sh\nindex dcaab1557b..d826e24b45 100755\n--- a/t/t7810-grep.sh\n+++ b/t/t7810-grep.sh\n@@ -691,8 +691,7 @@ test_expect_success 'log grep (5)' '\n \n test_expect_success 'log grep (6)' '\n \tgit log --author=-0700  --pretty=tformat:%s >actual &&\n-\t>expect &&\n-\ttest_cmp expect actual\n+\ttest_must_be_empty actual\n '\n \n test_expect_success 'log grep (7)' '\ndiff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh\nindex b8e919e25d..1ef1a19003 100755\n--- a/t/t9001-send-email.sh\n+++ b/t/t9001-send-email.sh\n@@ -253,10 +253,9 @@ test_suppress_self () {\n \n \tmv msgtxt1 msgtxt1-$3 &&\n \tsed -e '/^$/q' msgtxt1-$3 >\"msghdr1-$3\" &&\n-\t>\"expected-no-cc-$3\" &&\n \n \t(grep '^Cc:' msghdr1-$3 >\"actual-no-cc-$3\";\n-\t test_cmp expected-no-cc-$3 actual-no-cc-$3)\n+\t test_must_be_empty actual-no-cc-$3)\n }\n \n test_suppress_self_unquoted () {\ndiff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh\nindex 9e7f96223d..f8f869e26a 100755\n--- a/t/t9300-fast-import.sh\n+++ b/t/t9300-fast-import.sh\n@@ -2191,12 +2191,11 @@ test_expect_success 'R: --import-marks-if-exists' '\n \n test_expect_success 'R: feature import-marks-if-exists' '\n \trm -f io.marks &&\n-\t>expect &&\n \n \tgit fast-import --export-marks=io.marks <<-\\EOF &&\n \tfeature import-marks-if-exists=not_io.marks\n \tEOF\n-\ttest_cmp expect io.marks &&\n+\ttest_must_be_empty io.marks &&\n \n \tblob=$(echo hi | git hash-object --stdin) &&\n \n@@ -2227,13 +2226,11 @@ test_expect_success 'R: feature import-marks-if-exists' '\n \tEOF\n \ttest_cmp expect io.marks &&\n \n-\t>expect &&\n-\n \tgit fast-import --import-marks-if-exists=not_io.marks \\\n \t\t\t--export-marks=io.marks <<-\\EOF &&\n \tfeature import-marks-if-exists=io.marks\n \tEOF\n-\ttest_cmp expect io.marks\n+\ttest_must_be_empty io.marks\n '\n \n test_expect_success 'R: import to output marks works without any content' '\n-- \n2.18.0.345.g5c9ce644c3\n\n"},{"id":"353761","messageId":"xmqq7elgl54v.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180727174811.27360-1-avarab@gmail.com","subject":"Re: [PATCH] tests: make use of the test_must_be_empty function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-07-27T21:50:56Z","receivedAt":"2018-07-27T21:51:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\nÆvar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Change various tests that use an idiom of the form:\n>\n>     >expect &&\n>     test_cmp expect actual\n>\n> To instead use:\n>\n>     test_must_be_empty actual\n\nI trust that you eyeballed the result to make sure steps after these\nconverted parts do *not* depend on (or expect) the presence of an\nempty 'expect' file (e.g. \".gitignore\" has 'expect' listed, or the\nexpected output from \"status\" has expected in the untracked section)?\n\nI'd imagine that kind of sanity checking the result would have taken\n10x more time than the actual conversion itself.  \n\nVery much appreciated.\n\n> This patch is on top of \"next\" since one of the things being fixed up\n> is a test in my in-flight ab/checkout-default-remote series. It also\n> applies cleanly to \"pu\", and the only conflicts with \"master\" are due\n> to that series of mine.\n\nA patch that truly depends on all of 'next' won't have a chance to\ngraduate, but it seems you only need one topic out of it.\n\nIf I were you in such a situation I would have prepared two patches,\ni.e. one to apply on 'master', and the remainder to apply on tip of\nab/checkout-default-remote, and made sure that applying the former\non 'master' and merging the updated ab/checkout-default-remote would\nmatch the tree you would get by merging ab/checkout-default-remote\nto 'master' and applying _this_ patch.\n\nLet's see if I can tease them apart myself first.\n\nThanks.\n"},{"id":"353978","messageId":"CAM0VKjmgx1v9n90qc9rd2uY0Ki51UJb2DvpbP3_mUj59E1NABA@mail.gmail.com","threadId":"48399","inReplyTo":"20180727174811.27360-1-avarab@gmail.com","subject":"Re: [PATCH] tests: make use of the test_must_be_empty function","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-07-31T00:17:11Z","receivedAt":"2018-07-31T00:17:27Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Fri, Jul 27, 2018 at 7:48 PM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> Change various tests that use an idiom of the form:\n>\n>     >expect &&\n>     test_cmp expect actual\n>\n> To instead use:\n>\n>     test_must_be_empty actual\n\nThanks for working on this.\n\n> The test_must_be_empty() wrapper was introduced in ca8d148daf (\"test:\n> test_must_be_empty helper\", 2013-06-09). Many of these tests have been\n> added after that time. This was mostly found with, and manually pruned\n> from:\n>\n>     git grep '^\\s+>.*expect.* &&$' t\n\nThis command doesn't output anything as it is, it should be 'git grep\n-E ...'.\n\nFurthermore, it doesn't catch the cases when the empty expected file\nis written as ': > expect'.  I see that you noticed and converted a\nfew of these, too, but running\n\n  git grep ': >.*exp.*&&' t/\n\nturns up some more.\n"},{"id":"356288","messageId":"20180822174820.12909-1-szeder.dev@gmail.com","threadId":"48399","inReplyTo":"20180727174811.27360-1-avarab@gmail.com","subject":"[PATCH] t6018-rev-list-glob: fix 'empty stdin' test","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-08-22T17:48:20Z","receivedAt":"2018-08-22T17:48:31Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"Prior to d3c6751b18 (tests: make use of the test_must_be_empty\nfunction, 2018-07-27), in the test 'rev-list should succeed with empty\noutput on empty stdin' in 't6018-rev-list-glob' the empty 'expect'\nfile served dual purpose: besides specifying the expected output, as\nusual, it also served as empty input for 'git rev-list --stdin'.\n\nThen d3c6751b18 came along, and, as part of the conversion to\n'test_must_be_empty', removed this empty 'expect' file, not realizing\nits secondary purpose.  Redirecting stdin from the now non-existing\nfile failed the test, but since this test expects failure in the first\nplace, this issue went unnoticed.\n\nRedirect 'git rev-list's stdin explicitly from /dev/null to provide\nempty input.  (Strictly speaking we don't need this redirection,\nbecause the test script's stdin is already redirected from /dev/null\nanyway, but I think it's better to be explicit about it.)\n\nSigned-off-by: SZEDER Gábor <szeder.dev@gmail.com>\n---\n t/t6018-rev-list-glob.sh | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 02936c2f24..0bf10d0686 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -256,7 +256,7 @@ test_expect_success 'rev-list accumulates multiple --exclude' '\n '\n \n test_expect_failure 'rev-list should succeed with empty output on empty stdin' '\n-\tgit rev-list --stdin <expect >actual &&\n+\tgit rev-list --stdin </dev/null >actual &&\n \ttest_must_be_empty actual\n '\n \n-- \n2.19.0.rc0.136.gd2dd172e64\n\n"},{"id":"356291","messageId":"CAPig+cTQrBmgXQdwQpH7DO2AbraD3B4LiRHDx4Sf9rXg0d_eDA@mail.gmail.com","threadId":"48399","inReplyTo":"20180822174820.12909-1-szeder.dev@gmail.com","subject":"Re: [PATCH] t6018-rev-list-glob: fix 'empty stdin' test","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-08-22T17:53:23Z","receivedAt":"2018-08-22T17:53:36Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Aug 22, 2018 at 1:48 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:\n> Prior to d3c6751b18 (tests: make use of the test_must_be_empty\n> function, 2018-07-27), in the test 'rev-list should succeed with empty\n> output on empty stdin' in 't6018-rev-list-glob' the empty 'expect'\n> file served dual purpose: besides specifying the expected output, as\n> usual, it also served as empty input for 'git rev-list --stdin'.\n>\n> Then d3c6751b18 came along, and, as part of the conversion to\n> 'test_must_be_empty', removed this empty 'expect' file, not realizing\n> its secondary purpose.  Redirecting stdin from the now non-existing\n> file failed the test, but since this test expects failure in the first\n> place, this issue went unnoticed.\n\nCan you say a word or two (here in the email thread) about how you're\nfinding these failures (across the various test fixes you've posted\nrecently)? Are you instrumenting the code in some fashion? Or, finding\nthem by visual inspection?\n\nThanks.\n"},{"id":"356293","messageId":"xmqqk1oimga1.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180822174820.12909-1-szeder.dev@gmail.com","subject":"Re: [PATCH] t6018-rev-list-glob: fix 'empty stdin' test","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-22T18:01:58Z","receivedAt":"2018-08-22T18:02:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"SZEDER Gábor <szeder.dev@gmail.com> writes:\n\n> Prior to d3c6751b18 (tests: make use of the test_must_be_empty\n> function, 2018-07-27), in the test 'rev-list should succeed with empty\n> output on empty stdin' in 't6018-rev-list-glob' the empty 'expect'\n> file served dual purpose: besides specifying the expected output, as\n> usual, it also served as empty input for 'git rev-list --stdin'.\n\nThanks for a correction to a breakage on 'master' that was\nintroduced during this cycle.\n"},{"id":"356297","messageId":"xmqqefeqme0p.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180822174820.12909-1-szeder.dev@gmail.com","subject":"Re: [PATCH] t6018-rev-list-glob: fix 'empty stdin' test","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-22T18:50:46Z","receivedAt":"2018-08-22T18:50:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"SZEDER Gábor <szeder.dev@gmail.com> writes:\n\n> Redirect 'git rev-list's stdin explicitly from /dev/null to provide\n> empty input.  (Strictly speaking we don't need this redirection,\n> because the test script's stdin is already redirected from /dev/null\n> anyway, but I think it's better to be explicit about it.)\n\nYes.\n\n>  test_expect_failure 'rev-list should succeed with empty output on empty stdin' '\n> -\tgit rev-list --stdin <expect >actual &&\n> +\tgit rev-list --stdin </dev/null >actual &&\n>  \ttest_must_be_empty actual\n>  '\n\nBy the way, it may be about time to turn that expect-failure into\nexpect-success.  It is somewhat unfortunate that 0c5dc743 (\"t6018:\nflesh out empty input/output rev-list tests\", 2017-08-02) removed\nthe comment that said \"we _might_ want to change the behaviour in\nthese cases\" and explained the tests as reminders, anticipating that\nthe series will change the behaviour for three cases where the\npending list ends up empty to make the discussion moot, but it\nchanged the behaviour of only two of them, leaving the \"--stdin\nreads empty\" case behind.\n\nIt may be just the matter of doing something like the attached\npatch.  I won't be committing such a behaviour change during the\npre-release feature freeze, but we may want to consider doing this\nearly in the next cycle.\n\n revision.c               | 13 +++++++++++++\n t/t6018-rev-list-glob.sh |  4 ++--\n 2 files changed, 15 insertions(+), 2 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex d12e6d8a4a..21fb413511 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2441,6 +2441,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t\tobject = get_reference(revs, revs->def, &oid, 0);\n \t\tadd_pending_object_with_mode(revs, object, revs->def, oc.mode);\n \t}\n+\t/* \n+\t * Even if revs->pending is empty after all the above, if we\n+\t * handled \"--stdin\", then the caller really meant to give us\n+\t * an empty commit range.  Just let the traversal give an\n+\t * empty result without causing a \"no input?  do you know how\n+\t * to use this command?\" failure.\n+\t *\n+\t * NOTE!!!  Because \"--stdin </dev/null --default HEAD\" should\n+\t * default to HEAD, this must come _after_ the above block\n+\t * that deals with revs->ref fallback.\n+\t */\n+\tif (read_from_stdin)\n+\t\trevs->rev_input_given = 1;\n \n \t/* Did the user ask for any diff output? Run the diff! */\n \tif (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT)\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 02936c2f24..db8a7834d8 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -255,8 +255,8 @@ test_expect_success 'rev-list accumulates multiple --exclude' '\n \tcompare rev-list \"--exclude=refs/remotes/* --exclude=refs/tags/* --all\" --branches\n '\n \n-test_expect_failure 'rev-list should succeed with empty output on empty stdin' '\n+test_expect_success 'rev-list should succeed with empty output on empty stdin' '\n \tgit rev-list --stdin </dev/null >actual &&\n \ttest_must_be_empty actual\n '\n \n"},{"id":"356298","messageId":"CAM0VKjkVreBKQsvMZ=pEE0NN5gG0MM+XJ0MzCbw1rxi_pR+FXQ@mail.gmail.com","threadId":"48399","inReplyTo":"CAPig+cTQrBmgXQdwQpH7DO2AbraD3B4LiRHDx4Sf9rXg0d_eDA@mail.gmail.com","subject":"Re: [PATCH] t6018-rev-list-glob: fix 'empty stdin' test","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-08-22T18:59:11Z","receivedAt":"2018-08-22T18:59:26Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Wed, Aug 22, 2018 at 7:53 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n\n> Can you say a word or two (here in the email thread) about how you're\n> finding these failures (across the various test fixes you've posted\n> recently)? Are you instrumenting the code in some fashion? Or, finding\n> them by visual inspection?\n\nErrors from system commands in our tests look like these:\n\n  grep: file3: No such file or directory\n  sed: -e expression #1, char 2: extra characters after command\n  diff: sub1/.git: No such file or directory\n  tar: rmtlseek not stopped at a record boundary\n  tar: Error is not recoverable: exiting now\n\nwhile errors from the shell running the test like these:\n\n  t0020-crlf.sh: 8: eval: cannot open two: No such file\n  t6018-rev-list-glob.sh: 4: eval: cannot open expect: No such file\n  t7408-submodule-reference.sh: 615: test: =: unexpected operator\n\ni.e. lines starting with various system commands' or test scripts'\nnames, followed by ': '.\n\nSo I've modified t/Makefile to not remove the 'test-results' directory\nafter a successful 'make test':\n\ndiff --git a/t/Makefile b/t/Makefile\nindex ea36cf7ac7..c7b1655593 100644\n--- a/t/Makefile\n+++ b/t/Makefile\n@@ -54,10 +54,11 @@ pre-clean:\n        $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'\n\n clean-except-prove-cache:\n-       $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'\n+       $(RM) -r 'trash directory'.*\n        $(RM) -r valgrind/bin\n\n clean: clean-except-prove-cache\n+       $(RM) '$(TEST_RESULTS_DIRECTORY_SQ)'\n\n distclean: clean\n        $(RM) .prove\n\nAnd then scanned the results of a '--verbose-log -x' test run with:\n\n  grep -E '^(awk|basename|cat|cd|chmod|cmp|cp|cut|diff|dirname|egrep|find|fgrep|grep|gunzip|gzip|ln|mkdir|mkfifo|mktemp|mv|readlink|rmdir|sed|sort|tar|touch|tr|ulimit|umask|uniq|unzip|wc|zipinfo|t[0-9][0-9][0-9][0-9]-[^:]*\\.sh):\n' test-results/*.out\n\nand then, for lack of something better to do ;), I started looking at\nthe simpler looking errors.\n\nI've though about how a check like this could be automated, but\nhaven't had any workable idea yet.  There are commands that can\nlegitimately print errors, e.g. when checking for a prereq which the\nsystem doesn't have (e.g. the 'tar' errors above, I think).  And the\nlist of system commands in the grep pattern above is surely incomplete\nand will likely change in the future...\n"},{"id":"356300","messageId":"20180822192308.GB19730@sigill.intra.peff.net","threadId":"48399","inReplyTo":"xmqqefeqme0p.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] t6018-rev-list-glob: fix 'empty stdin' test","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-08-22T19:23:08Z","receivedAt":"2018-08-22T19:23:12Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Aug 22, 2018 at 11:50:46AM -0700, Junio C Hamano wrote:\n\n> >  test_expect_failure 'rev-list should succeed with empty output on empty stdin' '\n> > -\tgit rev-list --stdin <expect >actual &&\n> > +\tgit rev-list --stdin </dev/null >actual &&\n> >  \ttest_must_be_empty actual\n> >  '\n> \n> By the way, it may be about time to turn that expect-failure into\n> expect-success.  It is somewhat unfortunate that 0c5dc743 (\"t6018:\n> flesh out empty input/output rev-list tests\", 2017-08-02) removed\n> the comment that said \"we _might_ want to change the behaviour in\n> these cases\" and explained the tests as reminders, anticipating that\n> the series will change the behaviour for three cases where the\n> pending list ends up empty to make the discussion moot, but it\n> changed the behaviour of only two of them, leaving the \"--stdin\n> reads empty\" case behind.\n\nYeah, I think I had intended to make --stdin work there, and when I\nrealized at the end of the series it did not, I failed to go back and\nmodify that initial commit touching the test.\n\nMore discussion in the original thread:\n\n  https://public-inbox.org/git/20170802223416.gwiezhbuxbdmbjzx@sigill.intra.peff.net/\n\n> It may be just the matter of doing something like the attached\n> patch.  I won't be committing such a behaviour change during the\n> pre-release feature freeze, but we may want to consider doing this\n> early in the next cycle.\n\nYes, definitely this is tricky enough to avoid doing during the freeze.\n\n> diff --git a/revision.c b/revision.c\n> index d12e6d8a4a..21fb413511 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2441,6 +2441,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>  \t\tobject = get_reference(revs, revs->def, &oid, 0);\n>  \t\tadd_pending_object_with_mode(revs, object, revs->def, oc.mode);\n>  \t}\n> +\t/* \n> +\t * Even if revs->pending is empty after all the above, if we\n> +\t * handled \"--stdin\", then the caller really meant to give us\n> +\t * an empty commit range.  Just let the traversal give an\n> +\t * empty result without causing a \"no input?  do you know how\n> +\t * to use this command?\" failure.\n> +\t *\n> +\t * NOTE!!!  Because \"--stdin </dev/null --default HEAD\" should\n> +\t * default to HEAD, this must come _after_ the above block\n> +\t * that deals with revs->ref fallback.\n> +\t */\n> +\tif (read_from_stdin)\n> +\t\trevs->rev_input_given = 1;\n\nI think this should do the right thing. All of the issues discussed in\nthe earlier thread were about using revs->def, and this neatly sidesteps\nthat by touching the flag afterwards. It's a little funny that the flag\nnow means two things (earlier in the function it is \"we should use the\ndefault\" and later it becomes \"the caller may complain\").\n\nIt may be worth splitting it into two flags: rev_input_given and\nrev_read_stdin. That puts the responsibility on the caller to check both\nflags. But really, rev-list.c is the only caller that checks it anyway.\nAnd it would mean this scary comment can go away. ;)\n\nIt also _could_ be useful to other callers to distinguish the two cases\n(e.g., if they wanted to know whether they were free to use stdin\nthemselves). But I don't offhand know of any callers that need that (I\nhave a vague recollection that it might have come up once over the\nyears).\n\n-Peff\n"},{"id":"356301","messageId":"20180822195045.GC19730@sigill.intra.peff.net","threadId":"48399","inReplyTo":"20180822192308.GB19730@sigill.intra.peff.net","subject":"[PATCH] rev-list: make empty --stdin not an error","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-08-22T19:50:46Z","receivedAt":"2018-08-22T19:50:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Aug 22, 2018 at 03:23:08PM -0400, Jeff King wrote:\n\n> > +\t/* \n> > +\t * Even if revs->pending is empty after all the above, if we\n> > +\t * handled \"--stdin\", then the caller really meant to give us\n> > +\t * an empty commit range.  Just let the traversal give an\n> > +\t * empty result without causing a \"no input?  do you know how\n> > +\t * to use this command?\" failure.\n> > +\t *\n> > +\t * NOTE!!!  Because \"--stdin </dev/null --default HEAD\" should\n> > +\t * default to HEAD, this must come _after_ the above block\n> > +\t * that deals with revs->ref fallback.\n> > +\t */\n> > +\tif (read_from_stdin)\n> > +\t\trevs->rev_input_given = 1;\n> \n> I think this should do the right thing. All of the issues discussed in\n> the earlier thread were about using revs->def, and this neatly sidesteps\n> that by touching the flag afterwards. It's a little funny that the flag\n> now means two things (earlier in the function it is \"we should use the\n> default\" and later it becomes \"the caller may complain\").\n\nHere's my \"two flags\" approach as a real patch. After giving it some\nthought, I really do think it's cleaner.\n\nI know we're in a freeze. If you want to pick this up for pu or maybe\nnext, that's fine. If not, I'll re-send it after the release. It applies\non top of Gábor's text fixup patch.\n\n-- >8 --\nSubject: [PATCH] rev-list: make empty --stdin not an error\n\nWhen we originally did the series that contains 7ba826290a\n(revision: add rev_input_given flag, 2017-08-02) the intent\nwas that \"git rev-list --stdin </dev/null\" would similarly\nbecome a successful noop. However, an attempt at the time to\ndo that did not work[1]. The problem is that rev_input_given\nserves two roles:\n\n - it tells rev-list.c that it should not error out\n\n - it tells revision.c that it should not have the \"default\"\n   ref kick (e.g., \"HEAD\" in \"git log\")\n\nWe want to trigger the former, but not the latter. This is\ntechnically possible with a single flag, if we set the flag\nonly after revision.c's revs->def check. But this introduces\na rather subtle ordering dependency.\n\nInstead, let's keep two flags: one to denote when we got\nactual input (which triggers both roles) and one for when we\nread stdin (which triggers only the first).\n\nThis does mean a caller interested in the first role has to\ncheck both flags, but there's only one such caller. And any\nfuture callers might want to make the distinction anyway\n(e.g., if they care less about erroring out, and more about\nwhether revision.c soaked up our stdin).\n\n[1] https://public-inbox.org/git/20170802223416.gwiezhbuxbdmbjzx@sigill.intra.peff.net/\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/rev-list.c       | 2 +-\n revision.c               | 1 +\n revision.h               | 5 +++++\n t/t6018-rev-list-glob.sh | 2 +-\n 4 files changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 5b07f3f4a2..ed0ea7dc5b 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -493,7 +493,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif ((!revs.commits && reflog_walk_empty(revs.reflog_info) &&\n \t     (!(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&\n \t      !revs.pending.nr) &&\n-\t     !revs.rev_input_given) ||\n+\t     !revs.rev_input_given && !revs.read_from_stdin) ||\n \t    revs.diff)\n \t\tusage(rev_list_usage);\n \ndiff --git a/revision.c b/revision.c\nindex de4dce600d..4d53102cf4 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2369,6 +2369,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t\t\t\t}\n \t\t\t\tif (read_from_stdin++)\n \t\t\t\t\tdie(\"--stdin given twice?\");\n+\t\t\t\trevs->read_from_stdin = 1;\n \t\t\t\tread_revisions_from_stdin(revs, &prune_data);\n \t\t\t\tcontinue;\n \t\t\t}\ndiff --git a/revision.h b/revision.h\nindex 007278cc11..1225957927 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -82,6 +82,11 @@ struct rev_info {\n \t */\n \tint rev_input_given;\n \n+\t/*\n+\t * Whether we read from stdin due to the --stdin option.\n+\t */\n+\tint read_from_stdin;\n+\n \t/* topo-sort */\n \tenum rev_sort_order sort_order;\n \ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 0bf10d0686..db8a7834d8 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -255,7 +255,7 @@ test_expect_success 'rev-list accumulates multiple --exclude' '\n \tcompare rev-list \"--exclude=refs/remotes/* --exclude=refs/tags/* --all\" --branches\n '\n \n-test_expect_failure 'rev-list should succeed with empty output on empty stdin' '\n+test_expect_success 'rev-list should succeed with empty output on empty stdin' '\n \tgit rev-list --stdin </dev/null >actual &&\n \ttest_must_be_empty actual\n '\n-- \n2.19.0.rc0.412.g7005db4e88\n\n"},{"id":"356302","messageId":"CAPig+cS-3OttVT094Hw=RsN5pRvbXq8yekM-zNDXtiKiuvOcjw@mail.gmail.com","threadId":"48399","inReplyTo":"CAM0VKjkVreBKQsvMZ=pEE0NN5gG0MM+XJ0MzCbw1rxi_pR+FXQ@mail.gmail.com","subject":"Re: [PATCH] t6018-rev-list-glob: fix 'empty stdin' test","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2018-08-22T20:30:57Z","receivedAt":"2018-08-22T20:31:11Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Wed, Aug 22, 2018 at 2:59 PM SZEDER Gábor <szeder.dev@gmail.com> wrote:\n> On Wed, Aug 22, 2018 at 7:53 PM Eric Sunshine <sunshine@sunshineco.com> wrote:\n> > Can you say a word or two (here in the email thread) about how you're\n> > finding these failures (across the various test fixes you've posted\n> > recently)? Are you instrumenting the code in some fashion? Or, finding\n> > them by visual inspection?\n>\n> Errors from system commands in our tests look like these:\n>   grep: file3: No such file or directory\n> i.e. lines starting with various system commands' or test scripts'\n> names, followed by ': '.\n>\n> So I've modified t/Makefile to not remove the 'test-results' directory\n> after a successful 'make test':\n> And then scanned the results of a '--verbose-log -x' test run with:\n>   grep -E [...]\n> and then, for lack of something better to do ;), I started looking at\n> the simpler looking errors.\n\nThanks for the explanation. That makes a lot of sense.\n"},{"id":"356304","messageId":"xmqqa7pem8ul.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180822195045.GC19730@sigill.intra.peff.net","subject":"Re: [PATCH] rev-list: make empty --stdin not an error","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-22T20:42:26Z","receivedAt":"2018-08-22T20:42:32Z","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> Instead, let's keep two flags: one to denote when we got\n> actual input (which triggers both roles) and one for when we\n> read stdin (which triggers only the first).\n>\n> This does mean a caller interested in the first role has to\n> check both flags, but there's only one such caller. And any\n> future callers might want to make the distinction anyway\n> (e.g., if they care less about erroring out, and more about\n> whether revision.c soaked up our stdin).\n>\n> [1] https://public-inbox.org/git/20170802223416.gwiezhbuxbdmbjzx@sigill.intra.peff.net/\n>\n> Helped-by: Junio C Hamano <gitster@pobox.com>\n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n>  builtin/rev-list.c       | 2 +-\n>  revision.c               | 1 +\n>  revision.h               | 5 +++++\n>  t/t6018-rev-list-glob.sh | 2 +-\n>  4 files changed, 8 insertions(+), 2 deletions(-)\n\nI think this makes sense, but if we were to give a dedicated field\nin the revs structure, can we lose the local variable at the same\ntime, I wonder?\n\nThanks.\n\n> diff --git a/revision.c b/revision.c\n> index de4dce600d..4d53102cf4 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2369,6 +2369,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>  \t\t\t\t}\n>  \t\t\t\tif (read_from_stdin++)\n>  \t\t\t\t\tdie(\"--stdin given twice?\");\n> +\t\t\t\trevs->read_from_stdin = 1;\n>  \t\t\t\tread_revisions_from_stdin(revs, &prune_data);\n>  \t\t\t\tcontinue;\n>  \t\t\t}\n> diff --git a/revision.h b/revision.h\n> index 007278cc11..1225957927 100644\n> --- a/revision.h\n> +++ b/revision.h\n> @@ -82,6 +82,11 @@ struct rev_info {\n>  \t */\n>  \tint rev_input_given;\n>  \n> +\t/*\n> +\t * Whether we read from stdin due to the --stdin option.\n> +\t */\n> +\tint read_from_stdin;\n> +\n>  \t/* topo-sort */\n>  \tenum rev_sort_order sort_order;\n>  \n> diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\n> index 0bf10d0686..db8a7834d8 100755\n> --- a/t/t6018-rev-list-glob.sh\n> +++ b/t/t6018-rev-list-glob.sh\n> @@ -255,7 +255,7 @@ test_expect_success 'rev-list accumulates multiple --exclude' '\n>  \tcompare rev-list \"--exclude=refs/remotes/* --exclude=refs/tags/* --all\" --branches\n>  '\n>  \n> -test_expect_failure 'rev-list should succeed with empty output on empty stdin' '\n> +test_expect_success 'rev-list should succeed with empty output on empty stdin' '\n>  \tgit rev-list --stdin </dev/null >actual &&\n>  \ttest_must_be_empty actual\n>  '\n"},{"id":"356310","messageId":"20180822213722.GA25180@sigill.intra.peff.net","threadId":"48399","inReplyTo":"xmqqa7pem8ul.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] rev-list: make empty --stdin not an error","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-08-22T21:37:23Z","receivedAt":"2018-08-22T21:37:26Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Aug 22, 2018 at 01:42:26PM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > Instead, let's keep two flags: one to denote when we got\n> > actual input (which triggers both roles) and one for when we\n> > read stdin (which triggers only the first).\n> >\n> > This does mean a caller interested in the first role has to\n> > check both flags, but there's only one such caller. And any\n> > future callers might want to make the distinction anyway\n> > (e.g., if they care less about erroring out, and more about\n> > whether revision.c soaked up our stdin).\n> >\n> > [1] https://public-inbox.org/git/20170802223416.gwiezhbuxbdmbjzx@sigill.intra.peff.net/\n> >\n> > Helped-by: Junio C Hamano <gitster@pobox.com>\n> > Signed-off-by: Jeff King <peff@peff.net>\n> > ---\n> >  builtin/rev-list.c       | 2 +-\n> >  revision.c               | 1 +\n> >  revision.h               | 5 +++++\n> >  t/t6018-rev-list-glob.sh | 2 +-\n> >  4 files changed, 8 insertions(+), 2 deletions(-)\n> \n> I think this makes sense, but if we were to give a dedicated field\n> in the revs structure, can we lose the local variable at the same\n> time, I wonder?\n\nYes. I was thinking it had more purpose than this, but it really is just\na flag to check \"did we do this already?\". Which is one of the main\npurposes I claimed for the new flag in my commit message. :)\n\nHere it is with that squashed in (this is the whole patch, since I\nupdated the commit message to mention it).\n\n-- >8 --\nSubject: [PATCH] rev-list: make empty --stdin not an error\n\nWhen we originally did the series that contains 7ba826290a\n(revision: add rev_input_given flag, 2017-08-02) the intent\nwas that \"git rev-list --stdin </dev/null\" would similarly\nbecome a successful noop. However, an attempt at the time to\ndo that did not work[1]. The problem is that rev_input_given\nserves two roles:\n\n - it tells rev-list.c that it should not error out\n\n - it tells revision.c that it should not have the \"default\"\n   ref kick (e.g., \"HEAD\" in \"git log\")\n\nWe want to trigger the former, but not the latter. This is\ntechnically possible with a single flag, if we set the flag\nonly after revision.c's revs->def check. But this introduces\na rather subtle ordering dependency.\n\nInstead, let's keep two flags: one to denote when we got\nactual input (which triggers both roles) and one for when we\nread stdin (which triggers only the first).\n\nThis does mean a caller interested in the first role has to\ncheck both flags, but there's only one such caller. And any\nfuture callers might want to make the distinction anyway\n(e.g., if they care less about erroring out, and more about\nwhether revision.c soaked up our stdin).\n\nIn fact, we already keep such a flag internally in\nrevision.c for this purpose, so this is really just exposing\nthat to the caller (and the old function-local flag can go\naway in favor of our new one).\n\n[1] https://public-inbox.org/git/20170802223416.gwiezhbuxbdmbjzx@sigill.intra.peff.net/\n\nHelped-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/rev-list.c       | 2 +-\n revision.c               | 5 ++---\n revision.h               | 5 +++++\n t/t6018-rev-list-glob.sh | 2 +-\n 4 files changed, 9 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 5b07f3f4a2..ed0ea7dc5b 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -493,7 +493,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif ((!revs.commits && reflog_walk_empty(revs.reflog_info) &&\n \t     (!(revs.tag_objects || revs.tree_objects || revs.blob_objects) &&\n \t      !revs.pending.nr) &&\n-\t     !revs.rev_input_given) ||\n+\t     !revs.rev_input_given && !revs.read_from_stdin) ||\n \t    revs.diff)\n \t\tusage(rev_list_usage);\n \ndiff --git a/revision.c b/revision.c\nindex de4dce600d..46228f82ee 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2318,7 +2318,7 @@ static void NORETURN diagnose_missing_default(const char *def)\n  */\n int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)\n {\n-\tint i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt;\n+\tint i, flags, left, seen_dashdash, got_rev_arg = 0, revarg_opt;\n \tstruct argv_array prune_data = ARGV_ARRAY_INIT;\n \tconst char *submodule = NULL;\n \n@@ -2348,7 +2348,6 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \trevarg_opt = opt ? opt->revarg_opt : 0;\n \tif (seen_dashdash)\n \t\trevarg_opt |= REVARG_CANNOT_BE_FILENAME;\n-\tread_from_stdin = 0;\n \tfor (left = i = 1; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n \t\tif (*arg == '-') {\n@@ -2367,7 +2366,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \t\t\t\t\targv[left++] = arg;\n \t\t\t\t\tcontinue;\n \t\t\t\t}\n-\t\t\t\tif (read_from_stdin++)\n+\t\t\t\tif (revs->read_from_stdin++)\n \t\t\t\t\tdie(\"--stdin given twice?\");\n \t\t\t\tread_revisions_from_stdin(revs, &prune_data);\n \t\t\t\tcontinue;\ndiff --git a/revision.h b/revision.h\nindex 007278cc11..1225957927 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -82,6 +82,11 @@ struct rev_info {\n \t */\n \tint rev_input_given;\n \n+\t/*\n+\t * Whether we read from stdin due to the --stdin option.\n+\t */\n+\tint read_from_stdin;\n+\n \t/* topo-sort */\n \tenum rev_sort_order sort_order;\n \ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 0bf10d0686..db8a7834d8 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -255,7 +255,7 @@ test_expect_success 'rev-list accumulates multiple --exclude' '\n \tcompare rev-list \"--exclude=refs/remotes/* --exclude=refs/tags/* --all\" --branches\n '\n \n-test_expect_failure 'rev-list should succeed with empty output on empty stdin' '\n+test_expect_success 'rev-list should succeed with empty output on empty stdin' '\n \tgit rev-list --stdin </dev/null >actual &&\n \ttest_must_be_empty actual\n '\n-- \n2.19.0.rc0.412.g7005db4e88\n\n"},{"id":"356311","messageId":"xmqqva82krjs.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"xmqqa7pem8ul.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] rev-list: make empty --stdin not an error","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-22T21:41:27Z","receivedAt":"2018-08-22T21:41:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I think this makes sense, but if we were to give a dedicated field\n> in the revs structure, can we lose the local variable at the same\n> time, I wonder?\n>\n> Thanks.\n\nWell, the answer to \"can we\" is always \"yes\"; what I was truly\nwondering was if it makes sense to do so.  I am on the fence.\n\n>\n>> diff --git a/revision.c b/revision.c\n>> index de4dce600d..4d53102cf4 100644\n>> --- a/revision.c\n>> +++ b/revision.c\n>> @@ -2369,6 +2369,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>>  \t\t\t\t}\n>>  \t\t\t\tif (read_from_stdin++)\n>>  \t\t\t\t\tdie(\"--stdin given twice?\");\n>> +\t\t\t\trevs->read_from_stdin = 1;\n>>  \t\t\t\tread_revisions_from_stdin(revs, &prune_data);\n>>  \t\t\t\tcontinue;\n"},{"id":"356313","messageId":"xmqqr2iqkr5e.fsf@gitster-ct.c.googlers.com","threadId":"48399","inReplyTo":"20180822213722.GA25180@sigill.intra.peff.net","subject":"Re: [PATCH] rev-list: make empty --stdin not an error","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-08-22T21:50:05Z","receivedAt":"2018-08-22T21:50:10Z","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> Yes. I was thinking it had more purpose than this, but it really is just\n> a flag to check \"did we do this already?\". Which is one of the main\n> purposes I claimed for the new flag in my commit message. :)\n\nOK.  \n\nThe reason I was on the fence was primarily because read_from_stdin\nfield in the structure observable from outside can be a boolean\n(that is, \"unsigned :1\"), but internally this may want to count up\nto two.\n\nOr with \"unsigned read_from_stdin:1\", would this \n\n\tif (revs->read_from_stdin++)\n\t\tdie(\"twice???\");\n\nstill be usable?  As the value of post-increment would be 1 even\nwhen the resulting field would have wrapped-around already, it\nshould be OK, but it just felt strange to me.\n\nBut that is something we do not have to worry about until somebody\ntries to shrink the structure by making these flags into bitfields.\n\nThanks for an updated patch.\n"},{"id":"356315","messageId":"20180822215501.GA27737@sigill.intra.peff.net","threadId":"48399","inReplyTo":"xmqqr2iqkr5e.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] rev-list: make empty --stdin not an error","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-08-22T21:55:02Z","receivedAt":"2018-08-22T21:55:05Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Aug 22, 2018 at 02:50:05PM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > Yes. I was thinking it had more purpose than this, but it really is just\n> > a flag to check \"did we do this already?\". Which is one of the main\n> > purposes I claimed for the new flag in my commit message. :)\n> \n> OK.  \n> \n> The reason I was on the fence was primarily because read_from_stdin\n> field in the structure observable from outside can be a boolean\n> (that is, \"unsigned :1\"), but internally this may want to count up\n> to two.\n> \n> Or with \"unsigned read_from_stdin:1\", would this \n> \n> \tif (revs->read_from_stdin++)\n> \t\tdie(\"twice???\");\n> \n> still be usable?  As the value of post-increment would be 1 even\n> when the resulting field would have wrapped-around already, it\n> should be OK, but it just felt strange to me.\n\nI agree it would work in practice, though I also agree it is funny and\nshould be avoided.\n\n> But that is something we do not have to worry about until somebody\n> tries to shrink the structure by making these flags into bitfields.\n\nAlso agreed. I'd probably resolve it then by writing:\n\n  if (revs->read_from_stdin)\n\tdie(\"twice\");\n  revs->read_from_stdin = 1;\n\nI guess we could even do that now. Or add a test to make sure \"--stdin\n--stdin\" barfs. But I am perfectly happy to punt until somebody actually\nwants to use a bitfield.\n\n-Peff\n"}]}