{"thread":{"id":"57470","subject":"[PATCH 0/3] adding new branch.autosetupmerge option \"simple\"","startedAt":"2022-02-24T09:45:45Z","lastAt":"2022-04-30T15:51:48Z","messageCount":41,"participants":["Tao Klerks via GitGitGadget","Junio C Hamano","Tao Klerks","Ævar Arnfjörð Bjarmason","Eric Sunshine","Josh Steadmon"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"449417","messageId":"pull.1161.git.1645695940.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":null,"subject":"[PATCH 0/3] adding new branch.autosetupmerge option \"simple\"","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-24T09:45:37Z","receivedAt":"2022-02-24T09:45:45Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"This commit introduces a new option to the branch.autosetupmerge setting,\n\"simple\", which is intended to be consistent with and complementary to the\npush.default \"simple\" option.\n\nThe push.defaut option \"simple\" helps produce predictable/understandable\nbehavior for beginners, where they don't accidentally push to the \"wrong\"\nbranch in centralized workflows. If they create a local branch with a\ndifferent name and then try to do a plain push, it will helpfully fail and\nexplain why.\n\nHowever, such users can often find themselves confused by the behavior of\ngit after they first branch, and before they push. At that stage, their\nupstream tracking branch is the original remote branch, and pull (for\nexample) behaves very differently to how it later does when they create\ntheir own same-name remote branch.\n\nThis new option (with push.default set to simple) ensures that push/pull\nbehavior is generally consistent - tracking will be automatically set up for\nbranches that push will work for (and pull will be consistent for) only.\n\nTao Klerks (3):\n  merge: new autosetupmerge option 'simple' for matching branches\n  t3200: tests for new branch.autosetupmerge option \"simple\"\n  branch documentation: new autosetupmerge option \"simple\"\n\n Documentation/config/branch.txt |  4 +++-\n Documentation/git-branch.txt    |  4 +++-\n branch.c                        |  9 +++++++++\n branch.h                        |  1 +\n config.c                        |  3 +++\n t/t3200-branch.sh               | 35 +++++++++++++++++++++++++++++++++\n 6 files changed, 54 insertions(+), 2 deletions(-)\n\n\nbase-commit: dab1b7905d0b295f1acef9785bb2b9cbb0fdec84\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1161%2FTaoK%2Ffeature-branch-autosetupmerge-simple-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1161/TaoK/feature-branch-autosetupmerge-simple-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1161\n-- \ngitgitgadget\n"},{"id":"449418","messageId":"89efc1e15646599753baeab38ba2399dcbe868f1.1645695940.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.git.1645695940.gitgitgadget@gmail.com","subject":"[PATCH 1/3] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-24T09:45:38Z","receivedAt":"2022-02-24T09:45:47Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nThe push.defaut option \"simple\" helps produce\npredictable/understandable behavior for beginners,\nwhere they don't accidentally push to the\n\"wrong\" branch in centralized workflows. If they\ncreate a local branch with a different name\nand then try to do a plain push, it will\nhelpfully fail and explain why.\n\nHowever, such users can often find themselves\nconfused by the behavior of git after they first\nbranch, and before they push. At that stage,\ntheir upstream tracking branch is the original\nremote branch, and pull (for example) behaves\nvery differently to how it later does when they\ncreate their own same-name remote branch.\n\nThis commit introduces a new option to the\nbranch.autosetupmerge setting, \"simple\",\nwhich is intended to be consistent with and\ncomplementary to the push.default \"simple\"\noption.\n\nIt will set up automatic tracking for a new\nbranch only if the remote ref is a branch and\nthat remote branch name matches the new local\nbranch name. It is a reduction in scope of\nthe existing default option, \"true\".\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n branch.c | 9 +++++++++\n branch.h | 1 +\n config.c | 3 +++\n 3 files changed, 13 insertions(+)\n\ndiff --git a/branch.c b/branch.c\nindex 6b31df539a5..246bc82ce3c 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -256,6 +256,15 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,\n \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n \t\t    orig_ref);\n \n+\tif (track == BRANCH_TRACK_SIMPLE) {\n+\t\t// only track if remote branch name matches\n+\t\t// (tracking.srcs must contain only one entry from find_tracked_branch with this config)\n+\t\tif (strncmp(tracking.srcs->items[0].string, \"refs/heads/\", 11))\n+\t\t\treturn;\n+\t\tif (strcmp(tracking.srcs->items[0].string + 11, new_ref))\n+\t\t\treturn;\n+\t}\n+\n \tif (tracking.srcs->nr < 1)\n \t\tstring_list_append(tracking.srcs, orig_ref);\n \tif (install_branch_config_multiple_remotes(config_flags, new_ref,\ndiff --git a/branch.h b/branch.h\nindex 04df2aa5b51..560b6b96a8f 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -12,6 +12,7 @@ enum branch_track {\n \tBRANCH_TRACK_EXPLICIT,\n \tBRANCH_TRACK_OVERRIDE,\n \tBRANCH_TRACK_INHERIT,\n+\tBRANCH_TRACK_SIMPLE,\n };\n \n extern enum branch_track git_branch_track;\ndiff --git a/config.c b/config.c\nindex e0c03d154c9..cc586ac816c 100644\n--- a/config.c\n+++ b/config.c\n@@ -1673,6 +1673,9 @@ static int git_default_branch_config(const char *var, const char *value)\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n \t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n+\t\t} else if (value && !strcmp(value, \"simple\")) {\n+\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\treturn 0;\n \t\t}\n \t\tgit_branch_track = git_config_bool(var, value);\n \t\treturn 0;\n-- \ngitgitgadget\n\n"},{"id":"449419","messageId":"3fa56f1d2a0dfbb41df2a38a6b0ea26333915eda.1645695940.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.git.1645695940.gitgitgadget@gmail.com","subject":"[PATCH 2/3] t3200: tests for new branch.autosetupmerge option \"simple\"","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-24T09:45:39Z","receivedAt":"2022-02-24T09:45:49Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nIn the previous commit a new autosetupmerge option was\nintroduced. Here the existing branch tests are extended\nwith three new cases testing this option - the obvious\nmatching-name and non-matching-name cases, and also a\nnon-matching-ref-type case.\n\nThe matching-name case needs to temporarily create\nan independent repo to fetch from, as the general\nstrategy in these tests of using the local repo as\nthe remote precludes locally branching with the same\nname as the \"remote\".\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n t/t3200-branch.sh | 35 +++++++++++++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 7a0ff75ba86..15cc58f1e64 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -886,6 +886,41 @@ test_expect_success 'branch from tag w/--track causes failure' '\n \ttest_must_fail git branch --track my11 foobar\n '\n \n+test_expect_success 'simple tracking works when remote branch name matches' '\n+\ttest_create_repo otherserver &&\n+\ttest_commit -C otherserver my_commit 1 &&\n+\tgit -C otherserver branch feature &&\n+\tgit config branch.autosetupmerge simple &&\n+\tgit config remote.otherserver.url otherserver &&\n+\tgit config remote.otherserver.fetch refs/heads/*:refs/remotes/otherserver/* &&\n+\tgit fetch otherserver &&\n+\tgit branch feature otherserver/feature &&\n+\trm -fr otherserver &&\n+\ttest $(git config branch.feature.remote) = otherserver &&\n+\ttest $(git config branch.feature.merge) = refs/heads/feature\n+'\n+\n+test_expect_success 'simple tracking skips when remote branch name does not match' '\n+\tgit config branch.autosetupmerge simple &&\n+\tgit config remote.local.url . &&\n+\tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+\t(git show-ref -q refs/remotes/local/main || git fetch local) &&\n+\tgit branch my-other local/main &&\n+\ttest -z \"$(git config branch.my-other.remote)\" &&\n+\ttest -z \"$(git config branch.my-other.merge)\"\n+'\n+\n+test_expect_success 'simple tracking skips when remote ref is not a branch' '\n+\tgit config branch.autosetupmerge simple &&\n+\tgit tag mytag12 main &&\n+\tgit config remote.localtags.url . &&\n+\tgit config remote.localtags.fetch refs/tags/*:refs/remotes/localtags/* &&\n+\t(git show-ref -q refs/remotes/localtags/mytag12 || git fetch localtags) &&\n+\tgit branch mytag12 localtags/mytag12 &&\n+\ttest -z \"$(git config branch.mytag12.remote)\" &&\n+\ttest -z \"$(git config branch.mytag12.merge)\"\n+'\n+\n test_expect_success '--set-upstream-to fails on multiple branches' '\n \techo \"fatal: too many arguments to set new upstream\" >expect &&\n \ttest_must_fail git branch --set-upstream-to main a b c 2>err &&\n-- \ngitgitgadget\n\n"},{"id":"449420","messageId":"39c14906e7b65843c2543682bb577c6a2253240a.1645695940.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.git.1645695940.gitgitgadget@gmail.com","subject":"[PATCH 3/3] branch documentation: new autosetupmerge option \"simple\"","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-24T09:45:40Z","receivedAt":"2022-02-24T09:45:51Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nUpdating the branch and config documentation to reflect\nthe new \"simple\" option to branch.autosetupmerge.\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n Documentation/config/branch.txt | 4 +++-\n Documentation/git-branch.txt    | 4 +++-\n 2 files changed, 6 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 1e0c7af014b..7b4e5ca5b74 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -9,7 +9,9 @@ branch.autoSetupMerge::\n \tautomatic setup is done when the starting point is either a\n \tlocal branch or remote-tracking branch; `inherit` -- if the starting point\n \thas a tracking configuration, it is copied to the new\n-\tbranch. This option defaults to true.\n+\tbranch; `simple` -- automatic setup is done when the starting point is\n+\ta remote-tracking branch and the new branch has the same name as the\n+\tremote branch. This option defaults to true.\n \n branch.autoSetupRebase::\n \tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex c8b4f9ce3c7..f99d6a6b008 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -227,7 +227,9 @@ want `git switch`, `git checkout` and `git branch` to always behave as if `--no-\n were given. Set it to `always` if you want this behavior when the\n start-point is either a local or remote-tracking branch. Set it to\n `inherit` if you want to copy the tracking configuration from the\n-branch point.\n+branch point. Set it to `simple` if you want this behavior only when\n+the start-point is a remote branch and the new branch has the same name\n+as the remote branch.\n +\n See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on\n how the `branch.<name>.remote` and `branch.<name>.merge` options are used.\n-- \ngitgitgadget\n"},{"id":"449496","messageId":"xmqqbkywm5qt.fsf@gitster.g","threadId":"57470","inReplyTo":"89efc1e15646599753baeab38ba2399dcbe868f1.1645695940.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/3] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-24T19:20:10Z","receivedAt":"2022-02-24T19:20:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Tao Klerks via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Tao Klerks <tao@klerks.biz>\n>\n> The push.defaut option \"simple\" helps produce\n\nThe cover letter wrappeed around 70 columns, which was much easier\nto read.\n\nPlease re-read Documentation/SubmittingPatches[[describe-changes]]\nsection before going forward.\n\n> predictable/understandable behavior for beginners,\n> where they don't accidentally push to the\n> \"wrong\" branch in centralized workflows. If they\n> create a local branch with a different name\n> and then try to do a plain push, it will\n> helpfully fail and explain why.\n>\n> However, such users can often find themselves\n> confused by the behavior of git after they first\n> branch, and before they push. At that stage,\n> their upstream tracking branch is the original\n> remote branch, and pull (for example) behaves\n> very differently to how it later does when they\n> create their own same-name remote branch.\n\nInstead of saying \"very differently\", explain what happens before\nand after the behaviour-change-triggering-event.\n\n> This commit introduces a new option to the\n> branch.autosetupmerge setting, \"simple\",\n> which is intended to be consistent with and\n> complementary to the push.default \"simple\"\n> option.\n>\n> It will set up automatic tracking for a new\n> branch only if the remote ref is a branch and\n> that remote branch name matches the new local\n> branch name. It is a reduction in scope of\n> the existing default option, \"true\".\n>\n> Signed-off-by: Tao Klerks <tao@klerks.biz>\n> ---\n>  branch.c | 9 +++++++++\n>  branch.h | 1 +\n>  config.c | 3 +++\n>  3 files changed, 13 insertions(+)\n>\n> diff --git a/branch.c b/branch.c\n> index 6b31df539a5..246bc82ce3c 100644\n> --- a/branch.c\n> +++ b/branch.c\n> @@ -256,6 +256,15 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,\n>  \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n>  \t\t    orig_ref);\n>  \n> +\tif (track == BRANCH_TRACK_SIMPLE) {\n> +\t\t// only track if remote branch name matches\n> +\t\t// (tracking.srcs must contain only one entry from find_tracked_branch with this config)\n\n\t/*\n\t * Our multi-line comments look exactly\n\t * like this.  They are not overly long,\n\t * have their opening and closing slash-aster\n\t * and aster-slash on their own line.\n\t */\n\n> +\t\tif (strncmp(tracking.srcs->items[0].string, \"refs/heads/\", 11))\n> +\t\t\treturn;\n> +\t\tif (strcmp(tracking.srcs->items[0].string + 11, new_ref))\n> +\t\t\treturn;\n\n\nDon't count hardcoded string length.  \n\n\t\tchar *tracked_branch;\n\t\tif (!skip_prefix(tracking.srcs->items[0].string,\n\t\t\t\t \"refs/heads/\", &tracked_branch) ||\n\t\t    strcmp(tracked_branch, new_ref))\n\t\t\treturn;\n\nor something along the line, perhaps?\n\nBut the post-context in this hunk makes the refernece to items[0] in\nthe above look very wrong.  It says tracking.srcs may not have even\na single item at this point in the original code flow.  If that is\ntrue, the above reference to ->items[0] may not be safely done at\nall.\n\nAlso, what happens when there are more than one in the items[]\narray?  What makes it sensible to use the first one, ignoring the\nothers?\n\n> +\t}\n> +\n>  \tif (tracking.srcs->nr < 1)\n>  \t\tstring_list_append(tracking.srcs, orig_ref);\n>  \tif (install_branch_config_multiple_remotes(config_flags, new_ref,\n> diff --git a/branch.h b/branch.h\n> index 04df2aa5b51..560b6b96a8f 100644\n> --- a/branch.h\n> +++ b/branch.h\n> @@ -12,6 +12,7 @@ enum branch_track {\n>  \tBRANCH_TRACK_EXPLICIT,\n>  \tBRANCH_TRACK_OVERRIDE,\n>  \tBRANCH_TRACK_INHERIT,\n> +\tBRANCH_TRACK_SIMPLE,\n>  };\n>  \n>  extern enum branch_track git_branch_track;\n> diff --git a/config.c b/config.c\n> index e0c03d154c9..cc586ac816c 100644\n> --- a/config.c\n> +++ b/config.c\n> @@ -1673,6 +1673,9 @@ static int git_default_branch_config(const char *var, const char *value)\n>  \t\t} else if (value && !strcmp(value, \"inherit\")) {\n>  \t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n>  \t\t\treturn 0;\n> +\t\t} else if (value && !strcmp(value, \"simple\")) {\n> +\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n> +\t\t\treturn 0;\n>  \t\t}\n>  \t\tgit_branch_track = git_config_bool(var, value);\n>  \t\treturn 0;\n\nThese two hunks look perfect.\n\n"},{"id":"449498","messageId":"xmqq1qzsm4w3.fsf@gitster.g","threadId":"57470","inReplyTo":"39c14906e7b65843c2543682bb577c6a2253240a.1645695940.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 3/3] branch documentation: new autosetupmerge option \"simple\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-24T19:38:36Z","receivedAt":"2022-02-24T19:38:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Tao Klerks via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Tao Klerks <tao@klerks.biz>\n>\n> Updating the branch and config documentation to reflect\n> the new \"simple\" option to branch.autosetupmerge.\n\nDocumentation/Submittingpatches[[describe-changes]].\n\nBut it would be moot; these changes are better done as part of [1/3]\nand in that case, updating the documentation (or testing the desired\nbehaviour, for that matter) is not something we need to justify\nseparately.  It is something we must done as part of the change.\n\n> diff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\n> index 1e0c7af014b..7b4e5ca5b74 100644\n> --- a/Documentation/config/branch.txt\n> +++ b/Documentation/config/branch.txt\n> @@ -9,7 +9,9 @@ branch.autoSetupMerge::\n>  \tautomatic setup is done when the starting point is either a\n>  \tlocal branch or remote-tracking branch; `inherit` -- if the starting point\n>  \thas a tracking configuration, it is copied to the new\n> -\tbranch. This option defaults to true.\n> +\tbranch; `simple` -- automatic setup is done when the starting point is\n\nIt may be clearer to say \"done only when\".  I dunno.\n\n> +\ta remote-tracking branch and the new branch has the same name as the\n> +\tremote branch. This option defaults to true.\n\n> diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\n> index c8b4f9ce3c7..f99d6a6b008 100644\n> --- a/Documentation/git-branch.txt\n> +++ b/Documentation/git-branch.txt\n> @@ -227,7 +227,9 @@ want `git switch`, `git checkout` and `git branch` to always behave as if `--no-\n>  were given. Set it to `always` if you want this behavior when the\n>  start-point is either a local or remote-tracking branch. Set it to\n>  `inherit` if you want to copy the tracking configuration from the\n> -branch point.\n> +branch point. Set it to `simple` if you want this behavior only when\n> +the start-point is a remote branch and the new branch has the same name\n> +as the remote branch.\n\nThe existing \"if you want this behaviour when\" is already awkward.\nWhat it means is that only those who want to use the \"start-point\"\nitself as the upstream whether the start-point is local or\nremote-tracking,can use \"always\" and does not get hurt.\n\nBut using the phrase for \"simple\" makes it even worse, as the\ncondition that the tracking behaviour kicks in is even narrower.  If\nyou know that start-point is not a remote-tracking branch (by the\nway, do not say \"remote branch\" when you mean \"remote-tracking\nbrnach\"), or its name is not the same as the local branch, you just\ndo not pass --track=simple from the command line.  Strike everything\nafter \"Set it to `simple`\" and replace with something like\n\n    `--track=simple` sets up the upstream information only when the\n    start-point is a remote-tracking branch and ...\n\nperhaps?\n\nThanks.\n"},{"id":"449620","messageId":"pull.1161.v2.git.1645815142.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.git.1645695940.gitgitgadget@gmail.com","subject":"[PATCH v2 0/2] adding new branch.autosetupmerge option \"simple\"","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-25T18:52:20Z","receivedAt":"2022-02-25T18:52:38Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"Re-sending with proposed fixes to concerns raised by Junio.\n\nThis patchset introduces a new option to the branch.autosetupmerge setting,\n\"simple\", which is intended to be consistent with and complementary to the\npush.default \"simple\" option.\n\nThe push.defaut option \"simple\" helps produce predictable/understandable\nbehavior for beginners, where they don't accidentally push to the \"wrong\"\nbranch in centralized workflows. If they create a local branch with a\ndifferent name and then try to do a plain push, it will helpfully fail and\nexplain why.\n\nHowever, such users can often find themselves confused by the behavior of\ngit after they first branch, and before they push. At that stage, their\nupstream tracking branch is the original remote branch, and pull will be\nbringing in \"upstream changes\" - eg all changes to \"main\", in a typical\nproject where that's where they branched from. On the other hand, once they\npush their new branch (dealing with the initial error, following\ninstructions to push to the right name), subsequent \"pull\" calls will behave\nas expected, only bring in any changes to that new branch they pushed.\n\nThe new option introduced here, with push.default set to simple, ensures\nthat push/pull behavior is generally consistent - tracking will be\nautomatically set up for branches that push will work for (and pull will be\nconsistent for) only.\n\nTao Klerks (2):\n  merge: new autosetupmerge option 'simple' for matching branches\n  t3200: tests for new branch.autosetupmerge option \"simple\"\n\n Documentation/config/branch.txt |  4 +++-\n Documentation/git-branch.txt    | 18 ++++++++++-------\n branch.c                        | 19 ++++++++++++++++++\n branch.h                        |  1 +\n config.c                        |  3 +++\n t/t3200-branch.sh               | 35 +++++++++++++++++++++++++++++++++\n 6 files changed, 72 insertions(+), 8 deletions(-)\n\n\nbase-commit: dab1b7905d0b295f1acef9785bb2b9cbb0fdec84\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1161%2FTaoK%2Ffeature-branch-autosetupmerge-simple-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1161/TaoK/feature-branch-autosetupmerge-simple-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/1161\n\nRange-diff vs v1:\n\n 1:  89efc1e1564 ! 1:  890e016bfc0 merge: new autosetupmerge option 'simple' for matching branches\n     @@ Metadata\n       ## Commit message ##\n          merge: new autosetupmerge option 'simple' for matching branches\n      \n     -    The push.defaut option \"simple\" helps produce\n     -    predictable/understandable behavior for beginners,\n     -    where they don't accidentally push to the\n     -    \"wrong\" branch in centralized workflows. If they\n     -    create a local branch with a different name\n     -    and then try to do a plain push, it will\n     -    helpfully fail and explain why.\n     +    This commit introduces a new option to the branch.autosetupmerge\n     +    setting, \"simple\", which is intended to be consistent with and\n     +    complementary to the push.default \"simple\" option.\n      \n     -    However, such users can often find themselves\n     -    confused by the behavior of git after they first\n     -    branch, and before they push. At that stage,\n     -    their upstream tracking branch is the original\n     -    remote branch, and pull (for example) behaves\n     -    very differently to how it later does when they\n     -    create their own same-name remote branch.\n     +    The push.defaut option \"simple\" helps produce\n     +    predictable/understandable behavior for beginners, where they don't\n     +    accidentally push to the \"wrong\" branch in centralized workflows. If\n     +    they create a local branch with a different name and then try to do a\n     +    plain push, it will helpfully fail and explain why.\n      \n     -    This commit introduces a new option to the\n     -    branch.autosetupmerge setting, \"simple\",\n     -    which is intended to be consistent with and\n     -    complementary to the push.default \"simple\"\n     -    option.\n     +    However, such users can often find themselves confused by the behavior\n     +    of git after they first branch, and before they push. At that stage,\n     +    their upstream tracking branch is the original remote branch, and pull\n     +    will be bringing in \"upstream changes\" - eg all changes to \"main\", in\n     +    a typical project where that's where they branched from.\n     +    On the other hand, once they push their new branch (dealing with the\n     +    initial error, following instructions to push to the right name),\n     +    subsequent \"pull\" calls will behave as expected, only bring in any\n     +    changes to that new branch they pushed.\n      \n     -    It will set up automatic tracking for a new\n     -    branch only if the remote ref is a branch and\n     -    that remote branch name matches the new local\n     -    branch name. It is a reduction in scope of\n     -    the existing default option, \"true\".\n     +    The new option introduced here, with push.default set to simple,\n     +    ensures that push/pull behavior is generally consistent - tracking\n     +    will be automatically set up for branches that push will work for\n     +    (and pull will be consistent for) only.\n      \n          Signed-off-by: Tao Klerks <tao@klerks.biz>\n      \n     + ## Documentation/config/branch.txt ##\n     +@@ Documentation/config/branch.txt: branch.autoSetupMerge::\n     + \tautomatic setup is done when the starting point is either a\n     + \tlocal branch or remote-tracking branch; `inherit` -- if the starting point\n     + \thas a tracking configuration, it is copied to the new\n     +-\tbranch. This option defaults to true.\n     ++\tbranch; `simple` -- automatic setup is done only when the starting point\n     ++\tis a remote-tracking branch and the new branch has the same name as the\n     ++\tremote branch. This option defaults to true.\n     + \n     + branch.autoSetupRebase::\n     + \tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\n     +\n     + ## Documentation/git-branch.txt ##\n     +@@ Documentation/git-branch.txt: The exact upstream branch is chosen depending on the optional argument:\n     + itself as the upstream; `--track=inherit` means to copy the upstream\n     + configuration of the start-point branch.\n     + +\n     +-`--track=direct` is the default when the start point is a remote-tracking branch.\n     +-Set the branch.autoSetupMerge configuration variable to `false` if you\n     +-want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n     +-were given. Set it to `always` if you want this behavior when the\n     +-start-point is either a local or remote-tracking branch. Set it to\n     +-`inherit` if you want to copy the tracking configuration from the\n     +-branch point.\n     ++The branch.autoSetupMerge configuration variable specifies how `git switch`,\n     ++`git checkout` and `git branch` should behave when neither `--track` nor\n     ++`--no-track` are specified:\n     +++\n     ++The default option, `true`, behaves as though `--track=direct`\n     ++were given whenever the start-point is a remote-tracking branch.\n     ++`false` behaves as if `--no-track` were given. `always` behaves as though\n     ++`--track=direct` were given. `inherit` behaves as though `--track=inherit`\n     ++were given. `simple` behaves as though `--track=direct` were given only when\n     ++the start-point is a remote-tracking branch and the new branch has the same\n     ++name as the remote branch.\n     + +\n     + See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on\n     + how the `branch.<name>.remote` and `branch.<name>.merge` options are used.\n     +\n       ## branch.c ##\n      @@ branch.c: static void setup_tracking(const char *new_ref, const char *orig_ref,\n     + \t\t\tgoto cleanup;\n     + \t\t}\n     + \n     ++\t/*\n     ++\t * This check does not apply to the BRANCH_TRACK_INHERIT\n     ++\t * option; you can inherit one or more tracking entries\n     ++\t * and the tracking.matches counter is not incremented.\n     ++\t */\n     + \tif (tracking.matches > 1)\n       \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n       \t\t    orig_ref);\n       \n      +\tif (track == BRANCH_TRACK_SIMPLE) {\n     -+\t\t// only track if remote branch name matches\n     -+\t\t// (tracking.srcs must contain only one entry from find_tracked_branch with this config)\n     -+\t\tif (strncmp(tracking.srcs->items[0].string, \"refs/heads/\", 11))\n     -+\t\t\treturn;\n     -+\t\tif (strcmp(tracking.srcs->items[0].string + 11, new_ref))\n     ++\t\t/*\n     ++\t\t * Only track if remote branch name matches.\n     ++\t\t * Reaching into items[0].string is safe because\n     ++\t\t * we know there is at least one and not more than\n     ++\t\t * one entry (because not BRANCH_TRACK_INHERIT).\n     ++\t\t */\n     ++\t\tconst char *tracked_branch;\n     ++\t\tif (!skip_prefix(tracking.srcs->items[0].string,\n     ++\t\t\t\t \"refs/heads/\", &tracked_branch) ||\n     ++\t\t    strcmp(tracked_branch, new_ref))\n      +\t\t\treturn;\n      +\t}\n      +\n 2:  3fa56f1d2a0 ! 2:  c16a8fe01e7 t3200: tests for new branch.autosetupmerge option \"simple\"\n     @@ Commit message\n      \n          The matching-name case needs to temporarily create\n          an independent repo to fetch from, as the general\n     -    strategy in these tests of using the local repo as\n     -    the remote precludes locally branching with the same\n     -    name as the \"remote\".\n     +    strategy of using the local repo as the remote in these\n     +    tests precludes locally branching with the same\n     +    name as in the \"remote\".\n      \n          Signed-off-by: Tao Klerks <tao@klerks.biz>\n      \n 3:  39c14906e7b < -:  ----------- branch documentation: new autosetupmerge option \"simple\"\n\n-- \ngitgitgadget\n"},{"id":"449621","messageId":"890e016bfc0809d25a4ae8ae924b23895f520810.1645815142.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v2.git.1645815142.gitgitgadget@gmail.com","subject":"[PATCH v2 1/2] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-25T18:52:21Z","receivedAt":"2022-02-25T18:52:40Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nThis commit introduces a new option to the branch.autosetupmerge\nsetting, \"simple\", which is intended to be consistent with and\ncomplementary to the push.default \"simple\" option.\n\nThe push.defaut option \"simple\" helps produce\npredictable/understandable behavior for beginners, where they don't\naccidentally push to the \"wrong\" branch in centralized workflows. If\nthey create a local branch with a different name and then try to do a\nplain push, it will helpfully fail and explain why.\n\nHowever, such users can often find themselves confused by the behavior\nof git after they first branch, and before they push. At that stage,\ntheir upstream tracking branch is the original remote branch, and pull\nwill be bringing in \"upstream changes\" - eg all changes to \"main\", in\na typical project where that's where they branched from.\nOn the other hand, once they push their new branch (dealing with the\ninitial error, following instructions to push to the right name),\nsubsequent \"pull\" calls will behave as expected, only bring in any\nchanges to that new branch they pushed.\n\nThe new option introduced here, with push.default set to simple,\nensures that push/pull behavior is generally consistent - tracking\nwill be automatically set up for branches that push will work for\n(and pull will be consistent for) only.\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n Documentation/config/branch.txt |  4 +++-\n Documentation/git-branch.txt    | 18 +++++++++++-------\n branch.c                        | 19 +++++++++++++++++++\n branch.h                        |  1 +\n config.c                        |  3 +++\n 5 files changed, 37 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 1e0c7af014b..8df10d07129 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -9,7 +9,9 @@ branch.autoSetupMerge::\n \tautomatic setup is done when the starting point is either a\n \tlocal branch or remote-tracking branch; `inherit` -- if the starting point\n \thas a tracking configuration, it is copied to the new\n-\tbranch. This option defaults to true.\n+\tbranch; `simple` -- automatic setup is done only when the starting point\n+\tis a remote-tracking branch and the new branch has the same name as the\n+\tremote branch. This option defaults to true.\n \n branch.autoSetupRebase::\n \tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex c8b4f9ce3c7..ae82378349d 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -221,13 +221,17 @@ The exact upstream branch is chosen depending on the optional argument:\n itself as the upstream; `--track=inherit` means to copy the upstream\n configuration of the start-point branch.\n +\n-`--track=direct` is the default when the start point is a remote-tracking branch.\n-Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n-were given. Set it to `always` if you want this behavior when the\n-start-point is either a local or remote-tracking branch. Set it to\n-`inherit` if you want to copy the tracking configuration from the\n-branch point.\n+The branch.autoSetupMerge configuration variable specifies how `git switch`,\n+`git checkout` and `git branch` should behave when neither `--track` nor\n+`--no-track` are specified:\n++\n+The default option, `true`, behaves as though `--track=direct`\n+were given whenever the start-point is a remote-tracking branch.\n+`false` behaves as if `--no-track` were given. `always` behaves as though\n+`--track=direct` were given. `inherit` behaves as though `--track=inherit`\n+were given. `simple` behaves as though `--track=direct` were given only when\n+the start-point is a remote-tracking branch and the new branch has the same\n+name as the remote branch.\n +\n See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on\n how the `branch.<name>.remote` and `branch.<name>.merge` options are used.\ndiff --git a/branch.c b/branch.c\nindex 6b31df539a5..81613ade8bf 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -252,10 +252,29 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,\n \t\t\tgoto cleanup;\n \t\t}\n \n+\t/*\n+\t * This check does not apply to the BRANCH_TRACK_INHERIT\n+\t * option; you can inherit one or more tracking entries\n+\t * and the tracking.matches counter is not incremented.\n+\t */\n \tif (tracking.matches > 1)\n \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n \t\t    orig_ref);\n \n+\tif (track == BRANCH_TRACK_SIMPLE) {\n+\t\t/*\n+\t\t * Only track if remote branch name matches.\n+\t\t * Reaching into items[0].string is safe because\n+\t\t * we know there is at least one and not more than\n+\t\t * one entry (because not BRANCH_TRACK_INHERIT).\n+\t\t */\n+\t\tconst char *tracked_branch;\n+\t\tif (!skip_prefix(tracking.srcs->items[0].string,\n+\t\t\t\t \"refs/heads/\", &tracked_branch) ||\n+\t\t    strcmp(tracked_branch, new_ref))\n+\t\t\treturn;\n+\t}\n+\n \tif (tracking.srcs->nr < 1)\n \t\tstring_list_append(tracking.srcs, orig_ref);\n \tif (install_branch_config_multiple_remotes(config_flags, new_ref,\ndiff --git a/branch.h b/branch.h\nindex 04df2aa5b51..560b6b96a8f 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -12,6 +12,7 @@ enum branch_track {\n \tBRANCH_TRACK_EXPLICIT,\n \tBRANCH_TRACK_OVERRIDE,\n \tBRANCH_TRACK_INHERIT,\n+\tBRANCH_TRACK_SIMPLE,\n };\n \n extern enum branch_track git_branch_track;\ndiff --git a/config.c b/config.c\nindex e0c03d154c9..cc586ac816c 100644\n--- a/config.c\n+++ b/config.c\n@@ -1673,6 +1673,9 @@ static int git_default_branch_config(const char *var, const char *value)\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n \t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n+\t\t} else if (value && !strcmp(value, \"simple\")) {\n+\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\treturn 0;\n \t\t}\n \t\tgit_branch_track = git_config_bool(var, value);\n \t\treturn 0;\n-- \ngitgitgadget\n\n"},{"id":"449622","messageId":"c16a8fe01e7bb5811b883346bf381525b413bb9c.1645815142.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v2.git.1645815142.gitgitgadget@gmail.com","subject":"[PATCH v2 2/2] t3200: tests for new branch.autosetupmerge option \"simple\"","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-25T18:52:22Z","receivedAt":"2022-02-25T18:52:43Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nIn the previous commit a new autosetupmerge option was\nintroduced. Here the existing branch tests are extended\nwith three new cases testing this option - the obvious\nmatching-name and non-matching-name cases, and also a\nnon-matching-ref-type case.\n\nThe matching-name case needs to temporarily create\nan independent repo to fetch from, as the general\nstrategy of using the local repo as the remote in these\ntests precludes locally branching with the same\nname as in the \"remote\".\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n t/t3200-branch.sh | 35 +++++++++++++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 7a0ff75ba86..15cc58f1e64 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -886,6 +886,41 @@ test_expect_success 'branch from tag w/--track causes failure' '\n \ttest_must_fail git branch --track my11 foobar\n '\n \n+test_expect_success 'simple tracking works when remote branch name matches' '\n+\ttest_create_repo otherserver &&\n+\ttest_commit -C otherserver my_commit 1 &&\n+\tgit -C otherserver branch feature &&\n+\tgit config branch.autosetupmerge simple &&\n+\tgit config remote.otherserver.url otherserver &&\n+\tgit config remote.otherserver.fetch refs/heads/*:refs/remotes/otherserver/* &&\n+\tgit fetch otherserver &&\n+\tgit branch feature otherserver/feature &&\n+\trm -fr otherserver &&\n+\ttest $(git config branch.feature.remote) = otherserver &&\n+\ttest $(git config branch.feature.merge) = refs/heads/feature\n+'\n+\n+test_expect_success 'simple tracking skips when remote branch name does not match' '\n+\tgit config branch.autosetupmerge simple &&\n+\tgit config remote.local.url . &&\n+\tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+\t(git show-ref -q refs/remotes/local/main || git fetch local) &&\n+\tgit branch my-other local/main &&\n+\ttest -z \"$(git config branch.my-other.remote)\" &&\n+\ttest -z \"$(git config branch.my-other.merge)\"\n+'\n+\n+test_expect_success 'simple tracking skips when remote ref is not a branch' '\n+\tgit config branch.autosetupmerge simple &&\n+\tgit tag mytag12 main &&\n+\tgit config remote.localtags.url . &&\n+\tgit config remote.localtags.fetch refs/tags/*:refs/remotes/localtags/* &&\n+\t(git show-ref -q refs/remotes/localtags/mytag12 || git fetch localtags) &&\n+\tgit branch mytag12 localtags/mytag12 &&\n+\ttest -z \"$(git config branch.mytag12.remote)\" &&\n+\ttest -z \"$(git config branch.mytag12.merge)\"\n+'\n+\n test_expect_success '--set-upstream-to fails on multiple branches' '\n \techo \"fatal: too many arguments to set new upstream\" >expect &&\n \ttest_must_fail git branch --set-upstream-to main a b c 2>err &&\n-- \ngitgitgadget\n"},{"id":"449633","messageId":"xmqqczjaaeiv.fsf@gitster.g","threadId":"57470","inReplyTo":"890e016bfc0809d25a4ae8ae924b23895f520810.1645815142.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/2] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-25T20:15:52Z","receivedAt":"2022-02-25T20:16:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Tao Klerks via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Tao Klerks <tao@klerks.biz>\n>\n> This commit introduces a new option to the branch.autosetupmerge\n> setting, \"simple\", which is intended to be consistent with and\n> complementary to the push.default \"simple\" option.\n\nDocumentation/SubmittingPatches.\n\nWe do not say \"This commit does this\".  Instead, we say \"Add a new\noption that does X\".  Usually that is done after the explanation of\nthe status quo is finished to make readers understand what the\nproblem the change is trying to solve is.  So...\n\n> The push.defaut option \"simple\" helps produce\n> predictable/understandable behavior for beginners, where they don't\n> accidentally push to the \"wrong\" branch in centralized workflows. If\n> they create a local branch with a different name and then try to do a\n> plain push, it will helpfully fail and explain why.\n\n... this would be a better first paragraph to start the proposed log\nmessage with.\n\n\tWith push.default set to \"simple\", the users fork from a\n\tlocal branch from a remote-tracking branch of the same name,\n\tand are protected from a mistake to push to a wrong branch.\n\tIf they create a ... and explain why.\n\n> However, such users can often find themselves confused by the behavior\n> of git after they first branch, and before they push. At that stage,\n\nDepending on how they \"branch\", they may or may not be confused.  Be\nmore specific to illustrate what problem you are solving, e.g.\n\n\t... after they create a new local branch from a\n\tremote-tracking branch with a different name.\n\n> their upstream tracking branch is the original remote branch, and pull\n> will be bringing in \"upstream changes\" - eg all changes to \"main\", in\n> a typical project where that's where they branched from.\n\nOK.  So \"pull\" tries to grab from the upstream (which is most likely\nan integration branch with bland name like 'master', 'main' or\n'trunk'), while \"push\" does not allow the work on a branch (which is\nnamed after the theme of the work and not a bland name suitable for\nintegration branches) to be pushed to the upstream.\n\nIt may probably not be so clear why it is a problem to many readers,\nI suspect.  Isn't that what happens in a typical triangular workflow\nto work with a project with a centralized repository?  You fork from\nthe integration branch shared among project participants, you work on\nyour own branch, occasionally rebasing on top of the updated upstream,\nand when you are done, try to push it out to the integration branch,\nand that final leg needs to be explicit to make sure you won't push\nout to a wrong branch (in this case, a new branch at the remote with\nthe same name as your local topic branch) by mistake?\n\n> On the other hand, once they push their new branch (dealing with the\n> initial error, following instructions to push to the right name),\n> subsequent \"pull\" calls will behave as expected, only bring in any\n> changes to that new branch they pushed.\n\nIs that because the upstream for this local branch is updated?\nThe \"following instructions...\" part may want to clarify.\n\nIt somehow feels that a better solution might be to suggest\nupdating the push.default to 'upstream' when it happens?  I dunno.\n\nIn any case, now we have explained what happens with today's code,\nhere is a good place to propose a solution.  Do so in imperative,\ne.g.\n\n    Allow branch.autosetupmerge to take a new value, 'simple', which \n    sets the upstream of the new branch only when the local branch\n    being created has the same name as the remote-tracking branch it\n    was created out of.  Otherwise the new local branch will not get\n    any tracking information and \n\nor something, perhaps?\n\n> +\t/*\n> +\t * This check does not apply to the BRANCH_TRACK_INHERIT\n> +\t * option; you can inherit one or more tracking entries\n> +\t * and the tracking.matches counter is not incremented.\n> +\t */\n>  \tif (tracking.matches > 1)\n>  \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n>  \t\t    orig_ref);\n\n> +\tif (track == BRANCH_TRACK_SIMPLE) {\n> +\t\t/*\n> +\t\t * Only track if remote branch name matches.\n> +\t\t * Reaching into items[0].string is safe because\n> +\t\t * we know there is at least one and not more than\n> +\t\t * one entry (because not BRANCH_TRACK_INHERIT).\n> +\t\t */\n\nOK, because in the pre-context of this hunk, we would have jumped to\ncleanup: if there were no .matches; so we know there should at least\nbe one, and we rejected ambiguous matches already, so we know there\nis only one.\n\n> +\t\tconst char *tracked_branch;\n> +\t\tif (!skip_prefix(tracking.srcs->items[0].string,\n> +\t\t\t\t \"refs/heads/\", &tracked_branch) ||\n> +\t\t    strcmp(tracked_branch, new_ref))\n> +\t\t\treturn;\n> +\t}\n\nThat looks sensible.  Sometimes we do not set tracking information\nand just return.\n"},{"id":"449721","messageId":"CAPMMpoiJyWQp+UtaZWeWodkjVkm0buSykfuDZDrM4d1eC3vstQ@mail.gmail.com","threadId":"57470","inReplyTo":"xmqqczjaaeiv.fsf@gitster.g","subject":"Re: [PATCH v2 1/2] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-02-27T23:59:31Z","receivedAt":"2022-02-27T23:59:46Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Fri, Feb 25, 2022 at 9:15 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Tao Klerks via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > This commit introduces a new option to the branch.autosetupmerge\n> > setting, \"simple\", which is intended to be consistent with and\n> > complementary to the push.default \"simple\" option.\n>\n> Documentation/SubmittingPatches.\n>\n> We do not say \"This commit does this\".  Instead, we say \"Add a new\n> option that does X\".  Usually that is done after the explanation of\n> the status quo is finished to make readers understand what the\n> problem the change is trying to solve is.  So...\n\nYep, sorry, thx! (fixed, reroll coming!)\n\n>\n> > The push.defaut option \"simple\" helps produce\n> > predictable/understandable behavior for beginners, where they don't\n> > accidentally push to the \"wrong\" branch in centralized workflows. If\n> > they create a local branch with a different name and then try to do a\n> > plain push, it will helpfully fail and explain why.\n>\n> ... this would be a better first paragraph to start the proposed log\n> message with.\n>\n>         With push.default set to \"simple\", the users fork from a\n>         local branch from a remote-tracking branch of the same name,\n>         and are protected from a mistake to push to a wrong branch.\n>         If they create a ... and explain why.\n>\n> > However, such users can often find themselves confused by the behavior\n> > of git after they first branch, and before they push. At that stage,\n>\n> Depending on how they \"branch\", they may or may not be confused.  Be\n> more specific to illustrate what problem you are solving, e.g.\n>\n>         ... after they create a new local branch from a\n>         remote-tracking branch with a different name.\n>\n> > their upstream tracking branch is the original remote branch, and pull\n> > will be bringing in \"upstream changes\" - eg all changes to \"main\", in\n> > a typical project where that's where they branched from.\n>\n> OK.  So \"pull\" tries to grab from the upstream (which is most likely\n> an integration branch with bland name like 'master', 'main' or\n> 'trunk'), while \"push\" does not allow the work on a branch (which is\n> named after the theme of the work and not a bland name suitable for\n> integration branches) to be pushed to the upstream.\n>\n> It may probably not be so clear why it is a problem to many readers,\n> I suspect.  Isn't that what happens in a typical triangular workflow\n> to work with a project with a centralized repository?  You fork from\n> the integration branch shared among project participants, you work on\n> your own branch, occasionally rebasing on top of the updated upstream,\n> and when you are done, try to push it out to the integration branch,\n> and that final leg needs to be explicit to make sure you won't push\n> out to a wrong branch (in this case, a new branch at the remote with\n> the same name as your local topic branch) by mistake?\n>\n> > On the other hand, once they push their new branch (dealing with the\n> > initial error, following instructions to push to the right name),\n> > subsequent \"pull\" calls will behave as expected, only bring in any\n> > changes to that new branch they pushed.\n>\n> Is that because the upstream for this local branch is updated?\n> The \"following instructions...\" part may want to clarify.\n>\n> It somehow feels that a better solution might be to suggest\n> updating the push.default to 'upstream' when it happens?  I dunno.\n>\n> In any case, now we have explained what happens with today's code,\n> here is a good place to propose a solution.  Do so in imperative,\n> e.g.\n>\n>     Allow branch.autosetupmerge to take a new value, 'simple', which\n>     sets the upstream of the new branch only when the local branch\n>     being created has the same name as the remote-tracking branch it\n>     was created out of.  Otherwise the new local branch will not get\n>     any tracking information and\n>\n> or something, perhaps?\n\nThank you for taking the time to make sense of the rambling /\nlargely incoherent message and helping me identify some context\nother reviewers will expect.\n\nI've rewritten the whole thing to try to address these concerns, but of\ncourse I may well have introduced a whole new set. If nothing else, it's\nbecome even more rambling. Is there a recommended limit to the\nlength of a commit message?\n"},{"id":"449724","messageId":"pull.1161.v3.git.1646032466.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v2.git.1645815142.gitgitgadget@gmail.com","subject":"[PATCH v3 0/2] adding new branch.autosetupmerge option \"simple\"","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-28T07:14:24Z","receivedAt":"2022-02-28T07:14:34Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"Re-sending with proposed fixes to concerns raised by Junio.\n\nThis patchset introduces a new option to the branch.autosetupmerge setting,\n\"simple\", which is intended to be consistent with and complementary to the\npush.default \"simple\" option.\n\nThe push.defaut option \"simple\" helps produce predictable/understandable\nbehavior for beginners, where they don't accidentally push to the \"wrong\"\nbranch in centralized workflows. If they create a local branch with a\ndifferent name and then try to do a plain push, it will helpfully fail and\nexplain why.\n\nHowever, such users can often find themselves confused by the behavior of\ngit after they first branch, and before they push. At that stage, their\nupstream tracking branch is the original remote branch, and pull will be\nbringing in \"upstream changes\" - eg all changes to \"main\", in a typical\nproject where that's where they branched from. On the other hand, once they\npush their new branch (dealing with the initial error, following\ninstructions to push to the right name), subsequent \"pull\" calls will behave\nas expected, only bring in any changes to that new branch they pushed.\n\nThe new option introduced here, with push.default set to simple, ensures\nthat push/pull behavior is generally consistent - tracking will be\nautomatically set up for branches that push will work for (and pull will be\nconsistent for) only.\n\nTao Klerks (2):\n  merge: new autosetupmerge option 'simple' for matching branches\n  t3200: tests for new branch.autosetupmerge option \"simple\"\n\n Documentation/config/branch.txt |  4 +++-\n Documentation/git-branch.txt    | 18 ++++++++++-------\n branch.c                        | 19 ++++++++++++++++++\n branch.h                        |  1 +\n config.c                        |  3 +++\n t/t3200-branch.sh               | 35 +++++++++++++++++++++++++++++++++\n 6 files changed, 72 insertions(+), 8 deletions(-)\n\n\nbase-commit: dab1b7905d0b295f1acef9785bb2b9cbb0fdec84\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1161%2FTaoK%2Ffeature-branch-autosetupmerge-simple-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1161/TaoK/feature-branch-autosetupmerge-simple-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/1161\n\nRange-diff vs v2:\n\n 1:  890e016bfc0 ! 1:  0b5d4789512 merge: new autosetupmerge option 'simple' for matching branches\n     @@ Metadata\n       ## Commit message ##\n          merge: new autosetupmerge option 'simple' for matching branches\n      \n     -    This commit introduces a new option to the branch.autosetupmerge\n     -    setting, \"simple\", which is intended to be consistent with and\n     -    complementary to the push.default \"simple\" option.\n     -\n     -    The push.defaut option \"simple\" helps produce\n     -    predictable/understandable behavior for beginners, where they don't\n     -    accidentally push to the \"wrong\" branch in centralized workflows. If\n     -    they create a local branch with a different name and then try to do a\n     -    plain push, it will helpfully fail and explain why.\n     -\n     -    However, such users can often find themselves confused by the behavior\n     -    of git after they first branch, and before they push. At that stage,\n     -    their upstream tracking branch is the original remote branch, and pull\n     -    will be bringing in \"upstream changes\" - eg all changes to \"main\", in\n     -    a typical project where that's where they branched from.\n     -    On the other hand, once they push their new branch (dealing with the\n     -    initial error, following instructions to push to the right name),\n     -    subsequent \"pull\" calls will behave as expected, only bring in any\n     -    changes to that new branch they pushed.\n     -\n     -    The new option introduced here, with push.default set to simple,\n     -    ensures that push/pull behavior is generally consistent - tracking\n     -    will be automatically set up for branches that push will work for\n     -    (and pull will be consistent for) only.\n     +    With the default push.default option, \"simple\", beginners are\n     +    protected from accidentally pushing to the \"wrong\" branch in\n     +    centralized workflows: if the remote tracking branch they would push\n     +    to does not have the same name as the local branch, and they try to do\n     +    a \"default push\", they get an error and explanation with options.\n     +\n     +    There is a particular centralized workflow where this often happens:\n     +    a user branches to a new local feature branch from an existing\n     +    upstream branch, eg with \"checkout -b feature1 origin/master\". With\n     +    the default branch.autosetupmerge configuration (value \"true\"), git\n     +    will automatically add origin/master as the remote tracking branch.\n     +\n     +    When the user pushes with \"git push\", they get an error, and (amongst\n     +    other things) a suggestion to run \"git push origin HEAD\". Eventually\n     +    they figure out to add \"-u\" to change the tracking branch, or they set\n     +    push.default to \"current\", or some tooling does one or the other of\n     +    these things for them.\n     +\n     +    When one of their coworkers works on the same branch, they don't get\n     +    any of that weirdness. They just \"git checkout feature1\" and\n     +    everything works exactly as they expect, with the shared remote branch\n     +    set up as remote tracking branch, and push and pull working out of the\n     +    box.\n     +\n     +    The \"stable state\" for this way of working is that local branches have\n     +    the same-name remote tracking branch (origin/feature1 in this\n     +    example), and multiple people can work on that remote feature branch\n     +    at the same time, trusting \"git pull\" to merge or rebase as required\n     +    for them to be able to push their interim changes to that same feature\n     +    branch on that same remote.\n     +\n     +    (merging from the upstream \"master\" branch, and merging back to it,\n     +    are separate more involved processes in this flow).\n     +\n     +    There is a problem in this flow/way of working, however, which is that\n     +    the first user, when they first branched from origin/master, ended up\n     +    with the \"wrong\" remote tracking branch (different from the stable\n     +    state). For a while, before they pushed (and maybe longer, if they\n     +    don't use -u/--set-upstream), their \"git pull\" wasn't getting other\n     +    users' changes to the feature branch - it was getting any changes from\n     +    the remote \"master\" branch instead (a completely different class of\n     +    changes!)\n     +\n     +    Any experienced git user will presumably say \"well yeah, that's what\n     +    it means to have the remote tracking branch set to origin/master!\" -\n     +    but that user didn't *ask* to have the remote master branch added as\n     +    remote tracking branch - that just happened automatically when they\n     +    branched their feature branch. They didn't necessarily even notice or\n     +    understand the meaning of the \"set up to track 'origin/master'\"\n     +    message when they created the branch - especially if they are using a\n     +    GUI.\n     +\n     +    Looking at how to fix this, you might think \"OK, so disable auto setup\n     +    of remote tracking - set branch.autosetupmerge to false\" - but that\n     +    will inconvenience the *second* user in this story - the one who just\n     +    wanted to start working on the feature branch. The first and second\n     +    users swap roles at different points in time of course - they should\n     +    both have a sane configuration that does the right thing in both\n     +    situations.\n     +\n     +    Make these flows painless by introducing a new branch.autosetupmerge\n     +    option called \"simple\", to match the same-name \"push.default\" option\n     +    that makes similar assumptions.\n     +\n     +    This new option automatically sets up tracking in a *subset* of the\n     +    current default situations: when the original ref is a remote tracking\n     +    branch *and* has the same branch name on the remote (as the new local\n     +    branch name).\n     +\n     +    With this new configuration, in the example situation above, the first\n     +    user does *not* get origin/master set up as the tracking branch for\n     +    the new local branch. If they \"git pull\" in their new local-only\n     +    branch, they get an error explaining there is no upstream branch -\n     +    which makes sense and is helpful. If they \"git push\", they get an\n     +    error explaining how to push *and* suggesting they specify\n     +    --set-upstream - which is exactly the right thing to do for them.\n     +\n     +    This new option is likely not appropriate for users intentionally\n     +    implementing a \"triangular workflow\" with a shared upstream tracking\n     +    branch, that they \"git pull\" in and a \"private\" feature branch that\n     +    they push/force-push to just for remote safe-keeping until they are\n     +    ready to push up to the shared branch explicitly/separately. Such\n     +    users are likely to prefer keeping the current default\n     +    merge.autosetupmerge=true behavior, and change their push.default to\n     +    \"current\".\n      \n          Signed-off-by: Tao Klerks <tao@klerks.biz>\n      \n 2:  c16a8fe01e7 ! 2:  d5b18c7949f t3200: tests for new branch.autosetupmerge option \"simple\"\n     @@ Metadata\n       ## Commit message ##\n          t3200: tests for new branch.autosetupmerge option \"simple\"\n      \n     -    In the previous commit a new autosetupmerge option was\n     -    introduced. Here the existing branch tests are extended\n     -    with three new cases testing this option - the obvious\n     -    matching-name and non-matching-name cases, and also a\n     -    non-matching-ref-type case.\n     +    In the previous commit a new autosetupmerge option was introduced.\n      \n     -    The matching-name case needs to temporarily create\n     -    an independent repo to fetch from, as the general\n     -    strategy of using the local repo as the remote in these\n     -    tests precludes locally branching with the same\n     +    Extend the existing branch tests with three new cases testing this\n     +    option - the obvious matching-name and non-matching-name cases, and\n     +    also a non-matching-ref-type case.\n     +\n     +    The matching-name case needs to temporarily create an independent\n     +    repo to fetch from, as the general strategy of using the local repo as\n     +    the remote in these tests precludes locally branching with the same\n          name as in the \"remote\".\n      \n          Signed-off-by: Tao Klerks <tao@klerks.biz>\n\n-- \ngitgitgadget\n"},{"id":"449725","messageId":"d5b18c7949fdea966d31b2b8ca3f8aa8ed3a86b6.1646032466.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v3.git.1646032466.gitgitgadget@gmail.com","subject":"[PATCH v3 2/2] t3200: tests for new branch.autosetupmerge option \"simple\"","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-28T07:14:26Z","receivedAt":"2022-02-28T07:14:36Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nIn the previous commit a new autosetupmerge option was introduced.\n\nExtend the existing branch tests with three new cases testing this\noption - the obvious matching-name and non-matching-name cases, and\nalso a non-matching-ref-type case.\n\nThe matching-name case needs to temporarily create an independent\nrepo to fetch from, as the general strategy of using the local repo as\nthe remote in these tests precludes locally branching with the same\nname as in the \"remote\".\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n t/t3200-branch.sh | 35 +++++++++++++++++++++++++++++++++++\n 1 file changed, 35 insertions(+)\n\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 7a0ff75ba86..15cc58f1e64 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -886,6 +886,41 @@ test_expect_success 'branch from tag w/--track causes failure' '\n \ttest_must_fail git branch --track my11 foobar\n '\n \n+test_expect_success 'simple tracking works when remote branch name matches' '\n+\ttest_create_repo otherserver &&\n+\ttest_commit -C otherserver my_commit 1 &&\n+\tgit -C otherserver branch feature &&\n+\tgit config branch.autosetupmerge simple &&\n+\tgit config remote.otherserver.url otherserver &&\n+\tgit config remote.otherserver.fetch refs/heads/*:refs/remotes/otherserver/* &&\n+\tgit fetch otherserver &&\n+\tgit branch feature otherserver/feature &&\n+\trm -fr otherserver &&\n+\ttest $(git config branch.feature.remote) = otherserver &&\n+\ttest $(git config branch.feature.merge) = refs/heads/feature\n+'\n+\n+test_expect_success 'simple tracking skips when remote branch name does not match' '\n+\tgit config branch.autosetupmerge simple &&\n+\tgit config remote.local.url . &&\n+\tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+\t(git show-ref -q refs/remotes/local/main || git fetch local) &&\n+\tgit branch my-other local/main &&\n+\ttest -z \"$(git config branch.my-other.remote)\" &&\n+\ttest -z \"$(git config branch.my-other.merge)\"\n+'\n+\n+test_expect_success 'simple tracking skips when remote ref is not a branch' '\n+\tgit config branch.autosetupmerge simple &&\n+\tgit tag mytag12 main &&\n+\tgit config remote.localtags.url . &&\n+\tgit config remote.localtags.fetch refs/tags/*:refs/remotes/localtags/* &&\n+\t(git show-ref -q refs/remotes/localtags/mytag12 || git fetch localtags) &&\n+\tgit branch mytag12 localtags/mytag12 &&\n+\ttest -z \"$(git config branch.mytag12.remote)\" &&\n+\ttest -z \"$(git config branch.mytag12.merge)\"\n+'\n+\n test_expect_success '--set-upstream-to fails on multiple branches' '\n \techo \"fatal: too many arguments to set new upstream\" >expect &&\n \ttest_must_fail git branch --set-upstream-to main a b c 2>err &&\n-- \ngitgitgadget\n"},{"id":"449726","messageId":"0b5d47895120539d6a72a91398f33a0e33df7cd5.1646032466.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v3.git.1646032466.gitgitgadget@gmail.com","subject":"[PATCH v3 1/2] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-02-28T07:14:25Z","receivedAt":"2022-02-28T07:14:38Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nWith the default push.default option, \"simple\", beginners are\nprotected from accidentally pushing to the \"wrong\" branch in\ncentralized workflows: if the remote tracking branch they would push\nto does not have the same name as the local branch, and they try to do\na \"default push\", they get an error and explanation with options.\n\nThere is a particular centralized workflow where this often happens:\na user branches to a new local feature branch from an existing\nupstream branch, eg with \"checkout -b feature1 origin/master\". With\nthe default branch.autosetupmerge configuration (value \"true\"), git\nwill automatically add origin/master as the remote tracking branch.\n\nWhen the user pushes with \"git push\", they get an error, and (amongst\nother things) a suggestion to run \"git push origin HEAD\". Eventually\nthey figure out to add \"-u\" to change the tracking branch, or they set\npush.default to \"current\", or some tooling does one or the other of\nthese things for them.\n\nWhen one of their coworkers works on the same branch, they don't get\nany of that weirdness. They just \"git checkout feature1\" and\neverything works exactly as they expect, with the shared remote branch\nset up as remote tracking branch, and push and pull working out of the\nbox.\n\nThe \"stable state\" for this way of working is that local branches have\nthe same-name remote tracking branch (origin/feature1 in this\nexample), and multiple people can work on that remote feature branch\nat the same time, trusting \"git pull\" to merge or rebase as required\nfor them to be able to push their interim changes to that same feature\nbranch on that same remote.\n\n(merging from the upstream \"master\" branch, and merging back to it,\nare separate more involved processes in this flow).\n\nThere is a problem in this flow/way of working, however, which is that\nthe first user, when they first branched from origin/master, ended up\nwith the \"wrong\" remote tracking branch (different from the stable\nstate). For a while, before they pushed (and maybe longer, if they\ndon't use -u/--set-upstream), their \"git pull\" wasn't getting other\nusers' changes to the feature branch - it was getting any changes from\nthe remote \"master\" branch instead (a completely different class of\nchanges!)\n\nAny experienced git user will presumably say \"well yeah, that's what\nit means to have the remote tracking branch set to origin/master!\" -\nbut that user didn't *ask* to have the remote master branch added as\nremote tracking branch - that just happened automatically when they\nbranched their feature branch. They didn't necessarily even notice or\nunderstand the meaning of the \"set up to track 'origin/master'\"\nmessage when they created the branch - especially if they are using a\nGUI.\n\nLooking at how to fix this, you might think \"OK, so disable auto setup\nof remote tracking - set branch.autosetupmerge to false\" - but that\nwill inconvenience the *second* user in this story - the one who just\nwanted to start working on the feature branch. The first and second\nusers swap roles at different points in time of course - they should\nboth have a sane configuration that does the right thing in both\nsituations.\n\nMake these flows painless by introducing a new branch.autosetupmerge\noption called \"simple\", to match the same-name \"push.default\" option\nthat makes similar assumptions.\n\nThis new option automatically sets up tracking in a *subset* of the\ncurrent default situations: when the original ref is a remote tracking\nbranch *and* has the same branch name on the remote (as the new local\nbranch name).\n\nWith this new configuration, in the example situation above, the first\nuser does *not* get origin/master set up as the tracking branch for\nthe new local branch. If they \"git pull\" in their new local-only\nbranch, they get an error explaining there is no upstream branch -\nwhich makes sense and is helpful. If they \"git push\", they get an\nerror explaining how to push *and* suggesting they specify\n--set-upstream - which is exactly the right thing to do for them.\n\nThis new option is likely not appropriate for users intentionally\nimplementing a \"triangular workflow\" with a shared upstream tracking\nbranch, that they \"git pull\" in and a \"private\" feature branch that\nthey push/force-push to just for remote safe-keeping until they are\nready to push up to the shared branch explicitly/separately. Such\nusers are likely to prefer keeping the current default\nmerge.autosetupmerge=true behavior, and change their push.default to\n\"current\".\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n Documentation/config/branch.txt |  4 +++-\n Documentation/git-branch.txt    | 18 +++++++++++-------\n branch.c                        | 19 +++++++++++++++++++\n branch.h                        |  1 +\n config.c                        |  3 +++\n 5 files changed, 37 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 1e0c7af014b..8df10d07129 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -9,7 +9,9 @@ branch.autoSetupMerge::\n \tautomatic setup is done when the starting point is either a\n \tlocal branch or remote-tracking branch; `inherit` -- if the starting point\n \thas a tracking configuration, it is copied to the new\n-\tbranch. This option defaults to true.\n+\tbranch; `simple` -- automatic setup is done only when the starting point\n+\tis a remote-tracking branch and the new branch has the same name as the\n+\tremote branch. This option defaults to true.\n \n branch.autoSetupRebase::\n \tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex c8b4f9ce3c7..ae82378349d 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -221,13 +221,17 @@ The exact upstream branch is chosen depending on the optional argument:\n itself as the upstream; `--track=inherit` means to copy the upstream\n configuration of the start-point branch.\n +\n-`--track=direct` is the default when the start point is a remote-tracking branch.\n-Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n-were given. Set it to `always` if you want this behavior when the\n-start-point is either a local or remote-tracking branch. Set it to\n-`inherit` if you want to copy the tracking configuration from the\n-branch point.\n+The branch.autoSetupMerge configuration variable specifies how `git switch`,\n+`git checkout` and `git branch` should behave when neither `--track` nor\n+`--no-track` are specified:\n++\n+The default option, `true`, behaves as though `--track=direct`\n+were given whenever the start-point is a remote-tracking branch.\n+`false` behaves as if `--no-track` were given. `always` behaves as though\n+`--track=direct` were given. `inherit` behaves as though `--track=inherit`\n+were given. `simple` behaves as though `--track=direct` were given only when\n+the start-point is a remote-tracking branch and the new branch has the same\n+name as the remote branch.\n +\n See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on\n how the `branch.<name>.remote` and `branch.<name>.merge` options are used.\ndiff --git a/branch.c b/branch.c\nindex 6b31df539a5..81613ade8bf 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -252,10 +252,29 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,\n \t\t\tgoto cleanup;\n \t\t}\n \n+\t/*\n+\t * This check does not apply to the BRANCH_TRACK_INHERIT\n+\t * option; you can inherit one or more tracking entries\n+\t * and the tracking.matches counter is not incremented.\n+\t */\n \tif (tracking.matches > 1)\n \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n \t\t    orig_ref);\n \n+\tif (track == BRANCH_TRACK_SIMPLE) {\n+\t\t/*\n+\t\t * Only track if remote branch name matches.\n+\t\t * Reaching into items[0].string is safe because\n+\t\t * we know there is at least one and not more than\n+\t\t * one entry (because not BRANCH_TRACK_INHERIT).\n+\t\t */\n+\t\tconst char *tracked_branch;\n+\t\tif (!skip_prefix(tracking.srcs->items[0].string,\n+\t\t\t\t \"refs/heads/\", &tracked_branch) ||\n+\t\t    strcmp(tracked_branch, new_ref))\n+\t\t\treturn;\n+\t}\n+\n \tif (tracking.srcs->nr < 1)\n \t\tstring_list_append(tracking.srcs, orig_ref);\n \tif (install_branch_config_multiple_remotes(config_flags, new_ref,\ndiff --git a/branch.h b/branch.h\nindex 04df2aa5b51..560b6b96a8f 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -12,6 +12,7 @@ enum branch_track {\n \tBRANCH_TRACK_EXPLICIT,\n \tBRANCH_TRACK_OVERRIDE,\n \tBRANCH_TRACK_INHERIT,\n+\tBRANCH_TRACK_SIMPLE,\n };\n \n extern enum branch_track git_branch_track;\ndiff --git a/config.c b/config.c\nindex e0c03d154c9..cc586ac816c 100644\n--- a/config.c\n+++ b/config.c\n@@ -1673,6 +1673,9 @@ static int git_default_branch_config(const char *var, const char *value)\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n \t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n+\t\t} else if (value && !strcmp(value, \"simple\")) {\n+\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\treturn 0;\n \t\t}\n \t\tgit_branch_track = git_config_bool(var, value);\n \t\treturn 0;\n-- \ngitgitgadget\n\n"},{"id":"449732","messageId":"220228.86o82r5nzm.gmgdl@evledraar.gmail.com","threadId":"57470","inReplyTo":"d5b18c7949fdea966d31b2b8ca3f8aa8ed3a86b6.1646032466.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 2/2] t3200: tests for new branch.autosetupmerge option \"simple\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-28T09:34:32Z","receivedAt":"2022-02-28T09:39:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Feb 28 2022, Tao Klerks via GitGitGadget wrote:\n\n> From: Tao Klerks <tao@klerks.biz>\n>\n> In the previous commit a new autosetupmerge option was introduced.\n>\n> Extend the existing branch tests with three new cases testing this\n> option - the obvious matching-name and non-matching-name cases, and\n> also a non-matching-ref-type case.\n>\n> The matching-name case needs to temporarily create an independent\n> repo to fetch from, as the general strategy of using the local repo as\n> the remote in these tests precludes locally branching with the same\n> name as in the \"remote\".\n>\n> Signed-off-by: Tao Klerks <tao@klerks.biz>\n> ---\n>  t/t3200-branch.sh | 35 +++++++++++++++++++++++++++++++++++\n>  1 file changed, 35 insertions(+)\n>\n> diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\n> index 7a0ff75ba86..15cc58f1e64 100755\n> --- a/t/t3200-branch.sh\n> +++ b/t/t3200-branch.sh\n> @@ -886,6 +886,41 @@ test_expect_success 'branch from tag w/--track causes failure' '\n>  \ttest_must_fail git branch --track my11 foobar\n>  '\n>  \n> +test_expect_success 'simple tracking works when remote branch name matches' '\n> +\ttest_create_repo otherserver &&\n> +\ttest_commit -C otherserver my_commit 1 &&\n> +\tgit -C otherserver branch feature &&\n> +\tgit config branch.autosetupmerge simple &&\n> +\tgit config remote.otherserver.url otherserver &&\n> +\tgit config remote.otherserver.fetch refs/heads/*:refs/remotes/otherserver/* &&\n\nShouldn't these use test_config, or if the tests below need them do that\nvia a helper, so later added tests don't need to reset this state?\n\n> +\tgit fetch otherserver &&\n> +\tgit branch feature otherserver/feature &&\n> +\trm -fr otherserver &&\n\nInstead of \"rm -rf\" after, do above:\n\n    test_when_finished \"rm -rf otherserver\" &&\n    git init otherserver\n\n(you don't need \"test_create_repo\" either, just use \"git init\")\n\n> +\ttest $(git config branch.feature.remote) = otherserver &&\n> +\ttest $(git config branch.feature.merge) = refs/heads/feature\n\nUse:\n\n    echo otherserver >expect &&\n    git config ... >actual &&\n    test_cmp expect actual\n\netc., the pattern you're using here will hide git's exit code on\nsegfaults, abort() etc., and also makes for less useful debug info on\nfailure than test_cmp.\n\n    \n> +'\n> +\n> +test_expect_success 'simple tracking skips when remote branch name does not match' '\n> +\tgit config branch.autosetupmerge simple &&\n> +\tgit config remote.local.url . &&\n> +\tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n\nditto config setup above, this is quite hard to follow in sequence since\nyo uneed to reason about all existing config. Let's start with a clean\nslate for each test_expect_success and setup the specific config we want\ninstead.fallow since\n\n> +\t(git show-ref -q refs/remotes/local/main || git fetch local) &&\n\nThis likewise hides segfaults etc. Use:\n\n    test_might_fail git show-ref ...\n\nBut maybe this whole thing should use \"git rev-parse --verify\" or\nsomething?\n\n> +\tgit branch my-other local/main &&\n> +\ttest -z \"$(git config branch.my-other.remote)\" &&\n> +\ttest -z \"$(git config branch.my-other.merge)\"\n\nditto test_cmp comments, but here:\n\n    git ... >out &&\n    test_must_be_empty out\n\n> +'\n> +\n> +test_expect_success 'simple tracking skips when remote ref is not a branch' '\n> +\tgit config branch.autosetupmerge simple &&\n> +\tgit tag mytag12 main &&\n> +\tgit config remote.localtags.url . &&\n> +\tgit config remote.localtags.fetch refs/tags/*:refs/remotes/localtags/* &&\n> +\t(git show-ref -q refs/remotes/localtags/mytag12 || git fetch localtags) &&\n> +\tgit branch mytag12 localtags/mytag12 &&\n> +\ttest -z \"$(git config branch.mytag12.remote)\" &&\n> +\ttest -z \"$(git config branch.mytag12.merge)\"\n\nditto above.\n\n> +'\n> +\n>  test_expect_success '--set-upstream-to fails on multiple branches' '\n>  \techo \"fatal: too many arguments to set new upstream\" >expect &&\n>  \ttest_must_fail git branch --set-upstream-to main a b c 2>err &&\n\n"},{"id":"449741","messageId":"220228.86k0df5key.gmgdl@evledraar.gmail.com","threadId":"57470","inReplyTo":"0b5d47895120539d6a72a91398f33a0e33df7cd5.1646032466.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 1/2] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-28T10:39:38Z","receivedAt":"2022-02-28T10:58:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Feb 28 2022, Tao Klerks via GitGitGadget wrote:\n\nI think squashing 2/2 inot this would make this much easier to follow,\ni.e. to have tests along with the new feature.\n\n> +\t/*\n> +\t * This check does not apply to the BRANCH_TRACK_INHERIT\n> +\t * option; you can inherit one or more tracking entries\n> +\t * and the tracking.matches counter is not incremented.\n> +\t */\n>  \tif (tracking.matches > 1)\n>  \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n>  \t\t    orig_ref);\n\nThis function is the only user of find_tracked_branch(). For e.g. \"git\ncheckout we emit\";\n\n    fatal: builtin/checkout.c:1246: 'foo' matched multiple (4) remote tracking branches\n\nPerhaps we can do something similar here, and even with some advise()\nemit information about what other branches conflicted.\n\n> +\tif (track == BRANCH_TRACK_SIMPLE) {\n> +\t\t/*\n> +\t\t * Only track if remote branch name matches.\n> +\t\t * Reaching into items[0].string is safe because\n> +\t\t * we know there is at least one and not more than\n> +\t\t * one entry (because not BRANCH_TRACK_INHERIT).\n> +\t\t */\n> +\t\tconst char *tracked_branch;\n> +\t\tif (!skip_prefix(tracking.srcs->items[0].string,\n> +\t\t\t\t \"refs/heads/\", &tracked_branch) ||\n> +\t\t    strcmp(tracked_branch, new_ref))\n> +\t\t\treturn;\n> +\t}\n> +\n\nI wondered when reading this if there isn't a way to merge this and the\n\"branch_get\" call made in \"inherit_tracking\" earlier in this function in\nthe \"track != BRANCH_TRACK_INHERIT\" case.\n\nBut maybe not, and that whole API entry point is a bit messy in needing\nto cover both the use-case of an existing branch & nonexisting\n(i.e. initial creation).\n"},{"id":"449848","messageId":"CAPig+cQQ30XZ1zAguZNgEMTFK3P029Ds-miXQq=A-_pd4HGiGQ@mail.gmail.com","threadId":"57470","inReplyTo":"220228.86o82r5nzm.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v3 2/2] t3200: tests for new branch.autosetupmerge option \"simple\"","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2022-03-01T02:58:58Z","receivedAt":"2022-03-01T02:59:13Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Mon, Feb 28, 2022 at 5:54 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n> On Mon, Feb 28 2022, Tao Klerks via GitGitGadget wrote:\n> > +     test $(git config branch.feature.remote) = otherserver &&\n> > +     test $(git config branch.feature.merge) = refs/heads/feature\n>\n> Use:\n>\n>     echo otherserver >expect &&\n>     git config ... >actual &&\n>     test_cmp expect actual\n>\n> etc., the pattern you're using here will hide git's exit code on\n> segfaults, abort() etc., and also makes for less useful debug info on\n> failure than test_cmp.\n\nBetter yet, use test_cmp_config():\n\n    test_cmp_config otherserver branch.feature.remote &&\n"},{"id":"449903","messageId":"CAPMMpogv995t102biwYVCcvTTdF7beXNMorn3KyWceuRcbAZPA@mail.gmail.com","threadId":"57470","inReplyTo":"220228.86o82r5nzm.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v3 2/2] t3200: tests for new branch.autosetupmerge option \"simple\"","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-03-01T09:59:19Z","receivedAt":"2022-03-01T09:59:36Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Mon, Feb 28, 2022 at 10:39 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n>\n> On Mon, Feb 28 2022, Tao Klerks via GitGitGadget wrote:\n>\n> > +test_expect_success 'simple tracking works when remote branch name matches' '\n> > +     test_create_repo otherserver &&\n> > +     test_commit -C otherserver my_commit 1 &&\n> > +     git -C otherserver branch feature &&\n> > +     git config branch.autosetupmerge simple &&\n> > +     git config remote.otherserver.url otherserver &&\n> > +     git config remote.otherserver.fetch refs/heads/*:refs/remotes/otherserver/* &&\n>\n> Shouldn't these use test_config, or if the tests below need them do that\n> via a helper, so later added tests don't need to reset this state?\n\nYes, I will look at this; I was naively (and clearly incorrectly)\nfollowing a pattern I saw in this same test file.\n\n>\n> > +     git fetch otherserver &&\n> > +     git branch feature otherserver/feature &&\n> > +     rm -fr otherserver &&\n>\n> Instead of \"rm -rf\" after, do above:\n>\n>     test_when_finished \"rm -rf otherserver\" &&\n>     git init otherserver\n>\n> (you don't need \"test_create_repo\" either, just use \"git init\")\n\nWill do, thx!\n\n>\n> > +     test $(git config branch.feature.remote) = otherserver &&\n> > +     test $(git config branch.feature.merge) = refs/heads/feature\n>\n> Use:\n>\n>     echo otherserver >expect &&\n>     git config ... >actual &&\n>     test_cmp expect actual\n>\n> etc., the pattern you're using here will hide git's exit code on\n> segfaults, abort() etc., and also makes for less useful debug info on\n> failure than test_cmp.\n\nAgain, thank you! (I will look at test_cmp_config() as Eric suggested)\n\n>\n>\n> > +'\n> > +\n> > +test_expect_success 'simple tracking skips when remote branch name does not match' '\n> > +     git config branch.autosetupmerge simple &&\n> > +     git config remote.local.url . &&\n> > +     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n>\n> ditto config setup above, this is quite hard to follow in sequence since\n> yo uneed to reason about all existing config. Let's start with a clean\n> slate for each test_expect_success and setup the specific config we want\n> instead.fallow since\n>\n> > +     (git show-ref -q refs/remotes/local/main || git fetch local) &&\n>\n> This likewise hides segfaults etc. Use:\n>\n>     test_might_fail git show-ref ...\n>\n> But maybe this whole thing should use \"git rev-parse --verify\" or\n> something?\n\nHonestly, I think this bad pattern is just a premature optimization against\na pretty-fast local fetch. Will simplify, and do the same for existing\npatterns in this file.\n\n>\n> > +     git branch my-other local/main &&\n> > +     test -z \"$(git config branch.my-other.remote)\" &&\n> > +     test -z \"$(git config branch.my-other.merge)\"\n>\n> ditto test_cmp comments, but here:\n>\n>     git ... >out &&\n>     test_must_be_empty out\n>\n\nOK, will look, thx.\n"},{"id":"449904","messageId":"CAPMMpoj6JM84v5kD8sFWG+157r9DHGckt9_uNrY6LMz3rQKA2w@mail.gmail.com","threadId":"57470","inReplyTo":"CAPig+cQQ30XZ1zAguZNgEMTFK3P029Ds-miXQq=A-_pd4HGiGQ@mail.gmail.com","subject":"Re: [PATCH v3 2/2] t3200: tests for new branch.autosetupmerge option \"simple\"","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-03-01T09:59:53Z","receivedAt":"2022-03-01T10:00:12Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Tue, Mar 1, 2022 at 3:59 AM Eric Sunshine <sunshine@sunshineco.com> wrote:\n>\n> On Mon, Feb 28, 2022 at 5:54 AM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n> > On Mon, Feb 28 2022, Tao Klerks via GitGitGadget wrote:\n> > > +     test $(git config branch.feature.remote) = otherserver &&\n> > > +     test $(git config branch.feature.merge) = refs/heads/feature\n> >\n> > Use:\n> >\n> >     echo otherserver >expect &&\n> >     git config ... >actual &&\n> >     test_cmp expect actual\n> >\n> > etc., the pattern you're using here will hide git's exit code on\n> > segfaults, abort() etc., and also makes for less useful debug info on\n> > failure than test_cmp.\n>\n> Better yet, use test_cmp_config():\n>\n>     test_cmp_config otherserver branch.feature.remote &&\n\nNoted, thx.\n"},{"id":"450073","messageId":"CAPMMpoi9gQscSQ5Xn1xTb6WaCXu+qR67DJh9nCbqN0jp7-b_5A@mail.gmail.com","threadId":"57470","inReplyTo":"220228.86k0df5key.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v3 1/2] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-03-02T09:35:47Z","receivedAt":"2022-03-02T09:36:02Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Mon, Feb 28, 2022 at 11:56 AM Ævar Arnfjörð Bjarmason\n<avarab@gmail.com> wrote:\n>\n> On Mon, Feb 28 2022, Tao Klerks via GitGitGadget wrote:\n>\n> I think squashing 2/2 inot this would make this much easier to follow,\n> i.e. to have tests along with the new feature.\n>\n\nOK! Doing.\n\n> > +     /*\n> > +      * This check does not apply to the BRANCH_TRACK_INHERIT\n> > +      * option; you can inherit one or more tracking entries\n> > +      * and the tracking.matches counter is not incremented.\n> > +      */\n> >       if (tracking.matches > 1)\n> >               die(_(\"not tracking: ambiguous information for ref %s\"),\n> >                   orig_ref);\n>\n> This function is the only user of find_tracked_branch(). For e.g. \"git\n> checkout we emit\";\n>\n>     fatal: builtin/checkout.c:1246: 'foo' matched multiple (4) remote tracking branches\n>\n> Perhaps we can do something similar here\n\nI'm not sure what you're pointing to specifically - the fact that the\ncheckout message provides a count? If so I guess I understand/agree,\nfind_tracked_branch() could be enhanced to keep counting rather than\nexiting at the first sign of trouble, to support such a\nslightly-more-explicit message here.\n\nI'm not convinced that this situation is common enough to warrant\nchange: mapping multiple remotes to the same remote-tracking path\nseems like a strange setup - is this something we recommend or\ndocument anywhere? maybe to have 2 \"remotes\" that correspond to the\nsame server over different protocols appear as one set of tracking\nbranches?\n\nOn the other hand I am of course happy to make things better if we\nthink this will do that!\n\n> even with some advise()\n> emit information about what other branches conflicted.\n\nI believe the conflict is not about different \"branches\" exactly, but\nabout *refspecs* that map to the tracking branch.\n\nIf I understand correctly this change would entail creating a new\nadvice type (and documenting it), and figuring out what the advice\nshould look like - something like \"find and disambiguate your fetch\nrefspecs to enable auto tracking setup! If you want to keep your\nambiguous refspecs, set auto tracking setup to false!\" - but nicer :)\n\n>\n> > +     if (track == BRANCH_TRACK_SIMPLE) {\n> > +             /*\n> > +              * Only track if remote branch name matches.\n> > +              * Reaching into items[0].string is safe because\n> > +              * we know there is at least one and not more than\n> > +              * one entry (because not BRANCH_TRACK_INHERIT).\n> > +              */\n> > +             const char *tracked_branch;\n> > +             if (!skip_prefix(tracking.srcs->items[0].string,\n> > +                              \"refs/heads/\", &tracked_branch) ||\n> > +                 strcmp(tracked_branch, new_ref))\n> > +                     return;\n> > +     }\n> > +\n>\n> I wondered when reading this if there isn't a way to merge this and the\n> \"branch_get\" call made in \"inherit_tracking\" earlier in this function in\n> the \"track != BRANCH_TRACK_INHERIT\" case.\n>\n> But maybe not, and that whole API entry point is a bit messy in needing\n> to cover both the use-case of an existing branch & nonexisting\n> (i.e. initial creation).\n\nHmm, I had a hard time understanding this comment. I *think* you were\nsaying \"why don't you use an existing API to get the full ref name of the\nnew local branch, and compare that to the full name of the remote\nbranch you already have, rather than messing with a \"refs/heads/\"\nprefix explicitly/redundantly\"... Is that right?\n"},{"id":"451685","messageId":"CAPMMpohKRq0N8MGcWmUfMxVLTXrMD-+ADBDp_W6xwOXjUxdkhA@mail.gmail.com","threadId":"57470","inReplyTo":"CAPMMpoi9gQscSQ5Xn1xTb6WaCXu+qR67DJh9nCbqN0jp7-b_5A@mail.gmail.com","subject":"Re: [PATCH v3 1/2] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-03-20T17:00:57Z","receivedAt":"2022-03-20T17:01:19Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Wed, Mar 2, 2022 at 10:35 AM Tao Klerks <tao@klerks.biz> wrote:\n>\n> On Mon, Feb 28, 2022 at 11:56 AM Ævar Arnfjörð Bjarmason\n> <avarab@gmail.com> wrote:\n> >\n> >\n> > This function is the only user of find_tracked_branch(). For e.g. \"git\n> > checkout we emit\";\n> >\n> >     fatal: builtin/checkout.c:1246: 'foo' matched multiple (4) remote tracking branches\n> >\n> > Perhaps we can do something similar here\n>\n> I'm not sure what you're pointing to specifically - the fact that the\n> checkout message provides a count? If so I guess I understand/agree,\n> find_tracked_branch() could be enhanced to keep counting rather than\n> exiting at the first sign of trouble, to support such a\n> slightly-more-explicit message here.\n>\n> I'm not convinced that this situation is common enough to warrant\n> change: mapping multiple remotes to the same remote-tracking path\n> seems like a strange setup - is this something we recommend or\n> document anywhere? maybe to have 2 \"remotes\" that correspond to the\n> same server over different protocols appear as one set of tracking\n> branches?\n>\n> On the other hand I am of course happy to make things better if we\n> think this will do that!\n\nHaving finally understood the logic in play here, I now see that\nfind_tracked_branch() does not \"exit at the first sign of trouble\" as\nI thought, so there isn't much change required to produce a marginally\nricher error message here, but I've decided to work on this proposed\nenhancement in a separate patch. The more I look at this, the less\nconfident I am about exactly the right thing to do - and I'd rather\nnot hold up the (in my opinion) net-good branch.autosetupmerge=simple\nwork.\n\nThe specific concern I have is about changing the \"fatal: Not\ntracking: ambiguous information for ref refs/remotes/origin/master\"\nmessage. Having understood when it can occur, I've realized it is\nprobably quite common - I at least have certainly seen it a few times,\nas the situation it describes is what happens if you copy/paste a\n\"remote\" section in your git config file, to create a new remote with\nthe same setup as an existing one, without remembering to adjust the\nrefspec for the new remote name.\n\n> > even with some advise()\n> > emit information about what other branches conflicted.\n>\n> I believe the conflict is not about different \"branches\" exactly, but\n> about *refspecs* that map to the tracking branch.\n>\n> If I understand correctly this change would entail creating a new\n> advice type (and documenting it), and figuring out what the advice\n> should look like - something like \"find and disambiguate your fetch\n> refspecs to enable auto tracking setup! If you want to keep your\n> ambiguous refspecs, set auto tracking setup to false!\" - but nicer :)\n\nIn addition to the mechanics of creating a new advice type, I\neventually realized that the right message would list the *remotes*\nthat have refspecs mapping to the same tracking ref - which would mean\nnewly tracking those in the per-remote find_tracked_branch() looping.\n\nI initially thought this situation was too rare to warrant this kind\nof change, but now, understanding how I myself have reached this\nsituation a few times *and it took me a while to understand what I did\nwrong* (at least the first time), I think it's worthwhile work in and\nof itself.\n\nExpect a new separate patchset sometime.\n"},{"id":"451697","messageId":"pull.1161.v4.git.1647843442911.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v3.git.1646032466.gitgitgadget@gmail.com","subject":"[PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-03-21T06:17:22Z","receivedAt":"2022-03-21T06:17:30Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nWith the default push.default option, \"simple\", beginners are\nprotected from accidentally pushing to the \"wrong\" branch in\ncentralized workflows: if the remote tracking branch they would push\nto does not have the same name as the local branch, and they try to do\na \"default push\", they get an error and explanation with options.\n\nThere is a particular centralized workflow where this often happens:\na user branches to a new local feature branch from an existing\nupstream branch, eg with \"checkout -b feature1 origin/master\". With\nthe default branch.autosetupmerge configuration (value \"true\"), git\nwill automatically add origin/master as the remote tracking branch.\n\nWhen the user pushes with \"git push\", they get an error, and (amongst\nother things) a suggestion to run \"git push origin HEAD\". Eventually\nthey figure out to add \"-u\" to change the tracking branch, or they set\npush.default to \"current\", or some tooling does one or the other of\nthese things for them.\n\nWhen one of their coworkers works on the same branch, they don't get\nany of that weirdness. They just \"git checkout feature1\" and\neverything works exactly as they expect, with the shared remote branch\nset up as remote tracking branch, and push and pull working out of the\nbox.\n\nThe \"stable state\" for this way of working is that local branches have\nthe same-name remote tracking branch (origin/feature1 in this\nexample), and multiple people can work on that remote feature branch\nat the same time, trusting \"git pull\" to merge or rebase as required\nfor them to be able to push their interim changes to that same feature\nbranch on that same remote.\n\n(merging from the upstream \"master\" branch, and merging back to it,\nare separate more involved processes in this flow).\n\nThere is a problem in this flow/way of working, however, which is that\nthe first user, when they first branched from origin/master, ended up\nwith the \"wrong\" remote tracking branch (different from the stable\nstate). For a while, before they pushed (and maybe longer, if they\ndon't use -u/--set-upstream), their \"git pull\" wasn't getting other\nusers' changes to the feature branch - it was getting any changes from\nthe remote \"master\" branch instead (a completely different class of\nchanges!)\n\nAny experienced git user will presumably say \"well yeah, that's what\nit means to have the remote tracking branch set to origin/master!\" -\nbut that user didn't *ask* to have the remote master branch added as\nremote tracking branch - that just happened automatically when they\nbranched their feature branch. They didn't necessarily even notice or\nunderstand the meaning of the \"set up to track 'origin/master'\"\nmessage when they created the branch - especially if they are using a\nGUI.\n\nLooking at how to fix this, you might think \"OK, so disable auto setup\nof remote tracking - set branch.autosetupmerge to false\" - but that\nwill inconvenience the *second* user in this story - the one who just\nwanted to start working on the feature branch. The first and second\nusers swap roles at different points in time of course - they should\nboth have a sane configuration that does the right thing in both\nsituations.\n\nMake these flows painless by introducing a new branch.autosetupmerge\noption called \"simple\", to match the same-name \"push.default\" option\nthat makes similar assumptions.\n\nThis new option automatically sets up tracking in a *subset* of the\ncurrent default situations: when the original ref is a remote tracking\nbranch *and* has the same branch name on the remote (as the new local\nbranch name).\n\nWith this new configuration, in the example situation above, the first\nuser does *not* get origin/master set up as the tracking branch for\nthe new local branch. If they \"git pull\" in their new local-only\nbranch, they get an error explaining there is no upstream branch -\nwhich makes sense and is helpful. If they \"git push\", they get an\nerror explaining how to push *and* suggesting they specify\n--set-upstream - which is exactly the right thing to do for them.\n\nThis new option is likely not appropriate for users intentionally\nimplementing a \"triangular workflow\" with a shared upstream tracking\nbranch, that they \"git pull\" in and a \"private\" feature branch that\nthey push/force-push to just for remote safe-keeping until they are\nready to push up to the shared branch explicitly/separately. Such\nusers are likely to prefer keeping the current default\nmerge.autosetupmerge=true behavior, and change their push.default to\n\"current\".\n\nAlso extend the existing branch tests with three new cases testing\nthis option - the obvious matching-name and non-matching-name cases,\nand also a non-matching-ref-type case. The matching-name case needs to\ntemporarily create an independent repo to fetch from, as the general\nstrategy of using the local repo as the remote in these tests\nprecludes locally branching with the same name as in the \"remote\".\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n    adding new branch.autosetupmerge option \"simple\"\n    \n    This patchset introduces a new option to the branch.autosetupmerge\n    setting, \"simple\", which is intended to be consistent with and\n    complementary to the push.default \"simple\" option.\n    \n    The push.defaut option \"simple\" helps produce predictable/understandable\n    behavior for beginners, where they don't accidentally push to the\n    \"wrong\" branch in centralized workflows. If they create a local branch\n    with a different name and then try to do a plain push, it will helpfully\n    fail and explain why.\n    \n    However, such users can often find themselves confused by the behavior\n    of git after they first branch, and before they push. At that stage,\n    their upstream tracking branch is the original remote branch, and pull\n    will be bringing in \"upstream changes\" - eg all changes to \"main\", in a\n    typical project where that's where they branched from. On the other\n    hand, once they push their new branch (dealing with the initial error,\n    following instructions to push to the right name), subsequent \"pull\"\n    calls will behave as expected, only bring in any changes to that new\n    branch they pushed.\n    \n    The new option introduced here, with push.default set to simple, ensures\n    that push/pull behavior is generally consistent - tracking will be\n    automatically set up for branches that push will work for (and pull will\n    be consistent for) only.\n    \n    Changes since v3:\n    \n     * squashed new-tests commit into main changes, as per Ævar's advice\n     * added some hopefully-helpful comments in some prior existing code\n     * improved tests to use better idioms following Ævar and Eric's advice\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1161%2FTaoK%2Ffeature-branch-autosetupmerge-simple-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1161/TaoK/feature-branch-autosetupmerge-simple-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/1161\n\nRange-diff vs v3:\n\n 1:  0b5d4789512 ! 1:  eca8ab2eb7b merge: new autosetupmerge option 'simple' for matching branches\n     @@ Commit message\n          merge.autosetupmerge=true behavior, and change their push.default to\n          \"current\".\n      \n     +    Also extend the existing branch tests with three new cases testing\n     +    this option - the obvious matching-name and non-matching-name cases,\n     +    and also a non-matching-ref-type case. The matching-name case needs to\n     +    temporarily create an independent repo to fetch from, as the general\n     +    strategy of using the local repo as the remote in these tests\n     +    precludes locally branching with the same name as in the \"remote\".\n     +\n          Signed-off-by: Tao Klerks <tao@klerks.biz>\n      \n       ## Documentation/config/branch.txt ##\n     @@ Documentation/git-branch.txt: The exact upstream branch is chosen depending on t\n       how the `branch.<name>.remote` and `branch.<name>.merge` options are used.\n      \n       ## branch.c ##\n     +@@ branch.c: static int find_tracked_branch(struct remote *remote, void *priv)\n     + \t\t\tfree(tracking->spec.src);\n     + \t\t\tstring_list_clear(tracking->srcs, 0);\n     + \t\t}\n     ++\t\t/* remote_find_tracking() searches by src if present */\n     + \t\ttracking->spec.src = NULL;\n     + \t}\n     +-\n     + \treturn 0;\n     + }\n     + \n      @@ branch.c: static void setup_tracking(const char *new_ref, const char *orig_ref,\n     + \n     + \tif (!tracking.matches)\n     + \t\tswitch (track) {\n     ++\t\t/* If ref is not remote, still use local */\n     + \t\tcase BRANCH_TRACK_ALWAYS:\n     + \t\tcase BRANCH_TRACK_EXPLICIT:\n     + \t\tcase BRANCH_TRACK_OVERRIDE:\n     ++\t\t/* Remote matches not evaluated */\n     + \t\tcase BRANCH_TRACK_INHERIT:\n     + \t\t\tbreak;\n     ++\t\t/* Otherwise, if no remote don't track */\n     + \t\tdefault:\n       \t\t\tgoto cleanup;\n       \t\t}\n       \n      +\t/*\n     -+\t * This check does not apply to the BRANCH_TRACK_INHERIT\n     -+\t * option; you can inherit one or more tracking entries\n     -+\t * and the tracking.matches counter is not incremented.\n     ++\t * This check does not apply to BRANCH_TRACK_INHERIT;\n     ++\t * that supports multiple entries in tracking_srcs but\n     ++\t * leaves tracking.matches at 0.\n      +\t */\n       \tif (tracking.matches > 1)\n       \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n     @@ branch.c: static void setup_tracking(const char *new_ref, const char *orig_ref,\n      +\t\t * Only track if remote branch name matches.\n      +\t\t * Reaching into items[0].string is safe because\n      +\t\t * we know there is at least one and not more than\n     -+\t\t * one entry (because not BRANCH_TRACK_INHERIT).\n     ++\t\t * one entry (because only BRANCH_TRACK_INHERIT can\n     ++\t\t * produce more than one entry).\n      +\t\t */\n      +\t\tconst char *tracked_branch;\n      +\t\tif (!skip_prefix(tracking.srcs->items[0].string,\n     @@ config.c: static int git_default_branch_config(const char *var, const char *valu\n       \t\t}\n       \t\tgit_branch_track = git_config_bool(var, value);\n       \t\treturn 0;\n     +\n     + ## t/t3200-branch.sh ##\n     +@@ t/t3200-branch.sh: test_expect_success 'branch from tag w/--track causes failure' '\n     + \ttest_must_fail git branch --track my11 foobar\n     + '\n     + \n     ++test_expect_success 'simple tracking works when remote branch name matches' '\n     ++\ttest_when_finished \"rm -rf otherserver\" &&\n     ++\tgit init otherserver &&\n     ++\ttest_commit -C otherserver my_commit 1 &&\n     ++\tgit -C otherserver branch feature &&\n     ++\ttest_config branch.autosetupmerge simple &&\n     ++\ttest_config remote.otherserver.url otherserver &&\n     ++\ttest_config remote.otherserver.fetch refs/heads/*:refs/remotes/otherserver/* &&\n     ++\tgit fetch otherserver &&\n     ++\tgit branch feature otherserver/feature &&\n     ++\ttest_cmp_config otherserver branch.feature.remote &&\n     ++\ttest_cmp_config refs/heads/feature branch.feature.merge\n     ++'\n     ++\n     ++test_expect_success 'simple tracking skips when remote branch name does not match' '\n     ++\ttest_config branch.autosetupmerge simple &&\n     ++\ttest_config remote.local.url . &&\n     ++\ttest_config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n     ++\tgit fetch local &&\n     ++\tgit branch my-other local/main &&\n     ++\ttest_cmp_config \"\" --default \"\" branch.my-other.remote &&\n     ++\ttest_cmp_config \"\" --default \"\" branch.my-other.merge\n     ++'\n     ++\n     ++test_expect_success 'simple tracking skips when remote ref is not a branch' '\n     ++\ttest_config branch.autosetupmerge simple &&\n     ++\ttest_config remote.localtags.url . &&\n     ++\ttest_config remote.localtags.fetch refs/tags/*:refs/remotes/localtags/* &&\n     ++\tgit tag mytag12 main &&\n     ++\tgit fetch localtags &&\n     ++\tgit branch mytag12 localtags/mytag12 &&\n     ++\ttest_cmp_config \"\" --default \"\" branch.mytag12.remote &&\n     ++\ttest_cmp_config \"\" --default \"\" branch.mytag12.merge\n     ++'\n     ++\n     + test_expect_success '--set-upstream-to fails on multiple branches' '\n     + \techo \"fatal: too many arguments to set new upstream\" >expect &&\n     + \ttest_must_fail git branch --set-upstream-to main a b c 2>err &&\n 2:  d5b18c7949f < -:  ----------- t3200: tests for new branch.autosetupmerge option \"simple\"\n\n\n Documentation/config/branch.txt |  4 +++-\n Documentation/git-branch.txt    | 18 ++++++++++-------\n branch.c                        | 25 ++++++++++++++++++++++-\n branch.h                        |  1 +\n config.c                        |  3 +++\n t/t3200-branch.sh               | 35 +++++++++++++++++++++++++++++++++\n 6 files changed, 77 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 1e0c7af014b..8df10d07129 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -9,7 +9,9 @@ branch.autoSetupMerge::\n \tautomatic setup is done when the starting point is either a\n \tlocal branch or remote-tracking branch; `inherit` -- if the starting point\n \thas a tracking configuration, it is copied to the new\n-\tbranch. This option defaults to true.\n+\tbranch; `simple` -- automatic setup is done only when the starting point\n+\tis a remote-tracking branch and the new branch has the same name as the\n+\tremote branch. This option defaults to true.\n \n branch.autoSetupRebase::\n \tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex c8b4f9ce3c7..ae82378349d 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -221,13 +221,17 @@ The exact upstream branch is chosen depending on the optional argument:\n itself as the upstream; `--track=inherit` means to copy the upstream\n configuration of the start-point branch.\n +\n-`--track=direct` is the default when the start point is a remote-tracking branch.\n-Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n-were given. Set it to `always` if you want this behavior when the\n-start-point is either a local or remote-tracking branch. Set it to\n-`inherit` if you want to copy the tracking configuration from the\n-branch point.\n+The branch.autoSetupMerge configuration variable specifies how `git switch`,\n+`git checkout` and `git branch` should behave when neither `--track` nor\n+`--no-track` are specified:\n++\n+The default option, `true`, behaves as though `--track=direct`\n+were given whenever the start-point is a remote-tracking branch.\n+`false` behaves as if `--no-track` were given. `always` behaves as though\n+`--track=direct` were given. `inherit` behaves as though `--track=inherit`\n+were given. `simple` behaves as though `--track=direct` were given only when\n+the start-point is a remote-tracking branch and the new branch has the same\n+name as the remote branch.\n +\n See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on\n how the `branch.<name>.remote` and `branch.<name>.merge` options are used.\ndiff --git a/branch.c b/branch.c\nindex 6b31df539a5..86ea91e76f8 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -30,9 +30,9 @@ static int find_tracked_branch(struct remote *remote, void *priv)\n \t\t\tfree(tracking->spec.src);\n \t\t\tstring_list_clear(tracking->srcs, 0);\n \t\t}\n+\t\t/* remote_find_tracking() searches by src if present */\n \t\ttracking->spec.src = NULL;\n \t}\n-\n \treturn 0;\n }\n \n@@ -243,19 +243,42 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,\n \n \tif (!tracking.matches)\n \t\tswitch (track) {\n+\t\t/* If ref is not remote, still use local */\n \t\tcase BRANCH_TRACK_ALWAYS:\n \t\tcase BRANCH_TRACK_EXPLICIT:\n \t\tcase BRANCH_TRACK_OVERRIDE:\n+\t\t/* Remote matches not evaluated */\n \t\tcase BRANCH_TRACK_INHERIT:\n \t\t\tbreak;\n+\t\t/* Otherwise, if no remote don't track */\n \t\tdefault:\n \t\t\tgoto cleanup;\n \t\t}\n \n+\t/*\n+\t * This check does not apply to BRANCH_TRACK_INHERIT;\n+\t * that supports multiple entries in tracking_srcs but\n+\t * leaves tracking.matches at 0.\n+\t */\n \tif (tracking.matches > 1)\n \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n \t\t    orig_ref);\n \n+\tif (track == BRANCH_TRACK_SIMPLE) {\n+\t\t/*\n+\t\t * Only track if remote branch name matches.\n+\t\t * Reaching into items[0].string is safe because\n+\t\t * we know there is at least one and not more than\n+\t\t * one entry (because only BRANCH_TRACK_INHERIT can\n+\t\t * produce more than one entry).\n+\t\t */\n+\t\tconst char *tracked_branch;\n+\t\tif (!skip_prefix(tracking.srcs->items[0].string,\n+\t\t\t\t \"refs/heads/\", &tracked_branch) ||\n+\t\t    strcmp(tracked_branch, new_ref))\n+\t\t\treturn;\n+\t}\n+\n \tif (tracking.srcs->nr < 1)\n \t\tstring_list_append(tracking.srcs, orig_ref);\n \tif (install_branch_config_multiple_remotes(config_flags, new_ref,\ndiff --git a/branch.h b/branch.h\nindex 04df2aa5b51..560b6b96a8f 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -12,6 +12,7 @@ enum branch_track {\n \tBRANCH_TRACK_EXPLICIT,\n \tBRANCH_TRACK_OVERRIDE,\n \tBRANCH_TRACK_INHERIT,\n+\tBRANCH_TRACK_SIMPLE,\n };\n \n extern enum branch_track git_branch_track;\ndiff --git a/config.c b/config.c\nindex e78397725c9..8de87400085 100644\n--- a/config.c\n+++ b/config.c\n@@ -1686,6 +1686,9 @@ static int git_default_branch_config(const char *var, const char *value)\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n \t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n+\t\t} else if (value && !strcmp(value, \"simple\")) {\n+\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\treturn 0;\n \t\t}\n \t\tgit_branch_track = git_config_bool(var, value);\n \t\treturn 0;\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 7a0ff75ba86..7a5a44a1ebf 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -886,6 +886,41 @@ test_expect_success 'branch from tag w/--track causes failure' '\n \ttest_must_fail git branch --track my11 foobar\n '\n \n+test_expect_success 'simple tracking works when remote branch name matches' '\n+\ttest_when_finished \"rm -rf otherserver\" &&\n+\tgit init otherserver &&\n+\ttest_commit -C otherserver my_commit 1 &&\n+\tgit -C otherserver branch feature &&\n+\ttest_config branch.autosetupmerge simple &&\n+\ttest_config remote.otherserver.url otherserver &&\n+\ttest_config remote.otherserver.fetch refs/heads/*:refs/remotes/otherserver/* &&\n+\tgit fetch otherserver &&\n+\tgit branch feature otherserver/feature &&\n+\ttest_cmp_config otherserver branch.feature.remote &&\n+\ttest_cmp_config refs/heads/feature branch.feature.merge\n+'\n+\n+test_expect_success 'simple tracking skips when remote branch name does not match' '\n+\ttest_config branch.autosetupmerge simple &&\n+\ttest_config remote.local.url . &&\n+\ttest_config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+\tgit fetch local &&\n+\tgit branch my-other local/main &&\n+\ttest_cmp_config \"\" --default \"\" branch.my-other.remote &&\n+\ttest_cmp_config \"\" --default \"\" branch.my-other.merge\n+'\n+\n+test_expect_success 'simple tracking skips when remote ref is not a branch' '\n+\ttest_config branch.autosetupmerge simple &&\n+\ttest_config remote.localtags.url . &&\n+\ttest_config remote.localtags.fetch refs/tags/*:refs/remotes/localtags/* &&\n+\tgit tag mytag12 main &&\n+\tgit fetch localtags &&\n+\tgit branch mytag12 localtags/mytag12 &&\n+\ttest_cmp_config \"\" --default \"\" branch.mytag12.remote &&\n+\ttest_cmp_config \"\" --default \"\" branch.mytag12.merge\n+'\n+\n test_expect_success '--set-upstream-to fails on multiple branches' '\n \techo \"fatal: too many arguments to set new upstream\" >expect &&\n \ttest_must_fail git branch --set-upstream-to main a b c 2>err &&\n\nbase-commit: 74cc1aa55f30ed76424a0e7226ab519aa6265061\n-- \ngitgitgadget\n"},{"id":"453901","messageId":"Yl2qwO0SMPOhb5h9@google.com","threadId":"57470","inReplyTo":"pull.1161.v4.git.1647843442911.gitgitgadget@gmail.com","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2022-04-18T18:15:28Z","receivedAt":"2022-04-18T18:15:40Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2022.03.21 06:17, Tao Klerks via GitGitGadget wrote:\n> From: Tao Klerks <tao@klerks.biz>\n> \n> With the default push.default option, \"simple\", beginners are\n> protected from accidentally pushing to the \"wrong\" branch in\n> centralized workflows: if the remote tracking branch they would push\n> to does not have the same name as the local branch, and they try to do\n> a \"default push\", they get an error and explanation with options.\n> \n> There is a particular centralized workflow where this often happens:\n> a user branches to a new local feature branch from an existing\n> upstream branch, eg with \"checkout -b feature1 origin/master\". With\n> the default branch.autosetupmerge configuration (value \"true\"), git\n> will automatically add origin/master as the remote tracking branch.\n> \n> When the user pushes with \"git push\", they get an error, and (amongst\n> other things) a suggestion to run \"git push origin HEAD\". Eventually\n> they figure out to add \"-u\" to change the tracking branch, or they set\n> push.default to \"current\", or some tooling does one or the other of\n> these things for them.\n> \n> When one of their coworkers works on the same branch, they don't get\n> any of that weirdness. They just \"git checkout feature1\" and\n> everything works exactly as they expect, with the shared remote branch\n> set up as remote tracking branch, and push and pull working out of the\n> box.\n> \n> The \"stable state\" for this way of working is that local branches have\n> the same-name remote tracking branch (origin/feature1 in this\n> example), and multiple people can work on that remote feature branch\n> at the same time, trusting \"git pull\" to merge or rebase as required\n> for them to be able to push their interim changes to that same feature\n> branch on that same remote.\n> \n> (merging from the upstream \"master\" branch, and merging back to it,\n> are separate more involved processes in this flow).\n> \n> There is a problem in this flow/way of working, however, which is that\n> the first user, when they first branched from origin/master, ended up\n> with the \"wrong\" remote tracking branch (different from the stable\n> state). For a while, before they pushed (and maybe longer, if they\n> don't use -u/--set-upstream), their \"git pull\" wasn't getting other\n> users' changes to the feature branch - it was getting any changes from\n> the remote \"master\" branch instead (a completely different class of\n> changes!)\n> \n> Any experienced git user will presumably say \"well yeah, that's what\n> it means to have the remote tracking branch set to origin/master!\" -\n> but that user didn't *ask* to have the remote master branch added as\n> remote tracking branch - that just happened automatically when they\n> branched their feature branch. They didn't necessarily even notice or\n> understand the meaning of the \"set up to track 'origin/master'\"\n> message when they created the branch - especially if they are using a\n> GUI.\n> \n> Looking at how to fix this, you might think \"OK, so disable auto setup\n> of remote tracking - set branch.autosetupmerge to false\" - but that\n> will inconvenience the *second* user in this story - the one who just\n> wanted to start working on the feature branch. The first and second\n> users swap roles at different points in time of course - they should\n> both have a sane configuration that does the right thing in both\n> situations.\n> \n> Make these flows painless by introducing a new branch.autosetupmerge\n> option called \"simple\", to match the same-name \"push.default\" option\n> that makes similar assumptions.\n> \n> This new option automatically sets up tracking in a *subset* of the\n> current default situations: when the original ref is a remote tracking\n> branch *and* has the same branch name on the remote (as the new local\n> branch name).\n> \n> With this new configuration, in the example situation above, the first\n> user does *not* get origin/master set up as the tracking branch for\n> the new local branch. If they \"git pull\" in their new local-only\n> branch, they get an error explaining there is no upstream branch -\n> which makes sense and is helpful. If they \"git push\", they get an\n> error explaining how to push *and* suggesting they specify\n> --set-upstream - which is exactly the right thing to do for them.\n> \n> This new option is likely not appropriate for users intentionally\n> implementing a \"triangular workflow\" with a shared upstream tracking\n> branch, that they \"git pull\" in and a \"private\" feature branch that\n> they push/force-push to just for remote safe-keeping until they are\n> ready to push up to the shared branch explicitly/separately. Such\n> users are likely to prefer keeping the current default\n> merge.autosetupmerge=true behavior, and change their push.default to\n> \"current\".\n\nI think this is a good solution for relatively inexperienced users, and\nI don't see any issues with the implementation or tests. However, I\nwonder how users for whom this may be useful are going to discover this\noption? I don't expect that such users are going to be watching Git's\nrelease notes looking for new features such as this, or carefully\nreading documentation changes.\n\nIn the discussion on v3 of this series, you mentioned you were thinking\nabout adding an advice setting to point users here; is there a reason\nwhy that didn't make it into v4? It seems appropriate to me to add one,\nperhaps at the point where a user with \"autosetupmerge=true\" would run\ninto a failure when trying to push?\n"},{"id":"453949","messageId":"CAPMMpogY5vZU8gyRSYh+BM4goPPtJw0cCiM-31sy-s_uGRv8uA@mail.gmail.com","threadId":"57470","inReplyTo":"Yl2qwO0SMPOhb5h9@google.com","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-04-20T05:12:06Z","receivedAt":"2022-04-20T05:12:27Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Mon, Apr 18, 2022 at 8:15 PM Josh Steadmon <steadmon@google.com> wrote:\n>\n>\n> I think this is a good solution for relatively inexperienced users, and\n> I don't see any issues with the implementation or tests.\n\nYay, thanks for the feedback!\n\n> However, I\n> wonder how users for whom this may be useful are going to discover this\n> option? I don't expect that such users are going to be watching Git's\n> release notes looking for new features such as this, or carefully\n> reading documentation changes.\n\nHonestly, I was being a bit selfish here - I effectively control the\ngitconfig of \"my\" users, so I was planning on enabling this by default\nand letting it \"settle in\" in git at large, eventually proposing to\nchange the default.\n\nI understand/agree that this is a little naive - if no-one has reason\nto try the new behavior, very little information as to its\nusefulness/appropriateness is likely to emerge, and it will never be\nan obviously good idea to change the default.\n\n>\n> In the discussion on v3 of this series, you mentioned you were thinking\n> about adding an advice setting to point users here; is there a reason\n> why that didn't make it into v4?\n\nThe advice I mentioned I would work on wasn't actually about this new\nsetting/behavior, but rather about the previously existing (and\nreasonably unrelated) \"not tracking: ambiguous information for ref\"\nerror, which I found to be unreasonably cryptic.\n\nI submitted that advice change as\nhttps://lore.kernel.org/git/pull.1183.v7.git.1648793113943.gitgitgadget@gmail.com/,\nand it's gone out in a recent release.\n\n> It seems appropriate to me to add one,\n> perhaps at the point where a user with \"autosetupmerge=true\" would run\n> into a failure when trying to push?\n\nHaving thought about this a bit, I agree. On the one hand I'm a little\nnervous about adding this kind of public behavior change as I would\nimagine it's more likely to encounter resistance here, on the other\nhand I do think it will make the changes themselves much more useful.\nAlso, this patchset hasn't moved in a while, so \"holding it up\" with\nnew changes may not be a significant concern.\n\nthe current advice looks something like:\n---\nfatal: The upstream branch of your current branch does not match\nthe name of your current branch.  To push to the upstream branch\non the remote, use\n\n    git push origin HEAD:master\n\nTo push to the branch of the same name on the remote, use\n\n    git push origin HEAD\n\nTo choose either option permanently, see push.default in 'git help config'.\n---\n\nI would propose to add one sentence at the end along the lines of:\n---\nTo instead avoid automatically configuring upstream branches when\ntheir name doesn't match the local branch, see option 'simple' of\nbranch.autosetupmerge in 'git help config'.\n---\n\nDoes that make sense to you?\n"},{"id":"453985","messageId":"YmBAhjR7rwAuHylN@google.com","threadId":"57470","inReplyTo":"CAPMMpogY5vZU8gyRSYh+BM4goPPtJw0cCiM-31sy-s_uGRv8uA@mail.gmail.com","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Josh Steadmon","fromEmail":"steadmon@google.com","sentAt":"2022-04-20T17:19:02Z","receivedAt":"2022-04-20T17:19:12Z","isPatch":true,"sender":{"key":"steadmon@google.com","avatar":"https://avatars.githubusercontent.com/u/2654920?v=4"},"body":"On 2022.04.20 07:12, Tao Klerks wrote:\n> On Mon, Apr 18, 2022 at 8:15 PM Josh Steadmon <steadmon@google.com> wrote:\n> > It seems appropriate to me to add one,\n> > perhaps at the point where a user with \"autosetupmerge=true\" would run\n> > into a failure when trying to push?\n> \n> Having thought about this a bit, I agree. On the one hand I'm a little\n> nervous about adding this kind of public behavior change as I would\n> imagine it's more likely to encounter resistance here, on the other\n> hand I do think it will make the changes themselves much more useful.\n> Also, this patchset hasn't moved in a while, so \"holding it up\" with\n> new changes may not be a significant concern.\n> \n> the current advice looks something like:\n> ---\n> fatal: The upstream branch of your current branch does not match\n> the name of your current branch.  To push to the upstream branch\n> on the remote, use\n> \n>     git push origin HEAD:master\n> \n> To push to the branch of the same name on the remote, use\n> \n>     git push origin HEAD\n> \n> To choose either option permanently, see push.default in 'git help config'.\n> ---\n> \n> I would propose to add one sentence at the end along the lines of:\n> ---\n> To instead avoid automatically configuring upstream branches when\n> their name doesn't match the local branch, see option 'simple' of\n> branch.autosetupmerge in 'git help config'.\n> ---\n> \n> Does that make sense to you?\n\nSounds good to me.\n"},{"id":"453990","messageId":"xmqqczhbr6pv.fsf@gitster.g","threadId":"57470","inReplyTo":"CAPMMpogY5vZU8gyRSYh+BM4goPPtJw0cCiM-31sy-s_uGRv8uA@mail.gmail.com","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-20T17:43:24Z","receivedAt":"2022-04-20T17:43:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Tao Klerks <tao@klerks.biz> writes:\n\n>> However, I\n>> wonder how users for whom this may be useful are going to discover this\n>> option? I don't expect that such users are going to be watching Git's\n>> release notes looking for new features such as this, or carefully\n>> reading documentation changes.\n>\n> Honestly, I was being a bit selfish here - I effectively control the\n> gitconfig of \"my\" users, so I was planning on enabling this by default\n> and letting it \"settle in\" in git at large, eventually proposing to\n> change the default.\n\nI am afraid that it is double disservice to your users.  Once they\ngraduate your organization, they notice that their Git does not work\nas they expect and puzzled.\n\n> ...\n> To choose either option permanently, see push.default in 'git help config'.\n> ---\n>\n> I would propose to add one sentence at the end along the lines of:\n> ---\n> To instead avoid automatically configuring upstream branches when\n> their name doesn't match the local branch, see option 'simple' of\n> branch.autosetupmerge in 'git help config'.\n> ---\n>\n> Does that make sense to you?\n\nTwo questions.\n\n - If a user follows the push.default advice, does it have any\n   advantage to set branch.autosetupmerge=simple at all?\n\n - If a user follows the branch.autosetupmerge=simple advice, what\n   happens their \"git push\" on a branch that the .merge is not set\n   due to this configuration?  Shouldn't they have to set up the\n   push.default for these branches anyway?\n\nWhile it might be a good thing to mention branch.autosetupmerge\nconfiguration variable, I am not sure if \"To instead avoid\" is a\ngood thing to say here.  It sounds as if the user can ignore\npush.default as long as branch.autosetupmerge is taken care of, but\nI suspect that is not the case.  Setting the latter to 'simple'\nmeans there are *MORE* branches that do not have .remote/.merge set\nup, doesn't it?  Which in turn means that we are relying more on\nwhat push.default is set to, right?\n"},{"id":"454037","messageId":"CAPMMpohQei9vBBm=7hC=N5LPwzMCED=fZcXyePnrkLCHfCJTZw@mail.gmail.com","threadId":"57470","inReplyTo":"xmqqczhbr6pv.fsf@gitster.g","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-04-20T21:31:57Z","receivedAt":"2022-04-20T21:32:13Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Wed, Apr 20, 2022 at 7:43 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Tao Klerks <tao@klerks.biz> writes:\n>\n> > ...\n> > To choose either option permanently, see push.default in 'git help config'.\n> > ---\n> >\n> > I would propose to add one sentence at the end along the lines of:\n> > ---\n> > To instead avoid automatically configuring upstream branches when\n> > their name doesn't match the local branch, see option 'simple' of\n> > branch.autosetupmerge in 'git help config'.\n> > ---\n> >\n> > Does that make sense to you?\n>\n> Two questions.\n>\n>  - If a user follows the push.default advice, does it have any\n>    advantage to set branch.autosetupmerge=simple at all?\n\nProbably not?\n\nIt really depends what they set push.default to:\n* If they set it to upstream/tracking, then\nbranch.autosetupmerge=simple doesn't make much sense. You can set\nboth, but the outcome is effectively the same as setting push.default\nto simple - not very useful.\n* If they set it to \"current\", then it probably doesn't make sense\nbecause what they're angling for is probably a triangular workflow,\nwhich branch.autosetupmerge=simple very explicitly doesn't support /\ndoesn't make sense for. \"matching\" seems to be an extreme version of\nthe same setup.\n* If they set it to \"nothing\" I'm not sure - I haven't understood in\nwhat workflows that makes sense.\n\nGenerally, I expect that branch.autosetupmerge=simple makes the most\nsense with push.default left at the default of \"simple\", for...\n\"simple\" workflows :)\n\n>\n>  - If a user follows the branch.autosetupmerge=simple advice, what\n>    happens their \"git push\" on a branch that the .merge is not set\n>    due to this configuration?  Shouldn't they have to set up the\n>    push.default for these branches anyway?\n\nIf the user follows the branch.autosetupmerge=simple advice (and\nleaves push.default at the \"simple\" default), what they get at push\ntime will depend on whether they branched from a same-name remote\nbranch or anything else:\n\nIf they branched from a same-name remote branch, their \"git push\" will\nbe perfectly uneventful / unsurprising: they will simply push to the\nremote branch. This is the same as without\nbranch.autosetupmerge=simple.\n\nIf they branched from a different-name remote branch (they created an\nnew / independent local branch), then no remote tracking relationship\nwill have been set up, and instead of the \"fatal: The upstream branch\nof your current branch does not match\nthe name of your current branch\" error and advice, they will get a\nmuch simpler error and advice:\n\n---\nfatal: The current branch whatevs has no upstream branch.\nTo push the current branch and set the remote as upstream, use\n\n    git push --set-upstream origin whatevs\n---\n\nWhen they follow those instructions, they will be in the \"simple\"\nsetup same as if they had just branched from same-name.\n\nImportantly, as soon as they enable branch.autosetupmerge=simple, they\nnever see the original mismatching-name error and advice anymore -\nthey never again end up with mismatching names at all. (except in edge\ncases like branch renames)\n\n>\n> While it might be a good thing to mention branch.autosetupmerge\n> configuration variable, I am not sure if \"To instead avoid\" is a\n> good thing to say here.  It sounds as if the user can ignore\n> push.default as long as branch.autosetupmerge is taken care of, but\n> I suspect that is not the case.\n\nI disagree. If they get that error and advice, then their push.default\nis set to \"simple\". If they then set their branch.autosetupmerge to\n\"simple\" also, this is the simple coherent setup that I, at least,\nwould recommend to non-experts.\n\n> Setting the latter to 'simple'\n> means there are *MORE* branches that do not have .remote/.merge set\n> up, doesn't it?  Which in turn means that we are relying more on\n> what push.default is set to, right?\n\nNo - the idea here is that instead of telling push.default to do\nsomething *independent* of the tracking branch (like, for example,\n\"current\"), the setup the user ends up with is one where the tracking\nbranch, if there is one, is always the same-name where you will push\nto.\n\nWhen you create a new branch (by branching with a new name), your new\nbranch doesn't initially have an upstream tracking branch - and that's\nright and correct, there's literally nothing on the server for you to\ntrack yet - but the first time you push, the (existing) advice\nencourages you to set up that tracking relationship. In this flow you\nvery explicitly *don't* rely on push.default, because you never want\nto end up in a confusing (un-simple) situation where what you're\npulling from and what you're pushing to aren't the same thing - a\ntriangular workflow.\n\nThe \"push the current branch and set the remote as upstream\" advice is\nconsistent with how many/most GUIs will handle first push for a branch\nthat does not have an upstream tracking relationship yet - GUIs will\ntypically automatically specify (or set the UI default to) the\n\"--set-upstream\" option on that first push.\n"},{"id":"454044","messageId":"xmqqlevzkxrf.fsf@gitster.g","threadId":"57470","inReplyTo":"CAPMMpohQei9vBBm=7hC=N5LPwzMCED=fZcXyePnrkLCHfCJTZw@mail.gmail.com","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-21T01:53:24Z","receivedAt":"2022-04-21T01:53:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Tao Klerks <tao@klerks.biz> writes:\n\n> If they branched from a different-name remote branch (they created an\n> new / independent local branch), then no remote tracking relationship\n> will have been set up, and instead of the \"fatal: The upstream branch\n> of your current branch does not match\n> the name of your current branch\" error and advice, they will get a\n> much simpler error and advice:\n>\n> ---\n> fatal: The current branch whatevs has no upstream branch.\n> To push the current branch and set the remote as upstream, use\n>\n>     git push --set-upstream origin whatevs\n> ---\n>\n> When they follow those instructions, they will be in the \"simple\"\n> setup same as if they had just branched from same-name.\n\nWhich means that they need to see an error once, offered to either\nset push.default or branch.autosetupmerge (it is not \"and/or\", but\n\"or\", because you want to tell them to set \"instead of push.default,\nset branch.autosetupmerge\"), and if they follow the latter, they have\nto then hit a different error and be told to do the \"set-upstream\"\nindividually.  I am wondering if that is more irritating than it is\nworth.  Instead, if you tell them to use branch.autosetupmerge=simple\nand use push.default to something better than simple, wouldn't that\ncover more cases and give fewer roadblocks to the end-user with\nunnecessary errors?\n\n>> Setting the latter to 'simple'\n>> means there are *MORE* branches that do not have .remote/.merge set\n>> up, doesn't it?  Which in turn means that we are relying more on\n>> what push.default is set to, right?\n>\n> No\n\nWhy no?  if setupauto is yes, then any new branch forked from a\nremote-tracking branch will get .remote/.merge set up, and with these\nspecific configuration they can \"push\" back to the configured place.\nIf it is set to simple, only new branches forked from a remote-tracking\nbranch that happens to have the same name will get it, and others do\nnot get .remote/.merge set up.  Which means user's \"git push\" will then\nconsult push.default settings, and setting it right becomes more \nimportant, no?\n\n"},{"id":"454055","messageId":"CAPMMpoiCD+fG=bs2j4Rin5Pvip9Mre9iqLcOb2LYnDQK9cuRxw@mail.gmail.com","threadId":"57470","inReplyTo":"xmqqlevzkxrf.fsf@gitster.g","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-04-21T10:04:54Z","receivedAt":"2022-04-21T10:05:12Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Thu, Apr 21, 2022 at 3:53 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Tao Klerks <tao@klerks.biz> writes:\n>\n> > If they branched from a different-name remote branch (they created an\n> > new / independent local branch), then no remote tracking relationship\n> > will have been set up, and instead of the \"fatal: The upstream branch\n> > of your current branch does not match\n> > the name of your current branch\" error and advice, they will get a\n> > much simpler error and advice:\n> >\n> > ---\n> > fatal: The current branch whatevs has no upstream branch.\n> > To push the current branch and set the remote as upstream, use\n> >\n> >     git push --set-upstream origin whatevs\n> > ---\n> >\n> > When they follow those instructions, they will be in the \"simple\"\n> > setup same as if they had just branched from same-name.\n>\n> Which means that they need to see an error once, offered to either\n> set push.default or branch.autosetupmerge (it is not \"and/or\", but\n> \"or\", because you want to tell them to set \"instead of push.default,\n> set branch.autosetupmerge\"), and if they follow the latter, they have\n> to then hit a different error and be told to do the \"set-upstream\"\n> individually.\n\nThey don't *have* to hit that error, they can set --set-upstream\npre-emptively, but if they're \"just following prompts\" then that's\nwhat happens, yes.\n\n> I am wondering if that is more irritating than it is\n> worth.  Instead, if you tell them to use branch.autosetupmerge=simple\n> and use push.default to something better than simple, wouldn't that\n> cover more cases and give fewer roadblocks to the end-user with\n> unnecessary errors?\n\nI think you're on to something I missed here.\n\nUnfortunately, I'm not sure what \"something better than simple\" for\npush.default actually is, in the current system.\n\nThe most obvious option is to set it to \"current\", so:\n- you only get branch-time tracking for same-name branches because of\nbranch.autosetupmerge=simple, and\n- you always get same-name pushes regardless of whether there is an\nupstream or not thanks to push.default, so you never see a \"do this\nother thing to push\" message...\n\nBut then you have a new problem: While new branches push consistently,\nthey never have an upstream tracking ref! This in turn means these\nno-tracking-ref branches, although they push smoothly, do not show\nahead/behind state in \"git status\", and simply don't support a regular\n\"git pull\". That's not \"simple\".\n\nWhere I think you're onto something, is that I believe there *should*\nbe a way to say \"if I request a default push and there is *no* remote\ntracking branch, then just push to the first remote, using the same\nbranch name, *and set up tracking*\". Now that would be simple.\n\nI don't know whether that behavior would require yet another\npush.default value, or if there's a better way of integrating it into\nthe existing options/behaviors. I'm also not sure what should happen,\nin this scheme, if I happened to clash/overlap with an existing remote\ntracking branch. But this does seem like where I would like to end up.\n\n>\n> >> Setting the latter to 'simple'\n> >> means there are *MORE* branches that do not have .remote/.merge set\n> >> up, doesn't it?  Which in turn means that we are relying more on\n> >> what push.default is set to, right?\n> >\n> > No\n>\n> Why no?  if setupauto is yes, then any new branch forked from a\n> remote-tracking branch will get .remote/.merge set up, and with these\n> specific configuration they can \"push\" back to the configured place.\n> If it is set to simple, only new branches forked from a remote-tracking\n> branch that happens to have the same name will get it, and others do\n> not get .remote/.merge set up.\n\nBut as long as push.default is set to \"simple\", *which is the only way\nyou get the above message ever*, those cases where the new setupauto\noption avoids a tracking branch altogether simply change the error\nmessage from \"your remote branch name does not match - you have lots\nof options\" to \"you do not have a remote branch yet - push like this\n(and you'll be all set for this branch henceforth)\".\n\nInsofar as you can only ever get the \"you might want to set setupauto\nto simple\" message when push.default is set to simple, the set of\ncases where you get an error on push ends up being the exact same set\nof cases - you just get a clearer more sensible error.\n\n> Which means user's \"git push\" will then\n> consult push.default settings, and setting it right becomes more\n> important, no?\n\nIf there were another push.default option that led to more automatic\n*and* correct outcomes, I would agree - and I believe that pursuing\nthe existence of such an option makes sense.\n\nDo you agree that none of the push.default options available today are\n\"right\" for this flow? Do you have a preference or opinion as to\nwhether:\n* push.default=current should be changed to set up tracking when absent, or\n* push.default=simple should be changed to \"simply\" push and set up\ntracking when there is no tracking, or\n* a new push.default option should be introduced for this behavior, or\n* some other configuration should be introduced to specify \"and set up\ntracking on default push if missing\" (and if so, under what\ncircumstances should it kick in?)\n"},{"id":"454205","messageId":"xmqqzgkddf8m.fsf@gitster.g","threadId":"57470","inReplyTo":"CAPMMpoiCD+fG=bs2j4Rin5Pvip9Mre9iqLcOb2LYnDQK9cuRxw@mail.gmail.com","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-22T02:27:37Z","receivedAt":"2022-04-22T02:27:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Tao Klerks <tao@klerks.biz> writes:\n\n>> I am wondering if that is more irritating than it is\n>> worth.  Instead, if you tell them to use branch.autosetupmerge=simple\n>> and use push.default to something better than simple, wouldn't that\n>> cover more cases and give fewer roadblocks to the end-user with\n>> unnecessary errors?\n>\n> I think you're on to something I missed here.\n>\n> Unfortunately, I'm not sure what \"something better than simple\" for\n> push.default actually is, in the current system.\n\n\"none\", probably.  Much better than \"current\" that can create new\nbranches on the other side, which you would want to do with an\nexplicit end-user instruction (i.e. not with \"git push\", but with\n\"git push origin topic\").\n\nThis depends on what you are really trying to achieve.  If we think\nit through, perhaps it may turn out to be a combination of a bit\nflawed workflow with a bit inadequate toolset.\n\nWith \"simple\" (both in branch.autosetupmerge and push.default), I\ncan see that if you create \"main\" from their \"main\" and \"maint\" from\ntheir \"maint\", you want to see that\n\n (1) your \"git pull\" to integrate what happend on their \"main\" or\n     \"maint\" respectively, and\n\n (2) your \"git push\" to push what you did on your \"main\" to their\n     \"main\", and \"maint\" to \"maint\".\n\nBut it is totally unclear what you really want to do on \"topic\" you\ncreated this way:\n\n    $ git checkout -b topic origin/main\n\nCurrently, with both set to \"simple\", you do not even get .remote\nand .merge for the \"topic\" branch, so your \"git pull\" simply does\nnot work.  And \"git push\" will also refuse to work.\n\nBut then why are you [*] forking from origin/main in the first\nplace?  What is the purpose you created 'topic' and what do you\nplan to do with the result you develop on 'topic'?\n\n\tSide note: \"you\" do not refer to\"Tao, the advocate of the\n\t'simple' configuration\", but figuratively the user who\n\tfollowed the \"simple\" route and created topic out of\n\torigin/main that is not connected to origin/main.\n\nWhatever you commit on topic eventually becomes part of what you'd\npush to origin or elsewhere.  I'd assume it would be origin, because\nas the user who choose 'simple', you have some branches that you\npush back to the same name over there.  Presumably, those are the\nprimary integration branches the project has, like 'trunk', 'main',\n'master', etc.\n\nSo perhaps the user would have been better off to fork off of the\nLOCAL branch that would eventually be pushed back?  In other words,\nthe above user who created 'topic' would have done \n\n    $ git checkout -b main origin/main\n\nto use as a local integration branch that collects the work you will\ndo locally that is targetted for their 'main' track, so to create a\ntopic that aims to be part of what is pushed back to their 'main'\ntrack, you would want to do\n\n    $ git checkout -b topic main\n\ninstead?  That way, \"git push\" would either not get .merge/.remote\n(when branch.autosetupmerge is set to 'true') or point at your local\n'main' branch.\n\n - The symptom you get from the former is no better than what you\n   get from branch.autosetupmerge=simple but it is not worse.\n   \"push\" and \"pull\" refuses to work and suggest you to do something\n   additional.\n\n - The latter would make your \"git push\" and \"git pull\" on 'topic'\n   to work with your local 'main', treating your 'main' in a way\n   very similar to how you treat your remote 'main' when you are on\n   your own 'main', which is quite reasonable if your change flow is\n   designed to be \"work on topic, when the changes on topic proves\n   OK, send that to main, and when the changes on main proves OK,\n   send that to their main\".\n\nI guess I am esseentially saying that the usefulness of \"simple\" for\nbranch.autosetupmerge is dubious.\n\n> Do you agree that none of the push.default options available today are\n> \"right\" for this flow? Do you have a preference or opinion as to\n> whether:\n> * push.default=current should be changed to set up tracking when absent, or\n> * push.default=simple should be changed to \"simply\" push and set up\n> tracking when there is no tracking, or\n> * a new push.default option should be introduced for this behavior, or\n> * some other configuration should be introduced to specify \"and set up\n> tracking on default push if missing\" (and if so, under what\n> circumstances should it kick in?)\n\nNone of the above, I guess.\n"},{"id":"454238","messageId":"CAPMMpoj+g-XFKXoAXzW4d6WZRSBO_uE6MRsw2jWUPAjqWFQt2A@mail.gmail.com","threadId":"57470","inReplyTo":"xmqqzgkddf8m.fsf@gitster.g","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-04-22T09:24:26Z","receivedAt":"2022-04-22T09:24:49Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Fri, Apr 22, 2022 at 4:27 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Tao Klerks <tao@klerks.biz> writes:\n>\n> >> I am wondering if that is more irritating than it is\n> >> worth.  Instead, if you tell them to use branch.autosetupmerge=simple\n> >> and use push.default to something better than simple, wouldn't that\n> >> cover more cases and give fewer roadblocks to the end-user with\n> >> unnecessary errors?\n> >\n> > I think you're on to something I missed here.\n> >\n> > Unfortunately, I'm not sure what \"something better than simple\" for\n> > push.default actually is, in the current system.\n>\n> \"none\", probably.  Much better than \"current\" that can create new\n> branches on the other side, which you would want to do with an\n> explicit end-user instruction (i.e. not with \"git push\", but with\n> \"git push origin topic\").\n>\n\nHmm, I don't understand you here. You either mean \"simple is the best\noption you could choose for push.default, when\nbranch.autosetupmerge=simple, none of the other options are better\",\nor there's a small typo and you're saying \"push.default=nothing would\nbe better\". I'll assume the latter, but I'm not sure because I don't\nsee how it can be a good-faith statement.\n\n\"nothing\" is a good setting for someone who needs and/or wants to make\na conscious choice about where they push to, *every time* they push,\nregardless of any remote tracking information. For whom the\noperational and mental overhead of choosing a target ref to push to\nevery time is less than the cost of defaulting to *any* given workflow\nor \"local to remote branch mapping strategy\".\n\nHow could this possibly be something we recommend or think is\ngenerally \"best\" for arbitrary, especially novice, users??\n\n> This depends on what you are really trying to achieve.  If we think\n> it through, perhaps it may turn out to be a combination of a bit\n> flawed workflow with a bit inadequate toolset.\n>\n> With \"simple\" (both in branch.autosetupmerge and push.default), I\n> can see that if you create \"main\" from their \"main\" and \"maint\" from\n> their \"maint\", you want to see that\n>\n>  (1) your \"git pull\" to integrate what happend on their \"main\" or\n>      \"maint\" respectively, and\n>\n>  (2) your \"git push\" to push what you did on your \"main\" to their\n>      \"main\", and \"maint\" to \"maint\".\n>\n> But it is totally unclear what you really want to do on \"topic\" you\n> created this way:\n>\n>     $ git checkout -b topic origin/main\n>\n\nThe idea of the \"simple\" workflow which I propose to better support,\nis that in creating this branch in this way, you are very clearly\nsaying:\n---\nI want to create a new topic branch 'topic' that starts at the current\nstate of 'origin/main':\n* I will want to be able to \"back up\" my topic branch by pushing it to\nthe server - and \"git push\" should do the right thing, just that -\npush my current branch to the server.\n* I will want to be able to collaborate with others on this topic\nbranch - after I have pushed, and they have checked out this same\nbranch, we will all be able to push and pull on this branch seamlessly\n* I will choose whether and when to merge in newer changes from\n\"origin/main\", and do so explicitly (not using a simple \"git pull\")\n* I will choose whether and when to rebase on top of \"origin/main\",\nassuming I work alone on this branch or my collaborators are\nsufficiently comfortable with rebasing workflows that it will be ok,\nand do so explicitly (not using a simple \"git pull\")\n* I will choose whether and when to push my changes on this branch\nback to master, and do so by explicitly pushing master after having\nmerged in this topic branch\n---\n\nI would argue that git generally has a \"problem\", in that\n\"branch.XXX.merge\" entries have two classes of\nmeanings/interpretation:\n* That is the \"parent branch\"\n** The one I want changes from, when I say \"update me with changes\"\n** The one I eventually want to get my changes to\n* That is the \"remote instance/address\" of *this* branch\n** If I pull, it's to get changes to the same branch that others might have made\n** When I push, it's to get this branch onto the server (not to get my\nchanges into the \"upstream\")\n\nFor local-only branches, git currently encourages the former\ninterpretation; when you create the new branch, by default you get the\ntracking branch set up.\n\nAs soon as you wish to keep your changes on the remote, however, and\nespecially if you're going to share this topic branch with others,\n*you have to give up that interpretation*, for that branch! Git does\nnot provide you with any facility to indicate a \"parent branch\",\nbesides using \"branch.XXX.merge\" on *local* branches. There's also no\nway to share a concept of \"parent branch\" with others, via a remote,\ninherent in git. You can of course look at the commits on your branch\nand compare to the commit histories of other branches, you can use\nnaming strategies to indicate an intended parent, etc - but there's no\ninherent storage and signalling mechanism for this idea of a \"parent\nbranch\" that you create a topic branch from, with the intent of\nmerging back eventually.\n\nI call this a \"problem\" because, in my experience, it confuses people.\nThe system defaults to setting a \"branch.XXX.merge\" relationship,\npresumably in the hopes of being helpful, but as soon as you want to\nshare your topic branch (or even just back it up on the server without\njumping through strange hoops), you need to give up that \"helpfulness\"\nand switch to the other model where that just stores the remote\ninstance of the same branch, rather than the parent.\n\nMy proposal here is to support a workflow that accepts, and assumes,\nthat git does not really have the concept of a \"parent branch\", and\nthat \"branch.XXX.merge\" relationships exist primarily to support the\nrelationship between local branches and their remote instances.\n\nThat of course introduces a tradeoff/compromise: What the user loses,\nin this workflow, is the ability to have many very short-lived\nlocal-only branches, all with the same \"branch.XXX.merge\" upstream,\ntreating that upstream as the \"parent\" implicitly. This workflow does\nnot of course prevent you or discourage you from creating lots of\nshort-lived local branches - but it does take away the *assumption*\nthat they're local-only, and the corresponding facility to treat the\nupstream as the thing push and pull should work with.\n\nBased on your feedback here, maybe \"simple\" is not the right name to\nassociate with workflow, its assumptions and tradeoffs - I believe is\naccurately represents the intent and closely relates to the apparent\ndesign intent behind the push.default=simple option, but I'd love\nproposals as to how to name (and do) it better!\n\n> Currently, with both set to \"simple\", you do not even get .remote\n> and .merge for the \"topic\" branch, so your \"git pull\" simply does\n> not work.  And \"git push\" will also refuse to work.\n>\n\nThat's right - because the assumption is that you've just created a\nnew independent branch - independent by name, and therefore\nindependent by default. You can of course add \"--track\" if you know\nwhat you're doing and know this is a local-only branch and you want it\nto track what you branched from and have \"pull\" bring in changes from\nthere (without explicitly specifying so)!\n\n> But then why are you [*] forking from origin/main in the first\n> place?  What is the purpose you created 'topic' and what do you\n> plan to do with the result you develop on 'topic'?\n\nThe assumption, in this workflow, is that you plan to work on that\nbranch, potentially push to origin to back up or share your work, and\nwill decide explicitly when to merge in changes from the origin you\nbranched (forked) from, or merge changes up there.\n\n>\n>         Side note: \"you\" do not refer to\"Tao, the advocate of the\n>         'simple' configuration\", but figuratively the user who\n>         followed the \"simple\" route and created topic out of\n>         origin/main that is not connected to origin/main.\n>\n> Whatever you commit on topic eventually becomes part of what you'd\n> push to origin or elsewhere.  I'd assume it would be origin, because\n> as the user who choose 'simple', you have some branches that you\n> push back to the same name over there.  Presumably, those are the\n> primary integration branches the project has, like 'trunk', 'main',\n> 'master', etc.\n>\n> So perhaps the user would have been better off to fork off of the\n> LOCAL branch that would eventually be pushed back?  In other words,\n> the above user who created 'topic' would have done\n>\n>     $ git checkout -b main origin/main\n>\n\n(completely beside the point, but they would be more likely to have\njust done \"git checkout main\", for the same outcome)\n\n> to use as a local integration branch that collects the work you will\n> do locally that is targetted for their 'main' track, so to create a\n> topic that aims to be part of what is pushed back to their 'main'\n> track, you would want to do\n>\n>     $ git checkout -b topic main\n>\n> instead?  That way, \"git push\" would either not get .merge/.remote\n> (when branch.autosetupmerge is set to 'true') or point at your local\n> 'main' branch.\n\nI'm not sure I understand or agree with what you're saying here with\n\"would [otherwise] point at your local 'main' branch\". I have to\nassume you mean that would be the outcome with \"always\", while the\nformer would be the outcome with \"true\" and \"false\" (and the proposed\n\"simple\"), and there would be a third possible outcome with \"inherit\",\nwhere \"topic\" would end up tracking \"origin/main\" directly.\n\n>\n>  - The symptom you get from the former is no better than what you\n>    get from branch.autosetupmerge=simple but it is not worse.\n>    \"push\" and \"pull\" refuses to work and suggest you to do something\n>    additional.\n\nIn suggesting the user could/should have done that (in order to get a\nsane workflow, presumably), you are also suggesting that they should\nkeep the state of that \"local version of the upstream they eventually\nwant to get their changes into\" up-to-date: They should first check\nout master (for example), pull on master to get the state they expect,\nand *then* create their new differently-named local branch.\n\nIf they take a shortcut (specify the origin branch), they would get\nthe wrong behavior, and stand a good chance of not understanding what\nis happening. I think this is a \"bad\" process - a bad thing to force\nusers to learn/understand in order for them to be productive.\n\n>\n>  - The latter would make your \"git push\" and \"git pull\" on 'topic'\n>    to work with your local 'main', treating your 'main' in a way\n>    very similar to how you treat your remote 'main' when you are on\n>    your own 'main', which is quite reasonable if your change flow is\n>    designed to be \"work on topic, when the changes on topic proves\n>    OK, send that to main, and when the changes on main proves OK,\n>    send that to their main\".\n\n(assuming you were referring to a \"branch.autosetupmerge=always\" outcome)\n\nIt can be considered \"reasonable\" if this branch is local-only, yes.\nAs a user, you then need to understand this duality / distinction\nbetween local-only branches that pull directly against some \"semantic\nupstream\", and local-and-remote branches that\n\n>\n> I guess I am esseentially saying that the usefulness of \"simple\" for\n> branch.autosetupmerge is dubious.\n>\n\nI understand that, and respectfully disagree :)\n\n> > Do you agree that none of the push.default options available today are\n> > \"right\" for this flow? Do you have a preference or opinion as to\n> > whether:\n> > * push.default=current should be changed to set up tracking when absent, or\n> > * push.default=simple should be changed to \"simply\" push and set up\n> > tracking when there is no tracking, or\n> > * a new push.default option should be introduced for this behavior, or\n> > * some other configuration should be introduced to specify \"and set up\n> > tracking on default push if missing\" (and if so, under what\n> > circumstances should it kick in?)\n>\n> None of the above, I guess.\n\nI made a mistake here, in under-emphasising \"for this flow\"; your\nanswer seems to be more of an \"in general, git is powerful enough that\nif the user knows to and chooses to do the right thing, they get the\nright outcome, and this proposed flow is flawed because it\nunder-supports local short-lived never-individually-pushed branches\".\nI completely agree with the former, and while I agree I would love to\nhave an even better flow that could easily and transparently support\n\"parent branch\" and \"server representation of the branch\" as separate\nconcepts - git simply isn't there at this time (and I don't know how\nto get it there, and I suspect you and others would not want to bake\nsuch concepts into git).\n\nYou stated earlier that I would do my users a disservice in setting\nthings up to support this flow (by default), without making it\nsignificantly discoverable for the wider git user community, because\nthey would find that git later behaved differently in other settings.\nThis is true, but doing them a disservice in terms of git expertise\nacross contexts is far less important, to me in my context, than\nmaking them comfortable and productive in this specific context - and\ncoming from , the blinkered workflow I propose is a veritable utopia\nof power & flexibility compared to the very-central VCS they come\nfrom. It is also well in-line with how our governance processes and\nDevOps processes work, in terms of the meaning of \"branch\" on the\nshared server.\n\nAnyway, I've gone way off-topic I think. I hope I can convince you\nthat this workflow makes sense for some segment of the git current and\nfuture population, that (with adjustments yet to be made), pushing to\nsame-name on the remote with tracking implicitly/by default makes\nsense, and that making this workflow discoverable to users beyond my\norg would also have value.\n"},{"id":"454258","messageId":"CAPMMpojyO82ooz8hMAnd_nuGOc68Th_bRXF=2t+DuTJjR8xWgw@mail.gmail.com","threadId":"57470","inReplyTo":"CAPMMpoj+g-XFKXoAXzW4d6WZRSBO_uE6MRsw2jWUPAjqWFQt2A@mail.gmail.com","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-04-22T13:27:10Z","receivedAt":"2022-04-22T13:27:28Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Fri, Apr 22, 2022 at 11:24 AM Tao Klerks <tao@klerks.biz> wrote:\n>\n> On Fri, Apr 22, 2022 at 4:27 AM Junio C Hamano <gitster@pobox.com> wrote:\n> >\n> > Tao Klerks <tao@klerks.biz> writes:\n> >\n> > >\n> > > Unfortunately, I'm not sure what \"something better than simple\" for\n> > > push.default actually is, in the current system.\n> >\n> > \"none\", probably.  Much better than \"current\" that can create new\n> > branches on the other side, which you would want to do with an\n> > explicit end-user instruction (i.e. not with \"git push\", but with\n> > \"git push origin topic\").\n> >\n>\n> Hmm, I don't understand you here. You either mean \"simple is the best\n> option you could choose for push.default, when\n> branch.autosetupmerge=simple, none of the other options are better\",\n> or there's a small typo and you're saying \"push.default=nothing would\n> be better\". I'll assume the latter, but I'm not sure because I don't\n> see how it can be a good-faith statement.\n>\n\nMy apologies for the tone here, I clearly sent before re-reading\nproperly. I cannot presume to state what you did mean; I meant to say\nsomething like \"I assume you mean either X or Y\",  or \"my plausible\ninterpretations are X or Y\", or something similarly reflective of my\nown limitations.\n"},{"id":"454323","messageId":"xmqqv8v02yu9.fsf@gitster.g","threadId":"57470","inReplyTo":"CAPMMpoj+g-XFKXoAXzW4d6WZRSBO_uE6MRsw2jWUPAjqWFQt2A@mail.gmail.com","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-23T04:44:14Z","receivedAt":"2022-04-23T04:44:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Tao Klerks <tao@klerks.biz> writes:\n\n>> \"none\", probably.  Much better than \"current\" that can create new\n>> branches on the other side, which you would want to do with an\n>> explicit end-user instruction (i.e. not with \"git push\", but with\n>> \"git push origin topic\").\n\nSorry, \"nothing\" was what I meant.  Those non-simple branches are\nleft unconfigured with ASU=simple.  We both agree that the user does\nnot want to see the \"with push.default=simple you have, you cannot\npush from it\" but the reason why they do not want to see can be\nmultiple.  You are assuming that they want to push to somewhere\nelse.  I am taking into account that they may not want to push them\nat all, but only use locally.  If the intended workflow is\n\n\tgit checkout -b main [origin/main] ;# assuming DWIM is on \n\tgit checkout -b topic origin/main\n\t... work work work ...\n\tgit checkout main\n\tgit merge topic\n\t... test test test ...\n\t... ahh, no it does not work, back to fix topic ...\n\tgit reset --hard ORIG_HEAD\n\tgit checkout topic\n\t... work work work ...\n\tgit rebase -i ...\n\tgit checkout main\n\tgit merge topic\n\t... test test test ...\n        ... yay, this time it is perfect and we can push it out ...\n\tgit push\n\ni.e. matching \"simple\" branches like main are used to locally bundle\nwhat you locally worked on, and the result is pushed out to the\nother side from there, while non-simple branches like topic are used\nto locally work on your real changes, it is reasonable to expect\nthat the user wants \"git push\" to fail when the 'topic' branch is\nchecked out.\n\nBut unfortunately that does not work at the last step, as \"nothing\"\nunfortunately affects the last step that tries to check out 'main',\ntoo.  push.default='simple' would make it work.\n\n> I would argue that git generally has a \"problem\", in that\n> \"branch.XXX.merge\" entries have two classes of\n> meanings/interpretation:\n> * That is the \"parent branch\"\n> ** The one I want changes from, when I say \"update me with changes\"\n> ** The one I eventually want to get my changes to\n> * That is the \"remote instance/address\" of *this* branch\n> ** If I pull, it's to get changes to the same branch that others might have made\n> ** When I push, it's to get this branch onto the server (not to get my\n> changes into the \"upstream\")\n\nYes, that is very well known, and there arey mechanisms to support\nsome workflows that separates \"where I get changes from\" and \"where\nI publish my work\" (look for \"triangular workflows\" in the list\narchive).  \n\nThe thing is, \"simple\" is *NOT* meant for triangular workflow.  It\nwas to cater to novice users who are used to cvs/svn style\ncentralized \"there is one place everybody pulls from and pushes to,\nwhich is where they meet\" model.\n> Based on your feedback here, maybe \"simple\" is not the right name to\n> associate with workflow, its assumptions and tradeoffs - I believe is\n> accurately represents the intent and closely relates to the apparent\n> design intent behind the push.default=simple option, but I'd love\n> proposals as to how to name (and do) it better!\n>\n>> Currently, with both set to \"simple\", you do not even get .remote\n>> and .merge for the \"topic\" branch, so your \"git pull\" simply does\n>> not work.  And \"git push\" will also refuse to work.\n>>\n>\n> That's right - because the assumption is that you've just created a\n> new independent branch - independent by name, and therefore\n> independent by default. You can of course add \"--track\" if you know\n> what you're doing and know this is a local-only branch and you want it\n> to track what you branched from and have \"pull\" bring in changes from\n> there (without explicitly specifying so)!\n>\n>> But then why are you [*] forking from origin/main in the first\n>> place?  What is the purpose you created 'topic' and what do you\n>> plan to do with the result you develop on 'topic'?\n>\n> The assumption, in this workflow, is that you plan to work on that\n> branch, potentially push to origin to back up or share your work, and\n> will decide explicitly when to merge in changes from the origin you\n> branched (forked) from, or merge changes up there.\n>\n>>\n>>         Side note: \"you\" do not refer to\"Tao, the advocate of the\n>>         'simple' configuration\", but figuratively the user who\n>>         followed the \"simple\" route and created topic out of\n>>         origin/main that is not connected to origin/main.\n>>\n>> Whatever you commit on topic eventually becomes part of what you'd\n>> push to origin or elsewhere.  I'd assume it would be origin, because\n>> as the user who choose 'simple', you have some branches that you\n>> push back to the same name over there.  Presumably, those are the\n>> primary integration branches the project has, like 'trunk', 'main',\n>> 'master', etc.\n>>\n>> So perhaps the user would have been better off to fork off of the\n>> LOCAL branch that would eventually be pushed back?  In other words,\n>> the above user who created 'topic' would have done\n>>\n>>     $ git checkout -b main origin/main\n>>\n>\n> (completely beside the point, but they would be more likely to have\n> just done \"git checkout main\", for the same outcome)\n>\n>> to use as a local integration branch that collects the work you will\n>> do locally that is targetted for their 'main' track, so to create a\n>> topic that aims to be part of what is pushed back to their 'main'\n>> track, you would want to do\n>>\n>>     $ git checkout -b topic main\n>>\n>> instead?  That way, \"git push\" would either not get .merge/.remote\n>> (when branch.autosetupmerge is set to 'true') or point at your local\n>> 'main' branch.\n>\n> I'm not sure I understand or agree with what you're saying here with\n> \"would [otherwise] point at your local 'main' branch\". I have to\n> assume you mean that would be the outcome with \"always\",\n\nYeah, I meant to add the matching (when ... is set to ...) after the\nsentence and forgot.  You inferred what I meant to say correctly.\n\n> In suggesting the user could/should have done that (in order to get a\n> sane workflow, presumably), you are also suggesting that they should\n> keep the state of that \"local version of the upstream they eventually\n> want to get their changes into\" up-to-date: They should first check\n> out master (for example), pull on master to get the state they expect,\n> and *then* create their new differently-named local branch.\n\nFWIW, I am not.\n\nI do not think it is healthy nor necessary to make your local work\n\"catch up\" too often with the outside world unnecessarily, be it\ndone with rebase or with merge.  They _can_ update 'master' when\noutside world has something worth adding to your topic extra\ndependency on and then update 'topic' to include what you took to\n'master' from the outside.  Dissociating the 'topic' from outside\nworld is one way to encourage a better workflow.\n"},{"id":"454360","messageId":"CAPMMpohn6+RQV=jg9fQc4nt1tK6zE38xAwejxNfGh+-4Dp_JNw@mail.gmail.com","threadId":"57470","inReplyTo":"xmqqv8v02yu9.fsf@gitster.g","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-04-24T11:57:29Z","receivedAt":"2022-04-24T11:57:48Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Sat, Apr 23, 2022 at 6:44 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Tao Klerks <tao@klerks.biz> writes:\n>\n> >> \"none\", probably.  Much better than \"current\" that can create new\n> >> branches on the other side, which you would want to do with an\n> >> explicit end-user instruction (i.e. not with \"git push\", but with\n> >> \"git push origin topic\").\n>\n> Sorry, \"nothing\" was what I meant.  Those non-simple branches are\n> left unconfigured with ASU=simple.  We both agree that the user does\n> not want to see the \"with push.default=simple you have, you cannot\n> push from it\" but the reason why they do not want to see can be\n> multiple.  You are assuming that they want to push to somewhere\n> else.  I am taking into account that they may not want to push them\n> at all, but only use locally.  If the intended workflow is\n>\n>         git checkout -b main [origin/main] ;# assuming DWIM is on\n>         git checkout -b topic origin/main\n>         ... work work work ...\n>         git checkout main\n>         git merge topic\n>         ... test test test ...\n>         ... ahh, no it does not work, back to fix topic ...\n>         git reset --hard ORIG_HEAD\n>         git checkout topic\n>         ... work work work ...\n>         git rebase -i ...\n>         git checkout main\n>         git merge topic\n>         ... test test test ...\n>         ... yay, this time it is perfect and we can push it out ...\n>         git push\n\n(two interesting/surprising things here:\n1) The user chooses to merge to master *before* testing (does not test on topic)\n2) The user does not use CI pipelines of any kind\n)\n\n>\n> i.e. matching \"simple\" branches like main are used to locally bundle\n> what you locally worked on, and the result is pushed out to the\n> other side from there, while non-simple branches like topic are used\n> to locally work on your real changes, it is reasonable to expect\n> that the user wants \"git push\" to fail when the 'topic' branch is\n> checked out.\n\nI would argue that the user who wants push to fail here is a very rare\nuser. Presumably they thought they were somewhere else, and the \"git\npush\" was a complete mistake? (why else would you run the command??)\n\nFor users of a central repo (github, gitlab, bitbucket, teamhub, etc\netc) when they say \"git push\" it is normally/typically to, well...\npush their changes to the remote! To back them up because they're\nworried about losing them, or to share them with\nteammates/collaborators, to run CI, or for any other reason.\n\nI'm not arguing that \"nothing\" is useless to everyone, but I am\narguing that for the workflow you have highlighted above, \"nothing\" is\nnot a valuable setting. It provides close to no practical safety\nbenefits, and makes pushing shared branches much more awkward.\n\n>\n> But unfortunately that does not work at the last step, as \"nothing\"\n> unfortunately affects the last step that tries to check out 'main',\n> too.  push.default='simple' would make it work.\n>\n\nOK\n\n> > I would argue that git generally has a \"problem\", in that\n> > \"branch.XXX.merge\" entries have two classes of\n> > meanings/interpretation:\n> > * That is the \"parent branch\"\n> > ** The one I want changes from, when I say \"update me with changes\"\n> > ** The one I eventually want to get my changes to\n> > * That is the \"remote instance/address\" of *this* branch\n> > ** If I pull, it's to get changes to the same branch that others might have made\n> > ** When I push, it's to get this branch onto the server (not to get my\n> > changes into the \"upstream\")\n>\n> Yes, that is very well known, and there arey mechanisms to support\n> some workflows that separates \"where I get changes from\" and \"where\n> I publish my work\" (look for \"triangular workflows\" in the list\n> archive).\n\nYes, the best/simplest summary I've seen so far is the github blog\npost https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/\n\nIn the particular model highlighted there, you use \"branch.XXX.merge\"\nentries to indicate \"the parent branch\" while keeping the \"remote\nbranch instance\" separate, by leveraging the \"push.default=current\"\nsetting, but of course this \"parent branch\" information is still\nlocal-only, and you cannot collaborate on your feature/topic branch\nwith others. If someone else checks out your topic branch from your\nserver and pushes some changes to it, then your flow breaks, because\nyour \"git pull\" means \"bring in changes from the parent\", not \"bring\nin any changes that might have occurred on the topic branch\".\n\nI understand there are techniques/flows that users can choose to use,\nbut I don't think this changes the fundamental and, for beginners,\nproblematic, ambiguity of meaning of \"branch.XXX.merge\". The\n\"branch.autosetupmerge=simple\" proposal is to simplify it down to\n\"branch.XXX.merge entries indicate what the remote instance of this\nbranch is, and will normally be aligned with the name of the local\nbranch\". This \"simplification\" is incompatible with the particular\ntriangular workflow highlighted above.\n\n>\n> > In suggesting the user could/should have done that (in order to get a\n> > sane workflow, presumably), you are also suggesting that they should\n> > keep the state of that \"local version of the upstream they eventually\n> > want to get their changes into\" up-to-date: They should first check\n> > out master (for example), pull on master to get the state they expect,\n> > and *then* create their new differently-named local branch.\n>\n> FWIW, I am not.\n\nFair enough, sorry I misunderstood. What I meant is that you need to\n\"maintain\" your local master when you do eventually want to push up\nany topic branch, *and* any other time you do want to \"catch up\" with\nupstream changes; assuming you work on multiple topic branches in\nparallel (which is one of the \"superpowers\" of git), the local master\nhas lots of different reasons to change.\n\n>\n> I do not think it is healthy nor necessary to make your local work\n> \"catch up\" too often with the outside world unnecessarily, be it\n> done with rebase or with merge.  They _can_ update 'master' when\n> outside world has something worth adding to your topic extra\n> dependency on and then update 'topic' to include what you took to\n> 'master' from the outside.  Dissociating the 'topic' from outside\n> world is one way to encourage a better workflow.\n\nOn this we agree, I guess :)\n\n\nI will have another go at proposing a complete, easy-to-understand,\neasy-to-enter, \"simple\" workflow that emphasises local and remote\nbranch \"correspondence\" by encouraging \"branch.XXX.merge\" to always\nand automatically be set to the same-name branch on the remote (and\nnot any other \"parent\" you might have branched from when creating a\ntopic branch), and a reasonable non-intrusive, non-misleading way to\non-ramp into it.\n"},{"id":"454610","messageId":"CAPMMpoiD8KFMg2vwNNRN9ZM5tpkCTPswcknLtDsDJZ3YepnD7Q@mail.gmail.com","threadId":"57470","inReplyTo":"CAPMMpohn6+RQV=jg9fQc4nt1tK6zE38xAwejxNfGh+-4Dp_JNw@mail.gmail.com","subject":"Re: [PATCH v4] merge: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-04-29T07:31:02Z","receivedAt":"2022-04-29T07:31:19Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Sun, Apr 24, 2022 at 1:57 PM Tao Klerks <tao@klerks.biz> wrote:\n>\n>\n> I will have another go at proposing a complete, easy-to-understand,\n> easy-to-enter, \"simple\" workflow that emphasises local and remote\n> branch \"correspondence\" by encouraging \"branch.XXX.merge\" to always\n> and automatically be set to the same-name branch on the remote (and\n> not any other \"parent\" you might have branched from when creating a\n> topic branch), and a reasonable non-intrusive, non-misleading way to\n> on-ramp into it.\n\nI now have a complete proposal that I think is coherent, clear,\nimproves the user experience as desired, and does not interfere with\nany of the existing functionality/workflows. There are a couple\nniggles around naming that I'd like feedback on. I expect to submit\nthis new proposal today.\n\n(nb: it's become a patch series again)\n"},{"id":"454611","messageId":"pull.1161.v5.git.1651226206.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v4.git.1647843442911.gitgitgadget@gmail.com","subject":"[PATCH v5 0/3] New options to support \"simple\" centralized workflow","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-04-29T09:56:43Z","receivedAt":"2022-04-29T09:56:55Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"This patchset introduces two new configuration options, intended to be\nconsistent with and complementary to the push.default \"simple\" option. It\nalso improves remote-defaulting in \"default push\" scenarios.\n\nIn some \"simple\" centralized workflows, users expect remote tracking branch\nnames to match local branch names. \"git push\" pushes to the remote\nversion/instance of the branch, and \"git pull\" pulls any changes to the\nremote branch (changes made by the same user in another place, or by other\nusers). The default push.default option, \"simple\", supports this kind of\nworkflow by \"raising eyebrows\" if a user looks like they are trying to push\nto the \"wrong\" remote tracking branch.\n\nNone of the existing branch.autosetupmerge settings support this\nworkflow/expectation well, so the new \"branch.autosetupmerge=simple\" option\naddresses this - acting like the default \"remote\" option only when the\nremote branch and (new) local branch have the same name. The new option is\nreferred to in new advice in the push.default=simple mismatching remote\nbranch error text.\n\nAt a later stage in the new-branch workflow, when a user first goes to push\na new branch to the remote, the default \"git push\" will complain that there\nis no remote tracking branch (unless push.default=current). For users that\nalways expect remote branch names to match local branch names, on a single\nremote, this is inconvenient. New config setting \"push.autoSetupRemote\"\naddresses this by automatically specifying \"--set-upstream\" (and allowing\nthe push) when there is no configured remote for push.default options\n\"simple\", \"upstream\", and \"current\". In the case of \"current\", this helps\nmake \"pull\" work correctly (under these workflow assumptions). For the other\ntwo options the primary benefit is being able to simply say \"git push\" and\nnot be interrupted with an unnecessary \"but this is a new branch!\" error.\n\nAlong the way, we also enhance the remote-defaulting behavior for \"git push\"\n(and ls-remote) to not only support \"origin\" as the default remote, but\nrather any single configured remote. Default push should only fail for lack\nof a remote if there are none, or if there is more than one and none are\ncalled \"origin\".\n\nChanges since v4:\n\n * Changed patchset subject to \"New options to support \"simple\" centralized\n   workflow\", reflecting the fact that there are now two new config options\n   available\n * Added some advice to the default push \"mismatching remote tracking branch\n   name\" error, offering the new branch.autosetupmerge=simple option, so\n   that new users can potentially discover and benefit from it\n * Introduced a new commit improving the defaulting of remote for \"default\n   push\" (and ls-remote), and fixing and adding related tests\n * Introduced a new commit for new config setting push.autoSetupRemote,\n   which will avoid the need for users to explicitly push to a specific\n   origin, explicitly requesting tracking, when doing a default push for a\n   new branch (with advice and tests).\n * Rebased onto current 'master'\n\nOpen questions:\n\n * The exact text of the two new pieces of advice should get some review, it\n   is likely improvable\n * The name and config help of the \"push.autoSetupRemote\" config setting\n   should also be reviewed - there is confusion (at least in my mind)\n   between \"upstream\", \"remote tracking\", and \"remote merge\" concepts.\n\nTao Klerks (3):\n  branch: new autosetupmerge option 'simple' for matching branches\n  push: default to single remote even when not named origin\n  push: new config option \"push.autoSetupRemote\" supports \"simple\" push\n\n Documentation/config/branch.txt |  9 ++--\n Documentation/config/push.txt   | 11 +++++\n Documentation/git-branch.txt    | 18 +++++---\n branch.c                        | 27 +++++++++++-\n branch.h                        |  1 +\n builtin/push.c                  | 64 +++++++++++++++++++++------\n config.c                        |  3 ++\n remote.c                        |  2 +\n t/t3200-branch.sh               | 35 +++++++++++++++\n t/t5512-ls-remote.sh            | 17 ++++++--\n t/t5528-push-default.sh         | 77 ++++++++++++++++++++++++++++++++-\n transport.h                     |  1 +\n 12 files changed, 237 insertions(+), 28 deletions(-)\n\n\nbase-commit: 6cd33dceed60949e2dbc32e3f0f5e67c4c882e1e\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1161%2FTaoK%2Ffeature-branch-autosetupmerge-simple-v5\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1161/TaoK/feature-branch-autosetupmerge-simple-v5\nPull-Request: https://github.com/gitgitgadget/git/pull/1161\n\nRange-diff vs v4:\n\n 1:  eca8ab2eb7b ! 1:  5b08edcdeef merge: new autosetupmerge option 'simple' for matching branches\n     @@ Metadata\n      Author: Tao Klerks <tao@klerks.biz>\n      \n       ## Commit message ##\n     -    merge: new autosetupmerge option 'simple' for matching branches\n     +    branch: new autosetupmerge option 'simple' for matching branches\n      \n          With the default push.default option, \"simple\", beginners are\n          protected from accidentally pushing to the \"wrong\" branch in\n     @@ Commit message\n          a \"default push\", they get an error and explanation with options.\n      \n          There is a particular centralized workflow where this often happens:\n     -    a user branches to a new local feature branch from an existing\n     -    upstream branch, eg with \"checkout -b feature1 origin/master\". With\n     +    a user branches to a new local topic branch from an existing\n     +    remote branch, eg with \"checkout -b feature1 origin/master\". With\n          the default branch.autosetupmerge configuration (value \"true\"), git\n     -    will automatically add origin/master as the remote tracking branch.\n     +    will automatically add origin/master as the upstream tracking branch.\n      \n     -    When the user pushes with \"git push\", they get an error, and (amongst\n     -    other things) a suggestion to run \"git push origin HEAD\". Eventually\n     -    they figure out to add \"-u\" to change the tracking branch, or they set\n     -    push.default to \"current\", or some tooling does one or the other of\n     -    these things for them.\n     +    When the user pushes with a default \"git push\", with the intention of\n     +    pushing their (new) topic branch to the remote, they get an error, and\n     +    (amongst other things) a suggestion to run \"git push origin HEAD\".\n      \n     -    When one of their coworkers works on the same branch, they don't get\n     -    any of that weirdness. They just \"git checkout feature1\" and\n     -    everything works exactly as they expect, with the shared remote branch\n     -    set up as remote tracking branch, and push and pull working out of the\n     -    box.\n     +    If they follow this suggestion the push succeeds, but on subsequent\n     +    default pushes they continue to get an error - so eventually they\n     +    figure out to add \"-u\" to change the tracking branch, or they spelunk\n     +    the push.default config doc as proposed and set it to \"current\", or\n     +    some GUI tooling does one or the other of these things for them.\n     +\n     +    When one of their coworkers later works on the same topic branch,\n     +    they don't get any of that \"weirdness\". They just \"git checkout\n     +    feature1\" and everything works exactly as they expect, with the shared\n     +    remote branch set up as remote tracking branch, and push and pull\n     +    working out of the box.\n      \n          The \"stable state\" for this way of working is that local branches have\n          the same-name remote tracking branch (origin/feature1 in this\n     @@ Commit message\n          the remote \"master\" branch instead (a completely different class of\n          changes!)\n      \n     -    Any experienced git user will presumably say \"well yeah, that's what\n     -    it means to have the remote tracking branch set to origin/master!\" -\n     -    but that user didn't *ask* to have the remote master branch added as\n     -    remote tracking branch - that just happened automatically when they\n     -    branched their feature branch. They didn't necessarily even notice or\n     -    understand the meaning of the \"set up to track 'origin/master'\"\n     +    An experienced git user might say \"well yeah, that's what it means to\n     +    have the remote tracking branch set to origin/master!\" - but the\n     +    original user above didn't *ask* to have the remote master branch\n     +    added as remote tracking branch - that just happened automatically\n     +    when they branched their feature branch. They didn't necessarily even\n     +    notice or understand the meaning of the \"set up to track 'origin/master'\"\n          message when they created the branch - especially if they are using a\n          GUI.\n      \n          Looking at how to fix this, you might think \"OK, so disable auto setup\n          of remote tracking - set branch.autosetupmerge to false\" - but that\n          will inconvenience the *second* user in this story - the one who just\n     -    wanted to start working on the feature branch. The first and second\n     +    wanted to start working on the topic branch. The first and second\n          users swap roles at different points in time of course - they should\n          both have a sane configuration that does the right thing in both\n          situations.\n      \n     -    Make these flows painless by introducing a new branch.autosetupmerge\n     -    option called \"simple\", to match the same-name \"push.default\" option\n     -    that makes similar assumptions.\n     +    Make this \"branches have the same name locally as on the remote\"\n     +    workflow less painful / more obvious by introducing a new\n     +    branch.autosetupmerge option called \"simple\", to match the same-name\n     +    \"push.default\" option that makes similar assumptions.\n      \n          This new option automatically sets up tracking in a *subset* of the\n          current default situations: when the original ref is a remote tracking\n          branch *and* has the same branch name on the remote (as the new local\n          branch name).\n      \n     +    Update the error displayed when the 'push.default=simple' configuration\n     +    rejects a mismatching-upstream-name default push, to offer this new\n     +    branch.autosetupmerge option that will prevent this class of error.\n     +\n          With this new configuration, in the example situation above, the first\n          user does *not* get origin/master set up as the tracking branch for\n          the new local branch. If they \"git pull\" in their new local-only\n     @@ Documentation/git-branch.txt: The exact upstream branch is chosen depending on t\n      \n       ## branch.c ##\n      @@ branch.c: static int find_tracked_branch(struct remote *remote, void *priv)\n     - \t\t\tfree(tracking->spec.src);\n       \t\t\tstring_list_clear(tracking->srcs, 0);\n     + \t\tbreak;\n       \t\t}\n      +\t\t/* remote_find_tracking() searches by src if present */\n       \t\ttracking->spec.src = NULL;\n     @@ branch.c: static void setup_tracking(const char *new_ref, const char *orig_ref,\n      +\t * that supports multiple entries in tracking_srcs but\n      +\t * leaves tracking.matches at 0.\n      +\t */\n     - \tif (tracking.matches > 1)\n     - \t\tdie(_(\"not tracking: ambiguous information for ref %s\"),\n     - \t\t    orig_ref);\n     + \tif (tracking.matches > 1) {\n     + \t\tint status = die_message(_(\"not tracking: ambiguous information for ref '%s'\"),\n     + \t\t\t\t\t    orig_ref);\n     +@@ branch.c: static void setup_tracking(const char *new_ref, const char *orig_ref,\n     + \t\texit(status);\n     + \t}\n       \n      +\tif (track == BRANCH_TRACK_SIMPLE) {\n      +\t\t/*\n     @@ branch.c: static void setup_tracking(const char *new_ref, const char *orig_ref,\n       \tif (tracking.srcs->nr < 1)\n       \t\tstring_list_append(tracking.srcs, orig_ref);\n       \tif (install_branch_config_multiple_remotes(config_flags, new_ref,\n     +@@ branch.c: static int submodule_create_branch(struct repository *r,\n     + \t\t/* Default for \"git checkout\". Do not pass --track. */\n     + \tcase BRANCH_TRACK_REMOTE:\n     + \t\t/* Default for \"git branch\". Do not pass --track. */\n     ++\tcase BRANCH_TRACK_SIMPLE:\n     ++\t\t/* Config-driven only. Do not pass --track. */\n     + \t\tbreak;\n     + \t}\n     + \n      \n       ## branch.h ##\n      @@ branch.h: enum branch_track {\n     @@ branch.h: enum branch_track {\n       \n       extern enum branch_track git_branch_track;\n      \n     + ## builtin/push.c ##\n     +@@\n     +  * \"git push\"\n     +  */\n     + #include \"cache.h\"\n     ++#include \"branch.h\"\n     + #include \"config.h\"\n     + #include \"refs.h\"\n     + #include \"refspec.h\"\n     +@@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,\n     + \t * upstream to a non-branch, we should probably be showing\n     + \t * them the big ugly fully qualified ref.\n     + \t */\n     +-\tconst char *advice_maybe = \"\";\n     ++\tconst char *advice_pushdefault_maybe = \"\";\n     ++\tconst char *advice_automergesimple_maybe = \"\";\n     + \tconst char *short_upstream = branch->merge[0]->src;\n     + \n     + \tskip_prefix(short_upstream, \"refs/heads/\", &short_upstream);\n     +@@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,\n     + \t * push.default.\n     + \t */\n     + \tif (push_default == PUSH_DEFAULT_UNSPECIFIED)\n     +-\t\tadvice_maybe = _(\"\\n\"\n     ++\t\tadvice_pushdefault_maybe = _(\"\\n\"\n     + \t\t\t\t \"To choose either option permanently, \"\n     +-\t\t\t\t \"see push.default in 'git help config'.\");\n     ++\t\t\t\t \"see push.default in 'git help config'.\\n\");\n     ++\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n     ++\t\tadvice_automergesimple_maybe = _(\"\\n\"\n     ++\t\t\t\t \"To avoid automatically configuring \"\n     ++\t\t\t\t \"upstream branches when their name\\n\"\n     ++\t\t\t\t \"doesn't match the local branch, see option \"\n     ++\t\t\t\t \"'simple' of branch.autosetupmerge\\n\"\n     ++\t\t\t\t \"in 'git help config'.\\n\");\n     + \tdie(_(\"The upstream branch of your current branch does not match\\n\"\n     + \t      \"the name of your current branch.  To push to the upstream branch\\n\"\n     + \t      \"on the remote, use\\n\"\n     +@@ builtin/push.c: static NORETURN void die_push_simple(struct branch *branch,\n     + \t      \"To push to the branch of the same name on the remote, use\\n\"\n     + \t      \"\\n\"\n     + \t      \"    git push %s HEAD\\n\"\n     +-\t      \"%s\"),\n     ++\t      \"%s%s\"),\n     + \t    remote->name, short_upstream,\n     +-\t    remote->name, advice_maybe);\n     ++\t    remote->name, advice_pushdefault_maybe,\n     ++\t    advice_automergesimple_maybe);\n     + }\n     + \n     + static const char message_detached_head_die[] =\n     +\n       ## config.c ##\n      @@ config.c: static int git_default_branch_config(const char *var, const char *value)\n       \t\t} else if (value && !strcmp(value, \"inherit\")) {\n -:  ----------- > 2:  31184c3a65d push: default to single remote even when not named origin\n -:  ----------- > 3:  41c88e51ac6 push: new config option \"push.autoSetupRemote\" supports \"simple\" push\n\n-- \ngitgitgadget\n"},{"id":"454612","messageId":"5b08edcdeefdaf2bd7bbc111bec82281b2f2b313.1651226207.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v5.git.1651226206.gitgitgadget@gmail.com","subject":"[PATCH v5 1/3] branch: new autosetupmerge option 'simple' for matching branches","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-04-29T09:56:44Z","receivedAt":"2022-04-29T09:57:00Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nWith the default push.default option, \"simple\", beginners are\nprotected from accidentally pushing to the \"wrong\" branch in\ncentralized workflows: if the remote tracking branch they would push\nto does not have the same name as the local branch, and they try to do\na \"default push\", they get an error and explanation with options.\n\nThere is a particular centralized workflow where this often happens:\na user branches to a new local topic branch from an existing\nremote branch, eg with \"checkout -b feature1 origin/master\". With\nthe default branch.autosetupmerge configuration (value \"true\"), git\nwill automatically add origin/master as the upstream tracking branch.\n\nWhen the user pushes with a default \"git push\", with the intention of\npushing their (new) topic branch to the remote, they get an error, and\n(amongst other things) a suggestion to run \"git push origin HEAD\".\n\nIf they follow this suggestion the push succeeds, but on subsequent\ndefault pushes they continue to get an error - so eventually they\nfigure out to add \"-u\" to change the tracking branch, or they spelunk\nthe push.default config doc as proposed and set it to \"current\", or\nsome GUI tooling does one or the other of these things for them.\n\nWhen one of their coworkers later works on the same topic branch,\nthey don't get any of that \"weirdness\". They just \"git checkout\nfeature1\" and everything works exactly as they expect, with the shared\nremote branch set up as remote tracking branch, and push and pull\nworking out of the box.\n\nThe \"stable state\" for this way of working is that local branches have\nthe same-name remote tracking branch (origin/feature1 in this\nexample), and multiple people can work on that remote feature branch\nat the same time, trusting \"git pull\" to merge or rebase as required\nfor them to be able to push their interim changes to that same feature\nbranch on that same remote.\n\n(merging from the upstream \"master\" branch, and merging back to it,\nare separate more involved processes in this flow).\n\nThere is a problem in this flow/way of working, however, which is that\nthe first user, when they first branched from origin/master, ended up\nwith the \"wrong\" remote tracking branch (different from the stable\nstate). For a while, before they pushed (and maybe longer, if they\ndon't use -u/--set-upstream), their \"git pull\" wasn't getting other\nusers' changes to the feature branch - it was getting any changes from\nthe remote \"master\" branch instead (a completely different class of\nchanges!)\n\nAn experienced git user might say \"well yeah, that's what it means to\nhave the remote tracking branch set to origin/master!\" - but the\noriginal user above didn't *ask* to have the remote master branch\nadded as remote tracking branch - that just happened automatically\nwhen they branched their feature branch. They didn't necessarily even\nnotice or understand the meaning of the \"set up to track 'origin/master'\"\nmessage when they created the branch - especially if they are using a\nGUI.\n\nLooking at how to fix this, you might think \"OK, so disable auto setup\nof remote tracking - set branch.autosetupmerge to false\" - but that\nwill inconvenience the *second* user in this story - the one who just\nwanted to start working on the topic branch. The first and second\nusers swap roles at different points in time of course - they should\nboth have a sane configuration that does the right thing in both\nsituations.\n\nMake this \"branches have the same name locally as on the remote\"\nworkflow less painful / more obvious by introducing a new\nbranch.autosetupmerge option called \"simple\", to match the same-name\n\"push.default\" option that makes similar assumptions.\n\nThis new option automatically sets up tracking in a *subset* of the\ncurrent default situations: when the original ref is a remote tracking\nbranch *and* has the same branch name on the remote (as the new local\nbranch name).\n\nUpdate the error displayed when the 'push.default=simple' configuration\nrejects a mismatching-upstream-name default push, to offer this new\nbranch.autosetupmerge option that will prevent this class of error.\n\nWith this new configuration, in the example situation above, the first\nuser does *not* get origin/master set up as the tracking branch for\nthe new local branch. If they \"git pull\" in their new local-only\nbranch, they get an error explaining there is no upstream branch -\nwhich makes sense and is helpful. If they \"git push\", they get an\nerror explaining how to push *and* suggesting they specify\n--set-upstream - which is exactly the right thing to do for them.\n\nThis new option is likely not appropriate for users intentionally\nimplementing a \"triangular workflow\" with a shared upstream tracking\nbranch, that they \"git pull\" in and a \"private\" feature branch that\nthey push/force-push to just for remote safe-keeping until they are\nready to push up to the shared branch explicitly/separately. Such\nusers are likely to prefer keeping the current default\nmerge.autosetupmerge=true behavior, and change their push.default to\n\"current\".\n\nAlso extend the existing branch tests with three new cases testing\nthis option - the obvious matching-name and non-matching-name cases,\nand also a non-matching-ref-type case. The matching-name case needs to\ntemporarily create an independent repo to fetch from, as the general\nstrategy of using the local repo as the remote in these tests\nprecludes locally branching with the same name as in the \"remote\".\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n Documentation/config/branch.txt |  4 +++-\n Documentation/git-branch.txt    | 18 ++++++++++-------\n branch.c                        | 27 ++++++++++++++++++++++++-\n branch.h                        |  1 +\n builtin/push.c                  | 20 ++++++++++++++-----\n config.c                        |  3 +++\n t/t3200-branch.sh               | 35 +++++++++++++++++++++++++++++++++\n 7 files changed, 94 insertions(+), 14 deletions(-)\n\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 1e0c7af014b..8df10d07129 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -9,7 +9,9 @@ branch.autoSetupMerge::\n \tautomatic setup is done when the starting point is either a\n \tlocal branch or remote-tracking branch; `inherit` -- if the starting point\n \thas a tracking configuration, it is copied to the new\n-\tbranch. This option defaults to true.\n+\tbranch; `simple` -- automatic setup is done only when the starting point\n+\tis a remote-tracking branch and the new branch has the same name as the\n+\tremote branch. This option defaults to true.\n \n branch.autoSetupRebase::\n \tWhen a new branch is created with 'git branch', 'git switch' or 'git checkout'\ndiff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt\nindex c8b4f9ce3c7..ae82378349d 100644\n--- a/Documentation/git-branch.txt\n+++ b/Documentation/git-branch.txt\n@@ -221,13 +221,17 @@ The exact upstream branch is chosen depending on the optional argument:\n itself as the upstream; `--track=inherit` means to copy the upstream\n configuration of the start-point branch.\n +\n-`--track=direct` is the default when the start point is a remote-tracking branch.\n-Set the branch.autoSetupMerge configuration variable to `false` if you\n-want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`\n-were given. Set it to `always` if you want this behavior when the\n-start-point is either a local or remote-tracking branch. Set it to\n-`inherit` if you want to copy the tracking configuration from the\n-branch point.\n+The branch.autoSetupMerge configuration variable specifies how `git switch`,\n+`git checkout` and `git branch` should behave when neither `--track` nor\n+`--no-track` are specified:\n++\n+The default option, `true`, behaves as though `--track=direct`\n+were given whenever the start-point is a remote-tracking branch.\n+`false` behaves as if `--no-track` were given. `always` behaves as though\n+`--track=direct` were given. `inherit` behaves as though `--track=inherit`\n+were given. `simple` behaves as though `--track=direct` were given only when\n+the start-point is a remote-tracking branch and the new branch has the same\n+name as the remote branch.\n +\n See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on\n how the `branch.<name>.remote` and `branch.<name>.merge` options are used.\ndiff --git a/branch.c b/branch.c\nindex 01ecb816d5c..962aa7c8609 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -44,9 +44,9 @@ static int find_tracked_branch(struct remote *remote, void *priv)\n \t\t\tstring_list_clear(tracking->srcs, 0);\n \t\tbreak;\n \t\t}\n+\t\t/* remote_find_tracking() searches by src if present */\n \t\ttracking->spec.src = NULL;\n \t}\n-\n \treturn 0;\n }\n \n@@ -264,15 +264,23 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,\n \n \tif (!tracking.matches)\n \t\tswitch (track) {\n+\t\t/* If ref is not remote, still use local */\n \t\tcase BRANCH_TRACK_ALWAYS:\n \t\tcase BRANCH_TRACK_EXPLICIT:\n \t\tcase BRANCH_TRACK_OVERRIDE:\n+\t\t/* Remote matches not evaluated */\n \t\tcase BRANCH_TRACK_INHERIT:\n \t\t\tbreak;\n+\t\t/* Otherwise, if no remote don't track */\n \t\tdefault:\n \t\t\tgoto cleanup;\n \t\t}\n \n+\t/*\n+\t * This check does not apply to BRANCH_TRACK_INHERIT;\n+\t * that supports multiple entries in tracking_srcs but\n+\t * leaves tracking.matches at 0.\n+\t */\n \tif (tracking.matches > 1) {\n \t\tint status = die_message(_(\"not tracking: ambiguous information for ref '%s'\"),\n \t\t\t\t\t    orig_ref);\n@@ -307,6 +315,21 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,\n \t\texit(status);\n \t}\n \n+\tif (track == BRANCH_TRACK_SIMPLE) {\n+\t\t/*\n+\t\t * Only track if remote branch name matches.\n+\t\t * Reaching into items[0].string is safe because\n+\t\t * we know there is at least one and not more than\n+\t\t * one entry (because only BRANCH_TRACK_INHERIT can\n+\t\t * produce more than one entry).\n+\t\t */\n+\t\tconst char *tracked_branch;\n+\t\tif (!skip_prefix(tracking.srcs->items[0].string,\n+\t\t\t\t \"refs/heads/\", &tracked_branch) ||\n+\t\t    strcmp(tracked_branch, new_ref))\n+\t\t\treturn;\n+\t}\n+\n \tif (tracking.srcs->nr < 1)\n \t\tstring_list_append(tracking.srcs, orig_ref);\n \tif (install_branch_config_multiple_remotes(config_flags, new_ref,\n@@ -603,6 +626,8 @@ static int submodule_create_branch(struct repository *r,\n \t\t/* Default for \"git checkout\". Do not pass --track. */\n \tcase BRANCH_TRACK_REMOTE:\n \t\t/* Default for \"git branch\". Do not pass --track. */\n+\tcase BRANCH_TRACK_SIMPLE:\n+\t\t/* Config-driven only. Do not pass --track. */\n \t\tbreak;\n \t}\n \ndiff --git a/branch.h b/branch.h\nindex 04df2aa5b51..560b6b96a8f 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -12,6 +12,7 @@ enum branch_track {\n \tBRANCH_TRACK_EXPLICIT,\n \tBRANCH_TRACK_OVERRIDE,\n \tBRANCH_TRACK_INHERIT,\n+\tBRANCH_TRACK_SIMPLE,\n };\n \n extern enum branch_track git_branch_track;\ndiff --git a/builtin/push.c b/builtin/push.c\nindex cad997965a7..447f91f5b47 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -2,6 +2,7 @@\n  * \"git push\"\n  */\n #include \"cache.h\"\n+#include \"branch.h\"\n #include \"config.h\"\n #include \"refs.h\"\n #include \"refspec.h\"\n@@ -151,7 +152,8 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t * upstream to a non-branch, we should probably be showing\n \t * them the big ugly fully qualified ref.\n \t */\n-\tconst char *advice_maybe = \"\";\n+\tconst char *advice_pushdefault_maybe = \"\";\n+\tconst char *advice_automergesimple_maybe = \"\";\n \tconst char *short_upstream = branch->merge[0]->src;\n \n \tskip_prefix(short_upstream, \"refs/heads/\", &short_upstream);\n@@ -161,9 +163,16 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t * push.default.\n \t */\n \tif (push_default == PUSH_DEFAULT_UNSPECIFIED)\n-\t\tadvice_maybe = _(\"\\n\"\n+\t\tadvice_pushdefault_maybe = _(\"\\n\"\n \t\t\t\t \"To choose either option permanently, \"\n-\t\t\t\t \"see push.default in 'git help config'.\");\n+\t\t\t\t \"see push.default in 'git help config'.\\n\");\n+\tif (git_branch_track != BRANCH_TRACK_SIMPLE)\n+\t\tadvice_automergesimple_maybe = _(\"\\n\"\n+\t\t\t\t \"To avoid automatically configuring \"\n+\t\t\t\t \"upstream branches when their name\\n\"\n+\t\t\t\t \"doesn't match the local branch, see option \"\n+\t\t\t\t \"'simple' of branch.autosetupmerge\\n\"\n+\t\t\t\t \"in 'git help config'.\\n\");\n \tdie(_(\"The upstream branch of your current branch does not match\\n\"\n \t      \"the name of your current branch.  To push to the upstream branch\\n\"\n \t      \"on the remote, use\\n\"\n@@ -173,9 +182,10 @@ static NORETURN void die_push_simple(struct branch *branch,\n \t      \"To push to the branch of the same name on the remote, use\\n\"\n \t      \"\\n\"\n \t      \"    git push %s HEAD\\n\"\n-\t      \"%s\"),\n+\t      \"%s%s\"),\n \t    remote->name, short_upstream,\n-\t    remote->name, advice_maybe);\n+\t    remote->name, advice_pushdefault_maybe,\n+\t    advice_automergesimple_maybe);\n }\n \n static const char message_detached_head_die[] =\ndiff --git a/config.c b/config.c\nindex a5e11aad7fe..8dbeb1932e5 100644\n--- a/config.c\n+++ b/config.c\n@@ -1781,6 +1781,9 @@ static int git_default_branch_config(const char *var, const char *value)\n \t\t} else if (value && !strcmp(value, \"inherit\")) {\n \t\t\tgit_branch_track = BRANCH_TRACK_INHERIT;\n \t\t\treturn 0;\n+\t\t} else if (value && !strcmp(value, \"simple\")) {\n+\t\t\tgit_branch_track = BRANCH_TRACK_SIMPLE;\n+\t\t\treturn 0;\n \t\t}\n \t\tgit_branch_track = git_config_bool(var, value);\n \t\treturn 0;\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex e12db593615..9723c2827cc 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -886,6 +886,41 @@ test_expect_success 'branch from tag w/--track causes failure' '\n \ttest_must_fail git branch --track my11 foobar\n '\n \n+test_expect_success 'simple tracking works when remote branch name matches' '\n+\ttest_when_finished \"rm -rf otherserver\" &&\n+\tgit init otherserver &&\n+\ttest_commit -C otherserver my_commit 1 &&\n+\tgit -C otherserver branch feature &&\n+\ttest_config branch.autosetupmerge simple &&\n+\ttest_config remote.otherserver.url otherserver &&\n+\ttest_config remote.otherserver.fetch refs/heads/*:refs/remotes/otherserver/* &&\n+\tgit fetch otherserver &&\n+\tgit branch feature otherserver/feature &&\n+\ttest_cmp_config otherserver branch.feature.remote &&\n+\ttest_cmp_config refs/heads/feature branch.feature.merge\n+'\n+\n+test_expect_success 'simple tracking skips when remote branch name does not match' '\n+\ttest_config branch.autosetupmerge simple &&\n+\ttest_config remote.local.url . &&\n+\ttest_config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+\tgit fetch local &&\n+\tgit branch my-other local/main &&\n+\ttest_cmp_config \"\" --default \"\" branch.my-other.remote &&\n+\ttest_cmp_config \"\" --default \"\" branch.my-other.merge\n+'\n+\n+test_expect_success 'simple tracking skips when remote ref is not a branch' '\n+\ttest_config branch.autosetupmerge simple &&\n+\ttest_config remote.localtags.url . &&\n+\ttest_config remote.localtags.fetch refs/tags/*:refs/remotes/localtags/* &&\n+\tgit tag mytag12 main &&\n+\tgit fetch localtags &&\n+\tgit branch mytag12 localtags/mytag12 &&\n+\ttest_cmp_config \"\" --default \"\" branch.mytag12.remote &&\n+\ttest_cmp_config \"\" --default \"\" branch.mytag12.merge\n+'\n+\n test_expect_success '--set-upstream-to fails on multiple branches' '\n \techo \"fatal: too many arguments to set new upstream\" >expect &&\n \ttest_must_fail git branch --set-upstream-to main a b c 2>err &&\n-- \ngitgitgadget\n\n"},{"id":"454613","messageId":"31184c3a65d64826a7f546fcc04f6efc6f2d017f.1651226207.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v5.git.1651226206.gitgitgadget@gmail.com","subject":"[PATCH v5 2/3] push: default to single remote even when not named origin","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-04-29T09:56:45Z","receivedAt":"2022-04-29T09:57:06Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nWith \"push.default=current\" configured, a simple \"git push\" will push to\nthe same-name branch on the current branch's branch.<name>.pushRemote, or\nremote.pushDefault, or origin. If none of these are defined, the push will\nfail with error \"fatal: No configured push destination\".\n\nThe same \"default to origin if no config\" behavior applies with\n\"push.default=matching\".\n\nOther commands use \"origin\" as a default when there are multiple options,\nbut default to the single remote when there is only one - for example,\n\"git checkout <something>\". This \"assume the single remote if there is\nonly one\" behavior is more friendly/useful than a defaulting behavior\nthat only uses the name \"origin\" no matter what.\n\nUpdate \"git push\" to also default to the single remote (and finally fall\nback to \"origin\" as default if there are several), for\n\"push.default=current\" and for other current and future remote-defaulting\npush behaviors.\n\nThis change also modifies the behavior of ls-remote in a consistent way,\nso defaulting not only supplies 'origin', but any single configured remote\nalso.\n\nDocument the change in behavior, correct incorrect assumptions in related\ntests, and add test cases reflecting this new single-remote-defaulting\nbehavior.\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n Documentation/config/branch.txt |  5 +--\n remote.c                        |  2 ++\n t/t5512-ls-remote.sh            | 17 +++++++--\n t/t5528-push-default.sh         | 63 ++++++++++++++++++++++++++++++++-\n 4 files changed, 81 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/branch.txt b/Documentation/config/branch.txt\nindex 8df10d07129..445341a906b 100644\n--- a/Documentation/config/branch.txt\n+++ b/Documentation/config/branch.txt\n@@ -40,8 +40,9 @@ branch.<name>.remote::\n \tmay be overridden with `remote.pushDefault` (for all branches).\n \tThe remote to push to, for the current branch, may be further\n \toverridden by `branch.<name>.pushRemote`.  If no remote is\n-\tconfigured, or if you are not on any branch, it defaults to\n-\t`origin` for fetching and `remote.pushDefault` for pushing.\n+\tconfigured, or if you are not on any branch and there is more than\n+\tone remote defined in the repository, it defaults to `origin` for\n+\tfetching and `remote.pushDefault` for pushing.\n \tAdditionally, `.` (a period) is the current local repository\n \t(a dot-repository), see `branch.<name>.merge`'s final note below.\n \ndiff --git a/remote.c b/remote.c\nindex 42a4e7106e1..930fdc9c2f6 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -543,6 +543,8 @@ static const char *remotes_remote_for_branch(struct remote_state *remote_state,\n \t}\n \tif (explicit)\n \t\t*explicit = 0;\n+\tif (remote_state->remotes_nr == 1)\n+\t\treturn remote_state->remotes[0]->name;\n \treturn \"origin\";\n }\n \ndiff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh\nindex f53f58895a1..20d063fb9ae 100755\n--- a/t/t5512-ls-remote.sh\n+++ b/t/t5512-ls-remote.sh\n@@ -15,6 +15,10 @@ generate_references () {\n \tdone\n }\n \n+test_expect_success 'dies when no remote found' '\n+\ttest_must_fail git ls-remote\n+'\n+\n test_expect_success setup '\n \t>file &&\n \tgit add file &&\n@@ -30,7 +34,8 @@ test_expect_success setup '\n \tgit show-ref -d\t>refs &&\n \tsed -e \"s/ /\t/\" refs >>expected.all &&\n \n-\tgit remote add self \"$(pwd)/.git\"\n+\tgit remote add self \"$(pwd)/.git\" &&\n+\tgit remote add self2 \".\"\n '\n \n test_expect_success 'ls-remote --tags .git' '\n@@ -83,11 +88,17 @@ test_expect_success 'ls-remote --sort=\"-refname\" --tags self' '\n \ttest_cmp expect actual\n '\n \n-test_expect_success 'dies when no remote specified and no default remotes found' '\n+test_expect_success 'dies when no remote specified, multiple remotes found, and no default specified' '\n \ttest_must_fail git ls-remote\n '\n \n-test_expect_success 'use \"origin\" when no remote specified' '\n+test_expect_success 'succeeds when no remote specified but only one found' '\n+\ttest_when_finished git remote add self2 \".\" &&\n+\tgit remote remove self2 &&\n+\tgit ls-remote\n+'\n+\n+test_expect_success 'use \"origin\" when no remote specified and multiple found' '\n \tURL=\"$(pwd)/.git\" &&\n \techo \"From $URL\" >exp_err &&\n \ndiff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh\nindex f280e00eb79..0d6c9869ed3 100755\n--- a/t/t5528-push-default.sh\n+++ b/t/t5528-push-default.sh\n@@ -94,13 +94,74 @@ test_expect_success '\"upstream\" does not push when remotes do not match' '\n \ttest_must_fail git push parent2\n '\n \n-test_expect_success 'push from/to new branch with upstream, matching and simple' '\n+test_expect_success '\"current\" does not push when multiple remotes and none origin' '\n+\tgit checkout main &&\n+\ttest_config push.default current &&\n+\ttest_commit current-multi &&\n+\ttest_must_fail git push\n+'\n+\n+test_expect_success '\"current\" pushes when remote explicitly specified' '\n+\tgit checkout main &&\n+\ttest_config push.default current &&\n+\ttest_commit current-specified &&\n+\tgit push parent1\n+'\n+\n+test_expect_success '\"current\" pushes to origin when no remote specified among multiple' '\n+\tgit checkout main &&\n+\ttest_config remote.origin.url repo1 &&\n+\ttest_config remote.origin.fetch \"+refs/heads/*:refs/remotes/origin/*\" &&\n+\ttest_commit current-origin &&\n+\ttest_push_success current main\n+'\n+\n+test_expect_success '\"current\" pushes to single remote even when not specified' '\n+\tgit checkout main &&\n+\ttest_when_finished git remote add parent1 repo1 &&\n+\tgit remote remove parent1 &&\n+\ttest_commit current-implied &&\n+\ttest_push_success current main repo2\n+'\n+\n+test_expect_success 'push from/to new branch with non-defaulted remote fails with upstream, matching, current and simple ' '\n \tgit checkout -b new-branch &&\n \ttest_push_failure simple &&\n \ttest_push_failure matching &&\n+\ttest_push_failure upstream &&\n+\ttest_push_failure current\n+'\n+\n+test_expect_success 'push from/to new branch fails with upstream and simple ' '\n+\tgit checkout -b new-branch-1 &&\n+\ttest_config branch.new-branch-1.remote parent1 &&\n+\ttest_push_failure simple &&\n \ttest_push_failure upstream\n '\n \n+# The behavior here is surprising but not entirely wrong:\n+#  - the current branch is used to determine the target remote\n+#  - the \"matching\" push default pushes matching branches, *ignoring* the\n+#       current new branch as it does not have upstream tracking\n+#  - the default push succeeds\n+#\n+# A previous test expected this to fail, but for the wrong reasons:\n+# it expected a fail becaause the branch is new and cannot be pushed, but\n+# in fact it was failing because of an ambiguous remote\n+#\n+test_expect_failure 'push from/to new branch fails with matching ' '\n+\tgit checkout -b new-branch-2 &&\n+\ttest_config branch.new-branch-2.remote parent1 &&\n+\ttest_push_failure matching\n+'\n+\n+test_expect_success 'push from/to branch with tracking fails with nothing ' '\n+\tgit checkout -b tracked-branch &&\n+\ttest_config branch.tracked-branch.remote parent1 &&\n+\ttest_config branch.tracked-branch.merge refs/heads/tracked-branch &&\n+\ttest_push_failure nothing\n+'\n+\n test_expect_success '\"matching\" fails if none match' '\n \tgit init --bare empty &&\n \ttest_must_fail git push empty : 2>actual &&\n-- \ngitgitgadget\n\n"},{"id":"454614","messageId":"41c88e51ac6baf3ddaf08f2335015b4fa69fadf6.1651226207.git.gitgitgadget@gmail.com","threadId":"57470","inReplyTo":"pull.1161.v5.git.1651226206.gitgitgadget@gmail.com","subject":"[PATCH v5 3/3] push: new config option \"push.autoSetupRemote\" supports \"simple\" push","fromName":"Tao Klerks via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-04-29T09:56:46Z","receivedAt":"2022-04-29T09:57:08Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"From: Tao Klerks <tao@klerks.biz>\n\nIn some \"simple\" centralized workflows, users expect remote tracking\nbranch names to match local branch names. \"git push\" pushes to the\nremote version/instance of the branch, and \"git pull\" pulls any changes\nto the remote branch (changes made by the same user in another place, or\nby other users).\n\nThis expectation is supported by the push.default default option \"simple\"\nwhich refuses a default push for a mismatching tracking branch name, and\nby the new branch.autosetupmerge option, \"simple\", which only sets up\nremote tracking for same-name remote branches.\n\nWhen a new branch has been created by the user and has not yet been\npushed (and push.default is not set to \"current\"), the user is prompted\nwith a \"The current branch %s has no upstream branch\" error, and\ninstructions on how to push and add tracking.\n\nThis error is helpful in that following the advice once per branch\n\"resolves\" the issue for that branch forever, but inconvenient in that\nfor the \"simple\" centralized workflow, this is always the right thing to\ndo, so it would be better to just do it.\n\nSupport this workflow with a new config setting, push.autoSetupRemote,\nwhich will cause a default push, when there is no remote tracking branch\nconfigured, to push to the same-name on the remote and --set-upstream.\n\nAlso add a hint offering this new option when the \"The current branch %s\nhas no upstream branch\" error is encountered, and add corresponding tests.\n\nSigned-off-by: Tao Klerks <tao@klerks.biz>\n---\n Documentation/config/push.txt | 11 +++++++++\n builtin/push.c                | 44 ++++++++++++++++++++++++++++-------\n t/t5528-push-default.sh       | 14 +++++++++++\n transport.h                   |  1 +\n 4 files changed, 62 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/config/push.txt b/Documentation/config/push.txt\nindex 632033638c4..e32801e6c91 100644\n--- a/Documentation/config/push.txt\n+++ b/Documentation/config/push.txt\n@@ -1,3 +1,14 @@\n+push.autoSetupRemote::\n+\tIf set to \"true\" assume `--set-upstream` on default push when no\n+\tupstream tracking exists for the current branch; this option\n+\ttakes effect with push.default options 'simple', 'upstream',\n+\tand 'current'. It is useful if by default you want new branches\n+\tto be pushed to the default remote (like the behavior of\n+\t'push.default=current') and you also want the upstream tracking\n+\tto be set. Workflows most likely to benefit from this option are\n+\t'simple' central workflows where all branches are expected to\n+\thave the same name on the remote.\n+\n push.default::\n \tDefines the action `git push` should take if no refspec is\n \tgiven (whether from the command-line, config, or elsewhere).\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 447f91f5b47..86b44f8aa71 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -195,16 +195,32 @@ static const char message_detached_head_die[] =\n \t   \"\\n\"\n \t   \"    git push %s HEAD:<name-of-remote-branch>\\n\");\n \n-static const char *get_upstream_ref(struct branch *branch, const char *remote_name)\n+static const char *get_upstream_ref(int flags, struct branch *branch, const char *remote_name)\n {\n-\tif (!branch->merge_nr || !branch->merge || !branch->remote_name)\n+\tif (branch->merge_nr == 0 && (flags & TRANSPORT_PUSH_AUTO_UPSTREAM)) {\n+\t\t/* if missing, assume same; set_upstream will be defined later */\n+\t\treturn branch->refname;\n+\t}\n+\n+\tif (!branch->merge_nr || !branch->merge || !branch->remote_name) {\n+\t\tconst char *advice_autosetup_maybe = \"\";\n+\t\tif (!(flags & TRANSPORT_PUSH_AUTO_UPSTREAM)) {\n+\t\t\tadvice_autosetup_maybe = _(\"\\n\"\n+\t\t\t\t\t   \"To have this happen automatically for \"\n+\t\t\t\t\t   \"branches without a tracking\\n\"\n+\t\t\t\t\t   \"upstream, see 'push.autoSetupRemote' \"\n+\t\t\t\t\t   \"in 'git help config'.\\n\");\n+\t\t}\n \t\tdie(_(\"The current branch %s has no upstream branch.\\n\"\n \t\t    \"To push the current branch and set the remote as upstream, use\\n\"\n \t\t    \"\\n\"\n-\t\t    \"    git push --set-upstream %s %s\\n\"),\n+\t\t    \"    git push --set-upstream %s %s\\n\"\n+\t\t    \"%s\"),\n \t\t    branch->name,\n \t\t    remote_name,\n-\t\t    branch->name);\n+\t\t    branch->name,\n+\t\t    advice_autosetup_maybe);\n+\t}\n \tif (branch->merge_nr != 1)\n \t\tdie(_(\"The current branch %s has multiple upstream branches, \"\n \t\t    \"refusing to push.\"), branch->name);\n@@ -212,7 +228,7 @@ static const char *get_upstream_ref(struct branch *branch, const char *remote_na\n \treturn branch->merge[0]->src;\n }\n \n-static void setup_default_push_refspecs(struct remote *remote)\n+static void setup_default_push_refspecs(int *flags, struct remote *remote)\n {\n \tstruct branch *branch;\n \tconst char *dst;\n@@ -244,7 +260,7 @@ static void setup_default_push_refspecs(struct remote *remote)\n \tcase PUSH_DEFAULT_SIMPLE:\n \t\tif (!same_remote)\n \t\t\tbreak;\n-\t\tif (strcmp(branch->refname, get_upstream_ref(branch, remote->name)))\n+\t\tif (strcmp(branch->refname, get_upstream_ref(*flags, branch, remote->name)))\n \t\t\tdie_push_simple(branch, remote);\n \t\tbreak;\n \n@@ -254,13 +270,21 @@ static void setup_default_push_refspecs(struct remote *remote)\n \t\t\t      \"your current branch '%s', without telling me what to push\\n\"\n \t\t\t      \"to update which remote branch.\"),\n \t\t\t    remote->name, branch->name);\n-\t\tdst = get_upstream_ref(branch, remote->name);\n+\t\tdst = get_upstream_ref(*flags, branch, remote->name);\n \t\tbreak;\n \n \tcase PUSH_DEFAULT_CURRENT:\n \t\tbreak;\n \t}\n \n+\t/*\n+\t * this is a default push - if auto-upstream is enabled and there is\n+\t * no upstream defined, then set it (with options 'simple', 'upstream',\n+\t * and 'current').\n+\t */\n+\tif ((*flags & TRANSPORT_PUSH_AUTO_UPSTREAM) && branch->merge_nr == 0)\n+\t\t*flags |= TRANSPORT_PUSH_SET_UPSTREAM;\n+\n \trefspec_appendf(&rs, \"%s:%s\", branch->refname, dst);\n }\n \n@@ -411,7 +435,7 @@ static int do_push(int flags,\n \t\tif (remote->push.nr) {\n \t\t\tpush_refspec = &remote->push;\n \t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR))\n-\t\t\tsetup_default_push_refspecs(remote);\n+\t\t\tsetup_default_push_refspecs(&flags, remote);\n \t}\n \terrs = 0;\n \turl_nr = push_url_of_remote(remote, &url);\n@@ -482,6 +506,10 @@ static int git_push_config(const char *k, const char *v, void *cb)\n \t\telse\n \t\t\t*flags &= ~TRANSPORT_PUSH_FOLLOW_TAGS;\n \t\treturn 0;\n+\t} else if (!strcmp(k, \"push.autosetupremote\")) {\n+\t\tif (git_config_bool(k, v))\n+\t\t\t*flags |= TRANSPORT_PUSH_AUTO_UPSTREAM;\n+\t\treturn 0;\n \t} else if (!strcmp(k, \"push.gpgsign\")) {\n \t\tconst char *value;\n \t\tif (!git_config_get_value(\"push.gpgsign\", &value)) {\ndiff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh\nindex 0d6c9869ed3..284e20fefda 100755\n--- a/t/t5528-push-default.sh\n+++ b/t/t5528-push-default.sh\n@@ -162,6 +162,20 @@ test_expect_success 'push from/to branch with tracking fails with nothing ' '\n \ttest_push_failure nothing\n '\n \n+test_expect_success 'push from/to new branch succeeds with upstream if push.autoSetupRemote' '\n+\tgit checkout -b new-branch-a &&\n+\ttest_config push.autoSetupRemote true &&\n+\ttest_config branch.new-branch-a.remote parent1 &&\n+\ttest_push_success upstream new-branch-a\n+'\n+\n+test_expect_success 'push from/to new branch succeeds with simple if push.autoSetupRemote' '\n+\tgit checkout -b new-branch-c &&\n+\ttest_config push.autoSetupRemote true &&\n+\ttest_config branch.new-branch-c.remote parent1 &&\n+\ttest_push_success simple new-branch-c\n+'\n+\n test_expect_success '\"matching\" fails if none match' '\n \tgit init --bare empty &&\n \ttest_must_fail git push empty : 2>actual &&\ndiff --git a/transport.h b/transport.h\nindex 12bc08fc339..b5bf7b3e704 100644\n--- a/transport.h\n+++ b/transport.h\n@@ -145,6 +145,7 @@ struct transport {\n #define TRANSPORT_PUSH_OPTIONS\t\t\t(1<<14)\n #define TRANSPORT_RECURSE_SUBMODULES_ONLY\t(1<<15)\n #define TRANSPORT_PUSH_FORCE_IF_INCLUDES\t(1<<16)\n+#define TRANSPORT_PUSH_AUTO_UPSTREAM\t\t(1<<17)\n \n int transport_summary_width(const struct ref *refs);\n \n-- \ngitgitgadget\n"},{"id":"454630","messageId":"xmqqk0b7bu77.fsf@gitster.g","threadId":"57470","inReplyTo":"pull.1161.v5.git.1651226206.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 0/3] New options to support \"simple\" centralized workflow","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-29T18:50:04Z","receivedAt":"2022-04-29T18:50:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Tao Klerks via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> This patchset introduces two new configuration options, intended to be\n> consistent with and complementary to the push.default \"simple\" option. It\n> also improves remote-defaulting in \"default push\" scenarios.\n\nThanks.  I still do not know offhand if the 'simple' thing makes\nsense without thinking it through, but I think that the 'missing\norigin is fine and we can use the unique remote if exists' is a\nreally good idea, especially if some push strategies already do so\nand some don't, which seems to be the case.\n\nWill queue.\n"},{"id":"454682","messageId":"CAPMMpog9msh-KgXybYXUCunbkzBRyfWKjbSG+L0AHRHGyJZk5A@mail.gmail.com","threadId":"57470","inReplyTo":"xmqqk0b7bu77.fsf@gitster.g","subject":"Re: [PATCH v5 0/3] New options to support \"simple\" centralized workflow","fromName":"Tao Klerks","fromEmail":"tao@klerks.biz","sentAt":"2022-04-30T15:48:26Z","receivedAt":"2022-04-30T15:51:48Z","isPatch":true,"sender":{"key":"tao@klerks.biz","avatar":"https://avatars.githubusercontent.com/u/531704?v=4"},"body":"On Fri, Apr 29, 2022 at 8:50 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> I still do not know offhand if the 'simple' thing makes\n> sense without thinking it through,\n\nAt the risk of insisting too much, I'd like to break this down into 3 parts:\n\n1) To what extent does a \"there is one remote, and local and remote\nbranches have the same name unless I explicitly choose to do something\ndifferent\" perspective make sense to a given population of users, and\nhow large is that population of users?\n\n2) For those users, can and should we have a better UX?\n\n3) To what extent does it make sense to call this mode of working\n\"simple\", what are the best UX changes to make, what should any new\noptions be called, and how should discoverability be implemented?\n\n\n1. Audience:\n\nI understand that git was designed as a distributed VCS, and that's a\ncompletely fundamental aspect of its power and success... but the\nreality (or \"my claim\"?) is that the vast majority of users end up\nusing git with a single remote per repo. I don't know how to\ncategorically confirm this - I suspect the github/microsoft, google\nand other sponsor-type folks here will have more access to research on\nthe topic. I don't want to imply that git should do less, but just\nthat the idea of \"multiple remotes\" is alien to almost every git user\nI've ever interacted with. Obviously as I work in a corporate\nenvironment I have a particular perspective... but I find this to be\ntrue of github users also.\n\nWithin the context of such \"single remote per repo\" users, I've spoken\nwith a dozen users of varying git experience levels to try to\nunderstand whether *any* of them intentionally end up with an\n\"upstream tracking branch different from the local branch name\"\nscenario, and what they use it for. I found two users who had ever\ndone this intentionally: One who had done it once, when faced with a\nproject with crazy machine-generated branch names, and another who\ndoes it routinely to have nice short local branch names (very much an\nadvanced user and enthusiast). To the majority it's only ever happened\nby accident, and they didn't even understand what was going on. It was\njust a weird message they got and eventually worked around.\n\nAmusingly, one was an old hand, and still avoided the default \"git\npush\" because he remembered a time when that pushed all branches, and\ndid not realize the default behavior had changed to \"current branch,\nas long as remote tracking name matches\" (aka push.default=simple) 8\nyears ago.\n\nAll these users are aware that there are options that change git's\nbehavior, but the only one who ever took the time to understand and\nconsider changing the defaults was the expert user enthusiast.\n\nI realize all this is anecdotal, I'm a hobbyist and a novice in this\ncommunity, and the deployment I support only has a few hundred users\nat the moment - but surely there must be a way to confirm whether it's\ntrue that git's primary value to millions of users in the world is in\na context where there is a single remote, and branches normally and\nintentionally have exactly the same name locally and on that single\nremote?\n\n2. Current Experience\n\nTaking the user model/workflow above and its statistical significance\nas a given, what's the \"problem\"?\n\nA) a user can accidentally end up in an unexpected state, and not\neasily understand why or what's going on, if they do \"git checkout -b\nmybranch origin/whatever\" - that is, if they choose to branch from a\nknown remote state, rather than creating a local branch for that\nremote branch first. In this unexpected state their \"git pull\" is not\ndoing what they expect (it's bringing in changes from a *different*\nbranch), and their \"git push\" is not working.\n\nFurthermore, the error message for \"git push\" is not actually giving\nthem the right option to solve their problem - it suggests they push\nto the same-name remote branch, but does not propose the \"-u\" option,\nbecause git can't be sure the mismatching branch name isn't\nintentional and \"-u\" would be a kind of destructive change! So they\nwill remain in this weird/unexpected state unless/until they figure\nout for themselves to specify -u or otherwise change the tracking\nupstream. I've seen people delete the local branch, and recreate it,\njust to sort out the remote tracking, because it's just not obvious to\nthem what is going on!\n\nOther flows don't have this issue, eg if they first \"git checkout\nmaster\" (potentially creating a new master branch with tracking from\nremote) and then \"git checkout -b mybranch\".\n\nThat inconsistency is part of the problem - it forces affected users\nto think about remote tracking branches in a way they shouldn't need\nto, in a way that is basically alien to their day-to-day experience\nand expectations of the relationship between local and remote.\n\nB) When a user creates a new branch and they want to push it, they get\nan error that spits out a magical incantation hint, they repeat the\nmagical incantation, and then things are working as expected. This is\na lot better than lacking the hint, of course, but is a completely\nunnecessary interruption in their workflow, *given the assumption that\nremote branches for these users always have the same name as local\nbranches anyway*. The intention of a default \"git push\", in this (in\nmy opinion vast-majority) situation, is simply to make this branch\nwork with its remote equivalent.\n\n3) Naming & changes to git behaviors\n\nOne way to approach the desired flow above would be to do away with or\nignore the concept of upstream tracking branches altogether, and have\na git behavior mode in which \"git pull\", \"git push\", and \"git status\"\nall work automatically and consistently with the same-name remote\nbranch.\n\nI think there are a few problems with that approach:\n - It would not be an on-ramp to slightly different behaviors / modes\nof functioning\n - We'd have to figure out what to do with any then-ignored upstream\ntracking entries for existing branches\n - It would involve a lot of code changes\n - It would be hard to explain in relation to all the rest of the doc/behaviors\n - A user interested in working with just a single\nlocally-differently-named branch (eg because they're working on a\nserver with remote branch names that they can't change and are\ninconveniently long, or have complex prexif/namespacing requirements)\nwould not be able to make use of such a mode - they'd have to switch\nto the \"full/normal\" mode.\n\nTherefore, it makes more sense to figure out the smallest changes in\nbehavior that lead to meeting the expectations/conveniences above, and\ndon't prevent still keeping branches that have a different name to the\nremote, when that is very explicitly desired & specified.\n\nHence the proposals in this patch series. I do truly believe that the\ntwo small changes (new \"don't auto-track differently-named upstream\nbranches\" option, and new \"automatically add remote tracking for\nsame-name branch if missing\" option) are the right thing. What I don't\nknow, is whether they are *named* in the best possible way, and\nwhether the text of the proposed \"hints\" is the best way to help the\n(in my opinion) majority of users who will probably benefit from\nsetting things up this way.\n\n\n> but I think that the 'missing\n> origin is fine and we can use the unique remote if exists' is a\n> really good idea,\n\nCool, that's an easy one, and a separate commit if you want to split\nit off. It's a prerequisite for the \"push.autoSetupRemote\" to work\nwell (in repos that have a single remote not called \"origin\"), but it\ndoes not depend on the other proposed changes.\n\n> especially if some push strategies already do so\n> and some don't, which seems to be the case.\n\nNot exactly - there are other *commands* that do this kind of \"the\nsingle remote\" defaulting, but not other push strategies. The reason I\ncalled out only two default push strategies explicitly, is that they\nare the ones that can work without a remote tracking branch being\nconfigured at all (as long as there is a remote called origin); the\nother strategies depend on a remote being explicitly configured as\npush default, or as branch remote, or as branch push remote.\n\n>\n> Will queue.\n\nGreat thx.\n"}]}