{"thread":{"id":"50874","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","startedAt":"2019-04-04T15:43:40Z","lastAt":"2019-08-20T08:09:45Z","messageCount":19,"participants":["Matthieu Moy","Corentin BOMPARD","Junio C Hamano","Pratyush Yadav"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"373122","messageId":"86sguxvkrd.fsf@univ-lyon1.fr","threadId":"50874","inReplyTo":"d21d42228425408298da9e99b5877ac9@BPMBX2013-01.univ-lyon1.fr","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2019-04-04T15:43:34Z","receivedAt":"2019-04-04T15:43:40Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"BOMPARD CORENTIN p1603631 <corentin.bompard@etu.univ-lyon1.fr> writes:\n\n> Adding the --set-upstream option to git pull/fetch\n\nWe usually write commit messages with imperative tone, hence \"add\", not\n\"adding\".\n\n> +\t\t/*\n> +\t\t * We want to set the current branch config following the \n> +\t\t * ref_map entry which fetches on FETCH_HEAD\n\nfetches _to_? And period at end of sentence.\n\n> +\t\t * In case of \"git pull <remote> --set-upstream\" we\n> +\t\t * \tdon't want to set all branches' config.\n> +\t\t * If there is no local ref which points on FETCH_HEAD\n\nIndentation is weird. If you're just writting sentences, just wrap the\ntext 1 column away from the \"*\", and to make paragraphs, add blank lines\n(containing just \"*\") between paragraphs.\n\n> +\t\t * \twe don't set the config for the current branch\n> +\t\t * \tand warn the user.\n> +\t\t * If there is a fetch of more than one branch for example: \n> +\t\t * \t\"git pull <remote> <branch> <branch> --set-upstream\"\n> +\t\t *\tsetting the current branch's config makes no sense.\n> +\t\t * Where we are in case of \"git pull <remote> <branch>:<branch>\" we\n> +\t\t * \tdon't want to set the config for the local branch\n> +\t\t * \tcan be improved in the future to set local branch's config.\n> +\t\t */\n\nI'm biaised because we talked about this in real-life, but I find the\nexplanation unclear. I'd write stg like\n\n/*\n * We're setting the upstream configuration for the current branch. The\n * relevant upstream is the fetched branch that is meant to be merged with\n * the current one, i.e. the one fetched to FETCH_HEAD.\n * \n * When there are several such branches, consider the request ambiguous and\n * err on the safe side by doing nothing and just emit a warning.\n */\n\nI think the discussion about the various use-case that may lead to\ndifferent cases (0, 1 or >1 branches fetched to FETCH_HEAD) is not\nneeded here, but can be relevant comments in the tests.\n\n> +\t\tfor (rm = ref_map; rm; rm = rm->next) {\n> +\t\t\tfprintf(stderr, \"\\n -%s\", rm->name);\n> +\t\t\tif (rm->peer_ref) {\n> +\t\t\t\tfprintf(stderr, \" -> %s\", rm->peer_ref->name);\n> +\t\t\t} else {\n> +\t\t\t\tif (target) {\n> +\t\t\t\t\tfprintf(stderr, \" -> FETCH_HEAD\\n\");\n> +\t\t\t\t\twarning(_(\"Multiple FETCH_HEAD\"));\n\nIs this a debug statement or a real warning? In the later case, it\nshould be made clearer to the user.\n\n> +\t\t\t\t\ttarget = NULL;\n> +\t\t\t\t\tbreak;\n> +\t\t\t\t} else {\n> +\t\t\t\t\ttarget = rm;\n\nThis is the branch you're fetching from, right? If so, \"target\" is a\nmisleading name. Perhaps source_ref?\n\n> +\t\t\t\t\tfprintf(stderr, \" -> FETCH_HEAD\");\n> +\t\t\t\t}\n> +\t\t\t}\n> +\t\t}\n> +\t\tfprintf(stderr, \"\\n\\n\");\n> +\t\tif (target) {\n> +\t\t\tif (!strcmp(ref_map->name, \"HEAD\") ||\n> +\t\t\t\t\tstarts_with(ref_map->name, \"refs/heads/\")) {\n\nWeird indentation. Perhaps you have a tab-width != 8?\n\nMore importantly, shouldn't ref_map->name be target->name here?\n\n> +\t\t\t\tinstall_branch_config(0, branch->name,\n> +\t\t\t\t\t\t\t transport->remote->name,\n> +\t\t\t\t\t\t\t target->name);\n> +\t\t\t} else if (starts_with(ref_map->name, \"refs/remotes/\")) {\n> +\t\t\t\twarning(_(\"Not setting upstream for a remote remote-tracking branch\"));\n> +\t\t\t} else if (starts_with(ref_map->name, \"refs/tags/\")) {\n> +\t\t\t\twarning(_(\"Tag upstream not set\"));\n> +\t\t\t} else {\n> +\t\t\t\twarning(_(\"Unknown branch type\"));\n> +\t\t\t}\n> +\t\t} else {\n> +\t\t\twarning(_(\"Fetching more than one branch. Current branch's upstream not set\"));\n\nThe warning seems misleading to me: this else branch is executed in many\ncases (described in the comment above), not only when there's more than\none branch, right?\n\n> --- /dev/null\n> +++ b/t/t5553-set-upstream.sh\n> @@ -0,0 +1,141 @@\n> +#!/bin/sh\n> +\n> +test_description='\"git fetch/pull --set-upstream\" basic tests.\n> +\n> +'\n> +. ./test-lib.sh\n> +\n> +\n> +\n> +check_config() {\n> +\t(echo $2; echo $3) >expect.$1\n> +\t(git config branch.$1.remote\n> +\t git config branch.$1.merge) >actual.$1\n> +\ttest_cmp expect.$1 actual.$1\n> +}\n> +\n> +check_config_empty() {\n> +\tgit config branch.$1.remote >remote.$1\n> +\ttest_must_be_empty remote.$1\n> +\tgit config branch.$1.merge >merge.$1\n> +\ttest_must_be_empty merge.$1\n> +}\n\nBroken &&-chain (in both functions, but most importantly in the second,\nwhere the first test_must_be_empty is useless without &&.\n\n> +test_expect_success 'fetch --set-upstream does not set branch other' '\n\nMisleading test name: \"set branch\" -> \"set upstream\"? And here it's not\njust about \"other\" but about all branches.\n\n'fetch --set-upstream does not set upstream w/o branch'\n\n?\n\n> +\tgit checkout master &&\n> +\tgit fetch --set-upstream upstream &&\n> +\tcheck_config_empty master &&\n> +\tcheck_config_empty other\n> +'\n\n> +#test_expect_success 'fetch --set-upstream does not set branch other' '\n> +#\tgit checkout master &&\n> +#\tgit fetch --set-upstream upstream &&\n> +#\tcheck_config master upstream refs/heads/master &&\n> +#\tcheck_config_empty other\n> +#'\n\nAvoid leaving leftovers like this, even in WIP patches, they distract\nthe reader.\n\n> +test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '\n> +\tgit fetch --set-upstream upstream master &&\n> +\tcheck_config master upstream refs/heads/master &&\n> +\tcheck_config_empty other\n> +'\n> +\n> +\n\nStyle: you sometimes leave 2 blank lines, sometimes 1 between tests. Try\nto be consistent.\n\n> +test_expect_success 'pull --set-upstream upstream other sets branch other' '\n\nTest title and content say the opposite of each other.\n\n> +\tgit pull --set-upstream upstream other &&\n> +\tcheck_config master upstream refs/heads/other &&\n> +\tcheck_config_empty other\n> +'\n\n> +test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with the bad url' '\n> +\ttest_must_fail git pull --set-upstream http://nosuchdomain.example.com\n> +'\n\nYou should check that it doesn't touch the config. That it fails is not\na surprise regardless of the correctness of your code, but the thing to\ncheck is that it does not touch the config before failing.\n\n> +test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '\n\nHere also, test title and content say different things. Probably you\nneed to reset the config and use check_config_empty.\n\n> +\tgit pull --set-upstream upstream master three &&\n> +\tcheck_config master upstream HEAD &&\n> +\tcheck_config_empty three\n> +'\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"373488","messageId":"20190409125205.13754-1-corentin.bompard@etu.univ-lyon1.fr","threadId":"50874","inReplyTo":"86sguxvkrd.fsf@univ-lyon1.fr","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Corentin BOMPARD","fromEmail":"corentin.bompard@etu.univ-lyon1.fr","sentAt":"2019-04-09T12:52:05Z","receivedAt":"2019-04-09T12:53:03Z","isPatch":true,"sender":{"key":"corentin.bompard@etu.univ-lyon1.fr","avatar":"https://avatars.githubusercontent.com/u/23448477?v=4"},"body":"> BOMPARD CORENTIN p1603631 <corentin.bompard@etu.univ-lyon1.fr> writes:\n>\n>> Adding the --set-upstream option to git pull/fetch\n>\n> We usually write commit messages with imperative tone, hence \"add\", not\n> \"adding\".\n\nFixed.\n\n>> +\t\t/*\n>> +\t\t * We want to set the current branch config following the \n>> +\t\t * ref_map entry which fetches on FETCH_HEAD\n>\n> fetches _to_? And period at end of sentence.\n\nFixed.\n\n>> +\t\t * In case of \"git pull <remote> --set-upstream\" we\n>> +\t\t * \tdon't want to set all branches' config.\n>> +\t\t * If there is no local ref which points on FETCH_HEAD\n>\n> Indentation is weird. If you're just writting sentences, just wrap the\n> text 1 column away from the \"*\", and to make paragraphs, add blank lines\n> (containing just \"*\") between paragraphs.\n\nWe fixed indentation.\n\n>> +\t\t * \twe don't set the config for the current branch\n>> +\t\t * \tand warn the user.\n>> +\t\t * If there is a fetch of more than one branch for example: \n>> +\t\t * \t\"git pull <remote> <branch> <branch> --set-upstream\"\n>> +\t\t *\tsetting the current branch's config makes no sense.\n>> +\t\t * Where we are in case of \"git pull <remote> <branch>:<branch>\" we\n>> +\t\t * \tdon't want to set the config for the local branch\n>> +\t\t * \tcan be improved in the future to set local branch's config.\n>> +\t\t */\n>\n> I'm biaised because we talked about this in real-life, but I find the\n> explanation unclear. I'd write stg like\n> /*\n> * We're setting the upstream configuration for the current branch. The\n> * relevant upstream is the fetched branch that is meant to be merged with\n> * the current one, i.e. the one fetched to FETCH_HEAD.\n> * \n> * When there are several such branches, consider the request ambiguous and\n> * err on the safe side by doing nothing and just emit a warning.\n> */\n>\n> I think the discussion about the various use-case that may lead to\n> different cases (0, 1 or >1 branches fetched to FETCH_HEAD) is not\n> needed here, but can be relevant comments in the tests.\n\nWe took your message and we will add the use-case in test file.\n\n>> +\t\tfor (rm = ref_map; rm; rm = rm->next) {\n>> +\t\t\tfprintf(stderr, \"\\n -%s\", rm->name);\n>> +\t\t\tif (rm->peer_ref) {\n>> +\t\t\t\tfprintf(stderr, \" -> %s\", rm->peer_ref->name);\n>> +\t\t\t} else {\n>> +\t\t\t\tif (target) {\n>> +\t\t\t\t\tfprintf(stderr, \" -> FETCH_HEAD\\n\");\n>> +\t\t\t\t\twarning(_(\"Multiple FETCH_HEAD\"));\n>\n> Is this a debug statement or a real warning? In the later case, it\n> should be made clearer to the user.\n\nThis statement is called when the user call set-upstream with more\nthan one branch like \"git pull <remote> <branch> <branch> --set-upstream\"\nWe replaced the warning message by the following message\n\"Multiple branch detected, incompatible with --set-upstream\".\n\n>> +\t\t\t\t\ttarget = NULL;\n>> +\t\t\t\t\tbreak;\n>> +\t\t\t\t} else {\n>> +\t\t\t\t\ttarget = rm;\n>\n> This is the branch you're fetching from, right? If so, \"target\" is a\n> misleading name. Perhaps source_ref?\n\nWe replaced target with source_ref because it's clearer.\n\n>> +\t\t\t\t\tfprintf(stderr, \" -> FETCH_HEAD\");\n>> +\t\t\t\t}\n>> +\t\t\t}\n>> +\t\t}\n>> +\t\tfprintf(stderr, \"\\n\\n\");\n>> +\t\tif (target) {\n>> +\t\t\tif (!strcmp(ref_map->name, \"HEAD\") ||\n>> +\t\t\t\t\tstarts_with(ref_map->name, \"refs/heads/\")) {\n>\n> Weird indentation. Perhaps you have a tab-width != 8?\n\nTaken in consideration.\n\n> More importantly, shouldn't ref_map->name be target->name here?\n\nFixed.\n\n>> +\t\t\t\tinstall_branch_config(0, branch->name,\n>> +\t\t\t\t\t\t\t transport->remote->name,\n>> +\t\t\t\t\t\t\t target->name);\n>> +\t\t\t} else if (starts_with(ref_map->name, \"refs/remotes/\")) {\n>> +\t\t\t\twarning(_(\"Not setting upstream for a remote remote-tracking branch\"));\n>> +\t\t\t} else if (starts_with(ref_map->name, \"refs/tags/\")) {\n>> +\t\t\t\twarning(_(\"Tag upstream not set\"));\n>> +\t\t\t} else {\n>> +\t\t\t\twarning(_(\"Unknown branch type\"));\n>> +\t\t\t}\n>> +\t\t} else {\n>> +\t\t\twarning(_(\"Fetching more than one branch. Current branch's upstream not set\"));\n>\n> The warning seems misleading to me: this else branch is executed in many\n> cases (described in the comment above), not only when there's more than\n> one branch, right?\n\nThis else clause is executed if there is more than one branch which fetches to FETCH_HEAD\nor if the user use the syntax git pull --set-upstream <remote> <branch>:<branch> or if there is no\nbranch which fetches to FETCH_HEAD.\n\n>> --- /dev/null\n>> +++ b/t/t5553-set-upstream.sh\n>> @@ -0,0 +1,141 @@\n>> +#!/bin/sh\n>> +\n>> +test_description='\"git fetch/pull --set-upstream\" basic tests.\n>> +\n>> +'\n>> +. ./test-lib.sh\n>> +\n>> +\n>> +\n>> +check_config() {\n>> +\t(echo $2; echo $3) >expect.$1\n>> +\t(git config branch.$1.remote\n>> +\t git config branch.$1.merge) >actual.$1\n>> +\ttest_cmp expect.$1 actual.$1\n>> +}\n>> +\n>> +check_config_empty() {\n>> +\tgit config branch.$1.remote >remote.$1\n>> +\ttest_must_be_empty remote.$1\n>> +\tgit config branch.$1.merge >merge.$1\n>> +\ttest_must_be_empty merge.$1\n>> +}\n>\n> Broken &&-chain (in both functions, but most importantly in the second,\n> where the first test_must_be_empty is useless without &&.\n\nWe restored the &&-chain in the functions. \n\n>> +test_expect_success 'fetch --set-upstream does not set branch other' '\n>\n> Misleading test name: \"set branch\" -> \"set upstream\"? And here it's not\n> just about \"other\" but about all branches.\n>\n> 'fetch --set-upstream does not set upstream w/o branch'\n> ?\n\nWe edited the test's title\n\n>> +\tgit checkout master &&\n>> +\tgit fetch --set-upstream upstream &&\n>> +\tcheck_config_empty master &&\n>> +\tcheck_config_empty other\n>> +'\n>\n>> +#test_expect_success 'fetch --set-upstream does not set branch other' '\n>> +#\tgit checkout master &&\n>> +#\tgit fetch --set-upstream upstream &&\n>> +#\tcheck_config master upstream refs/heads/master &&\n>> +#\tcheck_config_empty other\n>> +#'\n>\n> Avoid leaving leftovers like this, even in WIP patches, they distract\n> the reader.\n\nWe removed the test in comment because it no longer makes sense.\n\n>> +test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '\n>> +\tgit fetch --set-upstream upstream master &&\n>> +\tcheck_config master upstream refs/heads/master &&\n>> +\tcheck_config_empty other\n>> +'\n>> +\n>> +\n>\n> Style: you sometimes leave 2 blank lines, sometimes 1 between tests. Try\n> to be consistent.\n\nWe removed to have only 1 blank line between tests.\n\n>> +test_expect_success 'pull --set-upstream upstream other sets branch other' '\n>\n> Test title and content say the opposite of each other.\n>\n>> +\tgit pull --set-upstream upstream other &&\n>> +\tcheck_config master upstream refs/heads/other &&\n>> +\tcheck_config_empty other\n>> +'\n\nWe changed the title of this test.\n\n>> +test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with the bad url' '\n>> +\ttest_must_fail git pull --set-upstream http://nosuchdomain.example.com\n>> +'\n>\n> You should check that it doesn't touch the config. That it fails is not\n> a surprise regardless of the correctness of your code, but the thing to\n> check is that it does not touch the config before failing.\n\nWe added some config check and improved \nthe test 'fetch ---set-upstream http://nosuchdomain.example.com fails with the bad url'.\n\n>> +test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '\n>\n> Here also, test title and content say different things. Probably you\n> need to reset the config and use check_config_empty.\n\nWe created a new function clear_config which clears the branches config and use check_config_empty to \ncheck if the config is empty for all branches.\n\nThe fixed patch will follow.\n"},{"id":"374053","messageId":"20190417160138.6114-1-corentin.bompard@etu.univ-lyon1.fr","threadId":"50874","inReplyTo":"20190409125205.13754-1-corentin.bompard@etu.univ-lyon1.fr","subject":"[PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Corentin BOMPARD","fromEmail":"corentin.bompard@etu.univ-lyon1.fr","sentAt":"2019-04-17T16:01:38Z","receivedAt":"2019-04-17T16:02:03Z","isPatch":true,"sender":{"key":"corentin.bompard@etu.univ-lyon1.fr","avatar":"https://avatars.githubusercontent.com/u/23448477?v=4"},"body":"Add the --set-upstream option to git pull/fetch\nwhich lets the user set the upstream configuration\nfor the current branch.\n\nFor example a typical use-case like\n    git clone http://example.com/my-public-fork\n    git remote add main http://example.com/project-main-repo\n    git pull main master --set-upstream\nor, instead of the last line\n    git fetch main master --set-upstream\n    git merge # or git rebase\n\nThis foncionality works like git push --set-upstream.\n\nSigned-off-by: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\nSigned-off-by: Nathan BERBEZIER <nathan.berbezier@etu.univ-lyon1.fr>\nSigned-off-by: Pablo CHABANNE <pablo.chabanne@etu.univ-lyon1.fr>\nSigned-off-by: Matthieu MOY <matthieu.moy@univ-lyon1.fr>\n---\n Sorry for being so long.\n\n Documentation/fetch-options.txt |   5 ++\n builtin/fetch.c                 |  55 ++++++++++++-\n builtin/pull.c                  |   6 ++\n t/t5553-set-upstream.sh         | 142 ++++++++++++++++++++++++++++++++\n 4 files changed, 207 insertions(+), 1 deletion(-)\n create mode 100644 t/t5553-set-upstream.sh\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex fa0a3151b..4d2d55643 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -165,6 +165,11 @@ ifndef::git-pull[]\n \tDisable recursive fetching of submodules (this has the same effect as\n \tusing the `--recurse-submodules=no` option).\n \n+--set-upstream::\n+\tIf the new URL remote is correct, pull and add upstream (tracking) \n+\treference, used by argument-less linkgit:git-push[1] and other commands.\n+\tFor more information, see `branch.<name>.merge` in linkgit:git-config[1].\n+\n --submodule-prefix=<path>::\n \tPrepend <path> to paths printed in informative messages\n \tsuch as \"Fetching submodule foo\".  This option is used\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex b620fd54b..b43a4e0a2 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -23,6 +23,7 @@\n #include \"packfile.h\"\n #include \"list-objects-filter-options.h\"\n #include \"commit-reach.h\"\n+#include \"branch.h\"\n \n static const char * const builtin_fetch_usage[] = {\n \tN_(\"git fetch [<options>] [<repository> [<refspec>...]]\"),\n@@ -46,7 +47,7 @@ static int fetch_prune_tags_config = -1; /* unspecified */\n static int prune_tags = -1; /* unspecified */\n #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n \n-static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n+static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative, set_upstream;\n static int progress = -1;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n static int max_children = 1;\n@@ -113,6 +114,8 @@ static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n \tOPT_BOOL(0, \"all\", &all,\n \t\t N_(\"fetch from all remotes\")),\n+\tOPT_BOOL(0, \"set-upstream\", &set_upstream,\n+\t\t N_(\"set upstream for git pull/fetch\")),\n \tOPT_BOOL('a', \"append\", &append,\n \t\t N_(\"append to .git/FETCH_HEAD instead of overwriting\")),\n \tOPT_STRING(0, \"upload-pack\", &upload_pack, N_(\"path\"),\n@@ -1317,6 +1320,56 @@ static int do_fetch(struct transport *transport,\n \t\tretcode = 1;\n \t\tgoto cleanup;\n \t}\n+\n+\t/* TODO: remove debug trace */\n+\tif (set_upstream) {\n+\t\tstruct branch *branch = branch_get(\"HEAD\");\n+\t\tstruct ref *rm;\n+\t\tstruct ref *source_ref = NULL;\n+\t\t/*\n+\t\t * We're setting the upstream configuration for the current branch. The\n+\t\t * relevent upstream is the fetched branch that is meant to be merged with\n+\t\t * the current one, i.e. the one fetched to FETCH_HEAD.\n+\t\t *\n+\t\t * When there are several such branches, consider the request ambiguous and\n+\t\t * err on the safe side by doing nothing and just emit a waring.\n+\t\t */\n+\t\tfor (rm = ref_map; rm; rm = rm->next) {\n+\t\t\tfprintf(stderr, \"\\n -%s\", rm->name);\n+\t\t\tif (rm->peer_ref) {\n+\t\t\t\tfprintf(stderr, \" -> %s\", rm->peer_ref->name);\n+\t\t\t} else {\n+\t\t\t\tif (source_ref) {\n+\t\t\t\t\tfprintf(stderr, \" -> FETCH_HEAD\\n\");\n+\t\t\t\t\twarning(_(\"Multiple branch detected, incompatible with set-upstream\"));\n+\t\t\t\t\tsource_ref = NULL;\n+\t\t\t\t\tgoto skip;\n+\t\t\t\t} else {\n+\t\t\t\t\tsource_ref = rm;\n+\t\t\t\t\tfprintf(stderr, \" -> FETCH_HEAD\");\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tfprintf(stderr, \"\\n\\n\");\n+\t\tif (source_ref) {\n+\t\t\tif (!strcmp(source_ref->name, \"HEAD\") ||\n+\t\t\t\tstarts_with(source_ref->name, \"refs/heads/\")) {\n+\t\t\t\tinstall_branch_config(0, branch->name,\n+\t\t\t\t\t\t\t transport->remote->name,\n+\t\t\t\t\t\t\t source_ref->name);\n+\t\t\t} else if (starts_with(source_ref->name, \"refs/remotes/\")) {\n+\t\t\t\twarning(_(\"Not setting upstream for a remote remote-tracking branch\"));\n+\t\t\t} else if (starts_with(source_ref->name, \"refs/tags/\")) {\n+\t\t\t\twarning(_(\"Tag upstream not set\"));\n+\t\t\t} else {\n+\t\t\t\twarning(_(\"Unknown branch type\"));\n+\t\t\t}\n+\t\t} else {\n+\t\t\twarning(_(\"No source branch found. \\n You need to specify excatly \"\n+\t\t\t\t\t\t\"one branch with the set-upstream option.\"));\n+\t\t}\n+\t}\n+ skip:\n \tfree_refs(ref_map);\n \n \t/* if neither --no-tags nor --tags was specified, do automated tag\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 701d1473d..06d7cddce 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -122,6 +122,7 @@ static char *opt_update_shallow;\n static char *opt_refmap;\n static char *opt_ipv4;\n static char *opt_ipv6;\n+static char *set_upstream;\n \n static struct option pull_options[] = {\n \t/* Shared options */\n@@ -233,6 +234,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU('6',  \"ipv6\", &opt_ipv6, NULL,\n \t\tN_(\"use IPv6 addresses only\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"set-upstream\", &set_upstream, NULL,\n+\t\tN_(\"set upstream for git pull/fetch\"),\n+\t\tPARSE_OPT_NOARG),\n \n \tOPT_END()\n };\n@@ -541,6 +545,8 @@ static int run_fetch(const char *repo, const char **refspecs)\n \t\targv_array_push(&args, opt_ipv4);\n \tif (opt_ipv6)\n \t\targv_array_push(&args, opt_ipv6);\n+\tif (set_upstream)\n+\t\targv_array_push(&args, set_upstream);\n \n \tif (repo) {\n \t\targv_array_push(&args, repo);\ndiff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh\nnew file mode 100644\nindex 000000000..6126bb188\n--- /dev/null\n+++ b/t/t5553-set-upstream.sh\n@@ -0,0 +1,142 @@\n+#!/bin/sh\n+\n+test_description='\"git fetch/pull --set-upstream\" basic tests.\n+\n+'\n+. ./test-lib.sh\n+\n+check_config() {\n+\t(echo $2; echo $3) >expect.$1 &&\n+\t(git config branch.$1.remote\n+\t git config branch.$1.merge) >actual.$1 &&\n+\ttest_cmp expect.$1 actual.$1\n+}\n+\n+check_config_empty() {\n+\ttest_must_fail git config branch.$1.remote &&\n+\ttest_must_fail git config branch.$1.merge\n+}\n+check_config_empty1() {\n+\tgit config branch.$1.remote >remote.$1\n+\ttest_must_be_empty remote.$1 &&\n+\tgit config branch.$1.merge >merge.$1\n+\ttest_must_be_empty merge.$1\n+}\n+\n+clear_config() {\n+\tgit config --unset branch.$1.remote\n+\tgit config --unset branch.$1.merge\n+}\n+\n+ensure_fresh_upstream() {\n+\trm -rf parent && git init --bare parent\n+}\n+\n+test_expect_success 'setup bare parent fetch' '\n+\tensure_fresh_upstream &&\n+\tgit remote add upstream parent &&\n+\tgit remote add up parent\n+'\n+\n+test_expect_success 'setup commit on master and other fetch' '\n+\ttest_commit one &&\n+\tgit push upstream master &&\n+\tgit checkout -b other &&\n+\ttest_commit two &&\n+\tgit push upstream other\n+'\n+\n+#tests for fetch --set-upstream\n+\n+test_expect_success 'fetch --set-upstream does not set upstream w/o branch' '\n+\tgit checkout master &&\n+\tgit fetch --set-upstream upstream &&\n+\tcheck_config_empty master &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '\n+\tgit fetch --set-upstream upstream master &&\n+\tcheck_config master upstream refs/heads/master &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'fetch --set-upstream upstream other sets branch other' '\n+\tgit fetch --set-upstream upstream other &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'fetch --set-upstream master:other does not set the branch other2' '\n+\tgit fetch --set-upstream upstream master:other2 &&\n+\tcheck_config_empty other2\n+'\n+\n+test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with the bad url' '\n+\ttest_must_fail git fetch --set-upstream http://nosuchdomain.example.com &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_empty other &&\n+\tcheck_config_empty other2\n+'\n+\n+#tests for pull --set-upstream\n+\n+test_expect_success 'setup bare parent pull' '\n+\tgit remote rm upstream &&\n+\tensure_fresh_upstream &&\n+\tgit remote add upstream parent\n+'\n+\n+test_expect_success 'setup commit on master and other pull' '\n+\ttest_commit three &&\n+\tgit push --tags upstream master &&\n+\ttest_commit four &&\n+\tgit push upstream other\n+'\n+\n+test_expect_success 'pull --set-upstream upstream master sets branch master but not other' '\n+\tgit pull --set-upstream upstream master &&\n+\tcheck_config master upstream refs/heads/master &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'pull --set-upstream master:other2 does not set the branch other2' '\n+\tgit pull --set-upstream upstream master:other2 &&\n+\tcheck_config_empty other2\n+'\n+\n+test_expect_success 'pull --set-upstream upstream other sets branch master' '\n+\tgit pull --set-upstream upstream other &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'pull --set-upstream upstream tag does not set the tag' '\n+\tgit pull --tags --set-upstream upstream three &&\n+\tcheck_config_empty three\n+'\n+\n+test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with the bad url' '\n+\ttest_must_fail git pull --set-upstream http://nosuchdomain.example.com &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_empty other &&\n+\tcheck_config_empty other2 &&\n+\tcheck_config_empty three\n+'\n+\n+test_expect_success 'pull --set-upstream upstream HEAD sets branch HEAD' '\n+\tgit pull --set-upstream upstream HEAD &&\n+\tcheck_config master upstream HEAD &&\n+\tgit checkout other &&\n+\tgit pull --set-upstream upstream HEAD &&\n+\tcheck_config other upstream HEAD\n+'\n+\n+test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '\n+\tclear_config master &&\n+\tgit pull --set-upstream upstream master three &&\n+\tcheck_config_empty master &&\n+\tcheck_config_empty three\n+'\n+\n+test_done\n-- \n2.21.0-rc0\n\n"},{"id":"374081","messageId":"xmqq7ebshz7v.fsf@gitster-ct.c.googlers.com","threadId":"50874","inReplyTo":"20190417160138.6114-1-corentin.bompard@etu.univ-lyon1.fr","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-04-18T01:35:48Z","receivedAt":"2019-04-18T01:35:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr> writes:\n\n> Add the --set-upstream option to git pull/fetch\n> which lets the user set the upstream configuration\n> for the current branch.\n\nI think it is a good idea to mention what you exactly mean by \"the\nupstream configuration\" here.  \n\nDo you mean the \"branch.<current-branch-name>.merge\" configuration\nvariable?\n\n> For example a typical use-case like\n>     git clone http://example.com/my-public-fork\n>     git remote add main http://example.com/project-main-repo\n>     git pull main master --set-upstream\n> or, instead of the last line\n>     git fetch main master --set-upstream\n>     git merge # or git rebase\n\nA bit more blank lines around the block of sample commands would\nmake the result easier to read.\n\n> This foncionality works like git push --set-upstream.\n\nfunctionality?\n\n>\n> Signed-off-by: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\n> Signed-off-by: Nathan BERBEZIER <nathan.berbezier@etu.univ-lyon1.fr>\n> Signed-off-by: Pablo CHABANNE <pablo.chabanne@etu.univ-lyon1.fr>\n> Signed-off-by: Matthieu MOY <matthieu.moy@univ-lyon1.fr>\n> ---\n>  Sorry for being so long.\n>\n>  Documentation/fetch-options.txt |   5 ++\n>  builtin/fetch.c                 |  55 ++++++++++++-\n>  builtin/pull.c                  |   6 ++\n>  t/t5553-set-upstream.sh         | 142 ++++++++++++++++++++++++++++++++\n>  4 files changed, 207 insertions(+), 1 deletion(-)\n>  create mode 100644 t/t5553-set-upstream.sh\n>\n> diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\n> index fa0a3151b..4d2d55643 100644\n> --- a/Documentation/fetch-options.txt\n> +++ b/Documentation/fetch-options.txt\n> @@ -165,6 +165,11 @@ ifndef::git-pull[]\n>  \tDisable recursive fetching of submodules (this has the same effect as\n>  \tusing the `--recurse-submodules=no` option).\n>  \n> +--set-upstream::\n> +\tIf the new URL remote is correct, pull and add upstream (tracking) \n> +\treference, used by argument-less linkgit:git-push[1] and other commands.\n\ngit-push and other commands?  The way I read the motivating use case\nexample we saw in the proposed commit log message, i.e.\n\n     git clone http://example.com/my-public-fork\n     git remote add main http://example.com/project-main-repo\n     git pull --set-upstream main master [*1*]\n\nwas that your initial cloning made \"fetch/pull\" by default interact\nwith your public fork by mistake, and you are correcting it with the\nnew \"--set-upstream\" option so that future \"fetch/pull\" will instead\ngo to the true upstream, while directing your \"push\" traffic to still\ngo to your public fork.  If that is the case, then shouldn't this\nparagraph in the doc talking about affecting future \"git-fetch and\nother commands\", or \"git fetch and pull\" (which may be better)?\n\n\tSide note *1*: by the way, don't write --dashed-options\n\tafter positional arguments; the parse-options parser may\n\tallow such a sloppy command line but it makes the examples\n\tinconsistent when done in the documentation and log\n\tmessages.\n\n> @@ -1317,6 +1320,56 @@ static int do_fetch(struct transport *transport,\n>  \t\tretcode = 1;\n>  \t\tgoto cleanup;\n>  \t}\n> +\n> +\t/* TODO: remove debug trace */\n\nPerhaps do so before sending it out for the review?\n\n> +\tif (set_upstream) {\n> +\t\tstruct branch *branch = branch_get(\"HEAD\");\n> +\t\tstruct ref *rm;\n> +\t\tstruct ref *source_ref = NULL;\n\nHave a blank line here, after the decls that appear before the first\nstatement in a block.\n\n> +\t\t/*\n> +\t\t * We're setting the upstream configuration for the current branch. The\n> +\t\t * relevent upstream is the fetched branch that is meant to be merged with\n> +\t\t * the current one, i.e. the one fetched to FETCH_HEAD.\n> +\t\t *\n> +\t\t * When there are several such branches, consider the request ambiguous and\n> +\t\t * err on the safe side by doing nothing and just emit a waring.\n> +\t\t */\n> +\t\tfor (rm = ref_map; rm; rm = rm->next) {\n> +\t\t\tfprintf(stderr, \"\\n -%s\", rm->name);\n> +\t\t\tif (rm->peer_ref) {\n> +\t\t\t\tfprintf(stderr, \" -> %s\", rm->peer_ref->name);\n> +\t\t\t} else {\n> +\t\t\t\tif (source_ref) {\n> +\t\t\t\t\tfprintf(stderr, \" -> FETCH_HEAD\\n\");\n> +\t\t\t\t\twarning(_(\"Multiple branch detected, incompatible with set-upstream\"));\n\ndowncase \"M\" for consistency.  I won't repeat for other new messages\nin the patch.\n\nShouldn't this be diagnosed as an error and stop the \"fetch\" or\n\"pull\", though?\n\n> diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh\n> new file mode 100644\n\nMake your test scripts executable.\n\n> index 000000000..6126bb188\n> --- /dev/null\n> +++ b/t/t5553-set-upstream.sh\n> @@ -0,0 +1,142 @@\n> +#!/bin/sh\n> +\n> +test_description='\"git fetch/pull --set-upstream\" basic tests.\n> +\n> +'\n> +. ./test-lib.sh\n> +\n> +check_config() {\n\nSP before () is missing here (I won't repeat).\n\n> +\t(echo $2; echo $3) >expect.$1 &&\n\nMake sure to dq quote $variable_references UNLESS you mean you\nintend to pass a string with $IFS in it and want the shell to split\nthe interpolation into individual tokens (I won't repeat).\n\nEspecially, quote the filename that is a target for redirection to\nwork-around a (mis)feature in bash (I won't repeat).\n\nYou do not need subshell for the above.  Perhaps\n\n\tprintf \"%s\\n\" \"$2\" \"$3\" >\"expect.$1\" &&\n\n> +\t(git config branch.$1.remote\n> +\t git config branch.$1.merge) >actual.$1 &&\n\nYou do not need a subshell for this, either\n\n\t{\n\t\tgit config \"branch.$1.remote\" && git config \"branch.$1.merge\"\n\t} >\"actual.$1\"\n\n> +\ttest_cmp expect.$1 actual.$1\n\n> +check_config_empty() {\n\ns/empty/missing/ would make the distinction even clear.\n\n> +\ttest_must_fail git config branch.$1.remote &&\n> +\ttest_must_fail git config branch.$1.merge\n\nDo we document that \"git config\" errors out with a more specific\nsignal to say \"the reason why the command has failed is because the\nkey was not found\", by the way?  I think we do, and in that case the\ntest should expect that specific exit code.\n\n> +}\n> +check_config_empty1() {\n\nA blank line before a new shell function.\n\nThis one is about an empty string, so it can be named check_config_empty\nonce the misnamed one above that checked for a missing definition gets\nrenamed away.\n\n> +\tgit config branch.$1.remote >remote.$1\n\nHere is a break &&-chain; intended?\n\n> +\ttest_must_be_empty remote.$1 &&\n> +\tgit config branch.$1.merge >merge.$1\n\nLikewise.\n\n> +\ttest_must_be_empty merge.$1\n> +}\n\nIf this wanted to say \"It is OK for the variable to be missing, and\nit also is OK for the variable to have an empty string as its value;\nall other cases are unacceptable\", then have another layer of helper\nperhaps like\n\n        variable_missing_or_empty () (\n                value=$(git config \"$1\")\n                case $? in\n                0)\t# exists\n                        test -z \"$value\" ;;\n                1)\t# missing\n                        true ;;\n                *)\tfalse ;;\n                esac\n        )\n\nand then you can say\n\n\tcheck_config_missing_or_empty () {\n\t\tvariable_missing_or_empty \"remote.$1\" &&\n\t\tvariable_missing_or_empty \"merge.$1\"\n\t}\n\nIn any case, you do not seem to use empty1 variant in the rest of\nthe patch.  Has this been proofread before getting sent?\n\n\t... Ahh, this is WIP/RFC.  So a later iteration may start\n\tusing it.  OK then.\n\n"},{"id":"374096","messageId":"86h8av7ian.fsf@univ-lyon1.fr","threadId":"50874","inReplyTo":"36559daca9d84f7a91933add734020cd@BPMBX2013-01.univ-lyon1.fr","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2019-04-18T09:51:28Z","receivedAt":"2019-04-18T09:51:34Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> --- a/Documentation/fetch-options.txt\n>> +++ b/Documentation/fetch-options.txt\n>> @@ -165,6 +165,11 @@ ifndef::git-pull[]\n>>  \tDisable recursive fetching of submodules (this has the same effect as\n>>  \tusing the `--recurse-submodules=no` option).\n>>  \n>> +--set-upstream::\n>> +\tIf the new URL remote is correct, pull and add upstream (tracking) \n>> +\treference, used by argument-less linkgit:git-push[1] and other commands.\n>\n> git-push and other commands?\n\nI think this is taken from the documentation of --set-upstream for push,\nwhich says:\n\n-u::\n--set-upstream::\n\tFor every branch that is up to date or successfully pushed, add\n\tupstream (tracking) reference, used by argument-less\n\tlinkgit:git-pull[1] and other commands. For more information,\n\tsee `branch.<name>.merge` in linkgit:git-config[1].\n\nProbably the reasoning was to make a symmetry between \"git push\n--set-upstream\", which mentions \"pull\" in the doc, and the new \"git pull\n--set-upstream\". However, I do not think there should be such symmetry:\n\nActually, the way I see it, the notion of uptream (i.e.\nbranch.<branch>.remote and branch.<branch>.merge) is primarily about\n\"pull\" and friends, and \"push\" happens to use it also by default. But\nwhen branch.<branch>.pushRemote is set, upstream is really about\npulling, and pushing goes to the pushRemote.\n\n>> +\t/* TODO: remove debug trace */\n>\n> Perhaps do so before sending it out for the review?\n\nYes. This is WIP for now, but it's time to get closer to a real patch,\nand these debug statements are counter-productive for that.\n\n>> +\ttest_must_be_empty merge.$1\n>> +}\n>\n> If this wanted to say \"It is OK for the variable to be missing, and\n> it also is OK for the variable to have an empty string as its value;\n> all other cases are unacceptable\",\n\nActually, I don't think the \"present but empty\" case makes sense here,\nso just test_must_fail git config \"$1\" should do the trick.\n\nI agree with all other remarks.\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"374097","messageId":"86bm137i2d.fsf@univ-lyon1.fr","threadId":"50874","inReplyTo":"3d2ba75520b74c2e9e8251c41d6632ba@BPMBX2013-01.univ-lyon1.fr","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2019-04-18T09:56:26Z","receivedAt":"2019-04-18T09:56:30Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"BOMPARD CORENTIN p1603631 <corentin.bompard@etu.univ-lyon1.fr> writes:\n\n> +\t\t\twarning(_(\"No source branch found. \\n You need to specify excatly \"\n> +\t\t\t\t\t\t\"one branch with the set-upstream option.\"));\n\ns/excatly/exactly/\n\nAlso, this \" \\n \" is weird, the trailing whitespace is useless, and the\nleading one on the next line is weird. You can use the \\n to split the\nstring in the source without risking space issues:\n\n\t\t\twarning(_(\"no source branch found.\\n\"\n                                  \"You need to specify excatly one branch with the set-upstream option.\"));\n\n?\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"374141","messageId":"20190419160046.5283-1-corentin.bompard@etu.univ-lyon1.fr","threadId":"50874","inReplyTo":"xmqq7ebshz7v.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Corentin BOMPARD","fromEmail":"corentin.bompard@etu.univ-lyon1.fr","sentAt":"2019-04-19T16:00:46Z","receivedAt":"2019-04-19T18:17:09Z","isPatch":true,"sender":{"key":"corentin.bompard@etu.univ-lyon1.fr","avatar":"https://avatars.githubusercontent.com/u/23448477?v=4"},"body":">Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr> writes:\n>\n>> Add the --set-upstream option to git pull/fetch\n>> which lets the user set the upstream configuration\n>> for the current branch.\n>\n> I think it is a good idea to mention what you exactly mean by \"the\n> upstream configuration\" here.  \n>\n> Do you mean the \"branch.<current-branch-name>.merge\" configuration\n> variable?\n\nThe upstream configuration means the branch.<current-branch-name>.merge \nand branch.<current-branch-name>.remote\n\n>> +             /*\n>> +              * We're setting the upstream configuration for the current branch. The\n>> +              * relevent upstream is the fetched branch that is meant to be merged with\n>> +              * the current one, i.e. the one fetched to FETCH_HEAD.\n>> +              *\n>> +              * When there are several such branches, consider the request ambiguous and\n>> +              * err on the safe side by doing nothing and just emit a waring.\n>> +              */\n>> +             for (rm = ref_map; rm; rm = rm->next) {\n>> +                     fprintf(stderr, \"\\n -%s\", rm->name);\n>> +                     if (rm->peer_ref) {\n>> +                             fprintf(stderr, \" -> %s\", rm->peer_ref->name);\n>> +                     } else {\n>> +                             if (source_ref) {\n>> +                                     fprintf(stderr, \" -> FETCH_HEAD\\n\");\n>> +                                     warning(_(\"Multiple branch detected, incompatible with set-upstream\"));\n>\n> Shouldn't this be diagnosed as an error and stop the \"fetch\" or\n> \"pull\", though?\n\nWe can actually replace the warning with a die, but we think it's too harsh on the user, \nand if the warning is showing the upstream stays the same.\n\nWe fixed the spotted bugs/mistakes.\n\nThe fixed patch will follow.\n"},{"id":"374147","messageId":"20190419184203.14726-1-corentin.bompard@etu.univ-lyon1.fr","threadId":"50874","inReplyTo":"20190419160046.5283-1-corentin.bompard@etu.univ-lyon1.fr","subject":"[PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Corentin BOMPARD","fromEmail":"corentin.bompard@etu.univ-lyon1.fr","sentAt":"2019-04-19T18:42:03Z","receivedAt":"2019-04-19T18:42:31Z","isPatch":true,"sender":{"key":"corentin.bompard@etu.univ-lyon1.fr","avatar":"https://avatars.githubusercontent.com/u/23448477?v=4"},"body":"Add the --set-upstream option to git pull/fetch\nwhich lets the user set the upstream configuration\n(branch.<current-branch-name>.merge and\nbranch.<current-branch-name>.remote) for the current branch.\n\nFor example a typical use-case like\n\n    git clone http://example.com/my-public-fork\n\n    git remote add main http://example.com/project-main-repo\n\n    git pull --set-upstream main master\n\nor, instead of the last line\n\n    git fetch --set-upstream main master\n\n    git merge # or git rebase\n\nThis fonctionality works like git push --set-upstream.\n\nSigned-off-by: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\nSigned-off-by: Nathan BERBEZIER <nathan.berbezier@etu.univ-lyon1.fr>\nSigned-off-by: Pablo CHABANNE <pablo.chabanne@etu.univ-lyon1.fr>\nSigned-off-by: Matthieu MOY <matthieu.moy@univ-lyon1.fr>\n---\n Documentation/fetch-options.txt |   6 ++\n builtin/fetch.c                 |  49 +++++++++++-\n builtin/pull.c                  |   6 ++\n t/t5553-set-upstream.sh         | 137 ++++++++++++++++++++++++++++++++\n 4 files changed, 197 insertions(+), 1 deletion(-)\n create mode 100755 t/t5553-set-upstream.sh\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex fa0a3151b..a00705ea4 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -165,6 +165,12 @@ ifndef::git-pull[]\n \tDisable recursive fetching of submodules (this has the same effect as\n \tusing the `--recurse-submodules=no` option).\n \n+--set-upstream::\n+\tIf the new URL remote is correct, pull and add upstream (tracking) \n+\treference, used by argument-less linkgit:git-push[1] and other commands.\n+\tFor more information, see `branch.<name>.merge` and `branch.<name>.remote`\n+\tin linkgit:git-config[1].\n+\n --submodule-prefix=<path>::\n \tPrepend <path> to paths printed in informative messages\n \tsuch as \"Fetching submodule foo\".  This option is used\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex b620fd54b..760630f11 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -23,6 +23,7 @@\n #include \"packfile.h\"\n #include \"list-objects-filter-options.h\"\n #include \"commit-reach.h\"\n+#include \"branch.h\"\n \n static const char * const builtin_fetch_usage[] = {\n \tN_(\"git fetch [<options>] [<repository> [<refspec>...]]\"),\n@@ -46,7 +47,7 @@ static int fetch_prune_tags_config = -1; /* unspecified */\n static int prune_tags = -1; /* unspecified */\n #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n \n-static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n+static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative, set_upstream;\n static int progress = -1;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n static int max_children = 1;\n@@ -113,6 +114,8 @@ static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n \tOPT_BOOL(0, \"all\", &all,\n \t\t N_(\"fetch from all remotes\")),\n+\tOPT_BOOL(0, \"set-upstream\", &set_upstream,\n+\t\t N_(\"set upstream for git pull/fetch\")),\n \tOPT_BOOL('a', \"append\", &append,\n \t\t N_(\"append to .git/FETCH_HEAD instead of overwriting\")),\n \tOPT_STRING(0, \"upload-pack\", &upload_pack, N_(\"path\"),\n@@ -1317,6 +1320,50 @@ static int do_fetch(struct transport *transport,\n \t\tretcode = 1;\n \t\tgoto cleanup;\n \t}\n+\n+\tif (set_upstream) {\n+\t\tstruct branch *branch = branch_get(\"HEAD\");\n+\t\tstruct ref *rm;\n+\t\tstruct ref *source_ref = NULL;\n+\n+\t\t/*\n+\t\t * We're setting the upstream configuration for the current branch. The\n+\t\t * relevent upstream is the fetched branch that is meant to be merged with\n+\t\t * the current one, i.e. the one fetched to FETCH_HEAD.\n+\t\t *\n+\t\t * When there are several such branches, consider the request ambiguous and\n+\t\t * err on the safe side by doing nothing and just emit a waring.\n+\t\t */\n+\t\tfor (rm = ref_map; rm; rm = rm->next) {\n+\t\t\tif (!rm->peer_ref) {\n+\t\t\t\tif (source_ref) {\n+\t\t\t\t\twarning(_(\"multiple branch detected, incompatible with set-upstream\"));\n+\t\t\t\t\tsource_ref = NULL;\n+\t\t\t\t\tgoto skip;\n+\t\t\t\t} else {\n+\t\t\t\t\tsource_ref = rm;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tif (source_ref) {\n+\t\t\tif (!strcmp(source_ref->name, \"HEAD\") || \n+\t\t\t\tstarts_with(source_ref->name, \"refs/heads/\")) {\n+\t\t\t\tinstall_branch_config(0, branch->name,\n+\t\t\t\t\t\t\t transport->remote->name,\n+\t\t\t\t\t\t\t source_ref->name);\n+\t\t\t} else if (starts_with(source_ref->name, \"refs/remotes/\")) {\n+\t\t\t\twarning(_(\"not setting upstream for a remote remote-tracking branch\"));\n+\t\t\t} else if (starts_with(source_ref->name, \"refs/tags/\")) {\n+\t\t\t\twarning(_(\"tag upstream not set\"));\n+\t\t\t} else {\n+\t\t\t\twarning(_(\"unknown branch type\"));\n+\t\t\t}\n+\t\t} else {\n+\t\t\twarning(_(\"no source branch found. \\n\" \n+\t\t\t\t\"you need to specify exactly one branch with the set-upstream option.\"));\n+\t\t}\n+\t}\n+ skip:\n \tfree_refs(ref_map);\n \n \t/* if neither --no-tags nor --tags was specified, do automated tag\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 701d1473d..06d7cddce 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -122,6 +122,7 @@ static char *opt_update_shallow;\n static char *opt_refmap;\n static char *opt_ipv4;\n static char *opt_ipv6;\n+static char *set_upstream;\n \n static struct option pull_options[] = {\n \t/* Shared options */\n@@ -233,6 +234,9 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU('6',  \"ipv6\", &opt_ipv6, NULL,\n \t\tN_(\"use IPv6 addresses only\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_PASSTHRU(0, \"set-upstream\", &set_upstream, NULL,\n+\t\tN_(\"set upstream for git pull/fetch\"),\n+\t\tPARSE_OPT_NOARG),\n \n \tOPT_END()\n };\n@@ -541,6 +545,8 @@ static int run_fetch(const char *repo, const char **refspecs)\n \t\targv_array_push(&args, opt_ipv4);\n \tif (opt_ipv6)\n \t\targv_array_push(&args, opt_ipv6);\n+\tif (set_upstream)\n+\t\targv_array_push(&args, set_upstream);\n \n \tif (repo) {\n \t\targv_array_push(&args, repo);\ndiff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh\nnew file mode 100755\nindex 000000000..62ff77342\n--- /dev/null\n+++ b/t/t5553-set-upstream.sh\n@@ -0,0 +1,137 @@\n+#!/bin/sh\n+\n+test_description='\"git fetch/pull --set-upstream\" basic tests.\n+\n+'\n+. ./test-lib.sh\n+\n+check_config () {\n+\tprintf \"%s\\n\" \"$2\" \"$3\" >\"expect.$1\" &&\n+\t{\n+\t\tgit config \"branch.$1.remote\" && git config \"branch.$1.merge\"\n+\t} >\"actual.$1\" &&\n+\ttest_cmp \"expect.$1\" \"actual.$1\"\n+}\n+\n+check_config_empty () {\n+\ttest_expect_code 1 git config \"branch.$1.remote\" &&\n+\ttest_expect_code 1 git config \"branch.$1.merge\"\n+}\n+\n+clear_config () {\n+\tgit config --unset \"branch.$1.remote\" &&\n+\tgit config --unset \"branch.$1.merge\"\n+}\n+\n+ensure_fresh_upstream () {\n+\trm -rf parent && git init --bare parent\n+}\n+\n+test_expect_success 'setup bare parent fetch' '\n+\tensure_fresh_upstream &&\n+\tgit remote add upstream parent &&\n+\tgit remote add up parent\n+'\n+\n+test_expect_success 'setup commit on master and other fetch' '\n+\ttest_commit one &&\n+\tgit push upstream master &&\n+\tgit checkout -b other &&\n+\ttest_commit two &&\n+\tgit push upstream other\n+'\n+\n+#tests for fetch --set-upstream\n+\n+test_expect_success 'fetch --set-upstream does not set upstream w/o branch' '\n+\tgit checkout master &&\n+\tgit fetch --set-upstream upstream &&\n+\tcheck_config_empty master &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '\n+\tgit fetch --set-upstream upstream master &&\n+\tcheck_config master upstream refs/heads/master &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'fetch --set-upstream upstream other sets branch other' '\n+\tgit fetch --set-upstream upstream other &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'fetch --set-upstream master:other does not set the branch other2' '\n+\tgit fetch --set-upstream upstream master:other2 &&\n+\tcheck_config_empty other2\n+'\n+\n+test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with the bad url' '\n+\ttest_must_fail git fetch --set-upstream http://nosuchdomain.example.com &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_empty other &&\n+\tcheck_config_empty other2\n+'\n+\n+#tests for pull --set-upstream\n+\n+test_expect_success 'setup bare parent pull' '\n+\tgit remote rm upstream &&\n+\tensure_fresh_upstream &&\n+\tgit remote add upstream parent\n+'\n+\n+test_expect_success 'setup commit on master and other pull' '\n+\ttest_commit three &&\n+\tgit push --tags upstream master &&\n+\ttest_commit four &&\n+\tgit push upstream other\n+'\n+\n+test_expect_success 'pull --set-upstream upstream master sets branch master but not other' '\n+\tgit pull --set-upstream upstream master &&\n+\tcheck_config master upstream refs/heads/master &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'pull --set-upstream master:other2 does not set the branch other2' '\n+\tgit pull --set-upstream upstream master:other2 &&\n+\tcheck_config_empty other2\n+'\n+\n+test_expect_success 'pull --set-upstream upstream other sets branch master' '\n+\tgit pull --set-upstream upstream other &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_empty other\n+'\n+\n+test_expect_success 'pull --set-upstream upstream tag does not set the tag' '\n+\tgit pull --tags --set-upstream upstream three &&\n+\tcheck_config_empty three\n+'\n+\n+test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with the bad url' '\n+\ttest_must_fail git pull --set-upstream http://nosuchdomain.example.com &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_empty other &&\n+\tcheck_config_empty other2 &&\n+\tcheck_config_empty three\n+'\n+\n+test_expect_success 'pull --set-upstream upstream HEAD sets branch HEAD' '\n+\tgit pull --set-upstream upstream HEAD &&\n+\tcheck_config master upstream HEAD &&\n+\tgit checkout other &&\n+\tgit pull --set-upstream upstream HEAD &&\n+\tcheck_config other upstream HEAD\n+'\n+\n+test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '\n+\tclear_config master &&\n+\tgit pull --set-upstream upstream master three &&\n+\tcheck_config_empty master &&\n+\tcheck_config_empty three\n+'\n+\n+test_done\n-- \n2.21.0-rc0\n\n"},{"id":"374151","messageId":"867ebq72ib.fsf@univ-lyon1.fr","threadId":"50874","inReplyTo":"04f23ebf83bd4aff90ee9ca88cec984e@BPMBX2013-01.univ-lyon1.fr","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2019-04-19T09:44:44Z","receivedAt":"2019-04-19T19:06:53Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Matthieu Moy <Matthieu.Moy@univ-lyon1.fr> writes:\n>\n>> -u::\n>> --set-upstream::\n>> \tFor every branch that is up to date or successfully pushed, add\n>> \tupstream (tracking) reference, used by argument-less\n>> \tlinkgit:git-pull[1] and other commands. For more information,\n>> \tsee `branch.<name>.merge` in linkgit:git-config[1].\n>>\n>> Probably the reasoning was to make a symmetry between \"git push\n>> --set-upstream\", which mentions \"pull\" in the doc, and the new \"git pull\n>> --set-upstream\". However, I do not think there should be such symmetry:\n>\n> Yeah, if \"git push --set-upstream\" affects the settings that is used\n> by \"git pull\", then the above description is good.  Does this new\n> \"git pull --set-upstream\" affect the settings used by \"git push\"?  I\n> somehow did not think so.  It records the remote and branch used by\n> this particular \"git pull\" invocation in branch.<name>.{remote,merge}\n> for use by future uses of \"git pull\", right?\n\nIt also affects push, in the absence of a branch.<name>.pushRemote\nsetting (branch.<name>.remote will be used).\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"374152","messageId":"xmqqwojqeh6b.fsf@gitster-ct.c.googlers.com","threadId":"50874","inReplyTo":"86h8av7ian.fsf@univ-lyon1.fr","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-04-19T04:46:04Z","receivedAt":"2019-04-19T19:07:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@univ-lyon1.fr> writes:\n\n> -u::\n> --set-upstream::\n> \tFor every branch that is up to date or successfully pushed, add\n> \tupstream (tracking) reference, used by argument-less\n> \tlinkgit:git-pull[1] and other commands. For more information,\n> \tsee `branch.<name>.merge` in linkgit:git-config[1].\n>\n> Probably the reasoning was to make a symmetry between \"git push\n> --set-upstream\", which mentions \"pull\" in the doc, and the new \"git pull\n> --set-upstream\". However, I do not think there should be such symmetry:\n\nYeah, if \"git push --set-upstream\" affects the settings that is used\nby \"git pull\", then the above description is good.  Does this new\n\"git pull --set-upstream\" affect the settings used by \"git push\"?  I\nsomehow did not think so.  It records the remote and branch used by\nthis particular \"git pull\" invocation in branch.<name>.{remote,merge}\nfor use by future uses of \"git pull\", right?\n\n"},{"id":"374236","messageId":"86zhoil3yw.fsf@univ-lyon1.fr","threadId":"50874","inReplyTo":"f601baa2c2a04ddea4ba32ab25d0dd21@BPMBX2013-01.univ-lyon1.fr","subject":"Re: [PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@univ-lyon1.fr","sentAt":"2019-04-22T10:38:31Z","receivedAt":"2019-04-22T10:38:49Z","isPatch":true,"sender":{"key":"matthieu.moy@univ-lyon1.fr","avatar":"https://gravatar.com/avatar/8ab83b763226bd297b59ddd1463a8bfc852924577191233f73a953b86888fe0c?d=mp&s=160"},"body":"BOMPARD CORENTIN p1603631 <corentin.bompard@etu.univ-lyon1.fr> writes:\n\n> Add the --set-upstream option to git pull/fetch\n\nAdd _a_?\n\n> which lets the user set the upstream configuration\n> (branch.<current-branch-name>.merge and\n> branch.<current-branch-name>.remote) for the current branch.\n>\n> For example a typical use-case like\n\nI don't understand this sentence. Perhaps\n\nA typical use-case is:\n\n>     git clone http://example.com/my-public-fork\n>\n>     git remote add main http://example.com/project-main-repo\n>\n>     git pull --set-upstream main master\n\nI'd keep the newline before and after the block of commands, but not\nbetween commands.\n\n> +--set-upstream::\n> +\tIf the new URL remote is correct, pull and add upstream (tracking) \n\n\"URL remote\" seems translated literally from french. You probably meant\n\"remote URL\". I'd write \"If the remote is fetched successfully, ...\".\n\n> +\treference, used by argument-less linkgit:git-push[1] and other commands.\n\nWhat's your conclusion on the discussion following your previous\nsubmission here? Mine is that git-push is not the best command to\nmention here. The setting impacts pull, fetch, push, merge and rebase\n(I may have missed others), and to me the main motivation is to impact\npull, so if only one command should be cited, it should be pull.\n\n> +\t\t * When there are several such branches, consider the request ambiguous and\n> +\t\t * err on the safe side by doing nothing and just emit a waring.\n\ns/waring/warning/\n\n> +\t\t\tif (!rm->peer_ref) {\n> +\t\t\t\tif (source_ref) {\n> +\t\t\t\t\twarning(_(\"multiple branch detected, incompatible with set-upstream\"));\n> +\t\t\t\t\tsource_ref = NULL;\n> +\t\t\t\t\tgoto skip;\n\n\"source_ref = NULL\" is dead code due to the \"goto skip\" right below. I'd\nremove it.\n\n> +\t\tif (source_ref) {\n> +\t\t\tif (!strcmp(source_ref->name, \"HEAD\") || \n> +\t\t\t\tstarts_with(source_ref->name, \"refs/heads/\")) {\n> +\t\t\t\tinstall_branch_config(0, branch->name,\n> +\t\t\t\t\t\t\t transport->remote->name,\n> +\t\t\t\t\t\t\t source_ref->name);\n> +\t\t\t} else if (starts_with(source_ref->name, \"refs/remotes/\")) {\n> +\t\t\t\twarning(_(\"not setting upstream for a remote remote-tracking branch\"));\n> +\t\t\t} else if (starts_with(source_ref->name, \"refs/tags/\")) {\n> +\t\t\t\twarning(_(\"tag upstream not set\"));\n\nThe second warning seems a bit cryptic to me. Why not take the same as\nthe first, with s/remote-tracking branch/tag/?\n\n> +\t\t\twarning(_(\"no source branch found. \\n\" \n\nAlready noted in previous round: useless trailing whitespace.\n\n> --- /dev/null\n> +++ b/t/t5553-set-upstream.sh\n> @@ -0,0 +1,137 @@\n> +#!/bin/sh\n> +\n> +test_description='\"git fetch/pull --set-upstream\" basic tests.\n> +\n> +'\n\nDon't make $test_description a multi-line string, just close the ' on\nthe first line.\n\n> +check_config_empty () {\n\nPerhaps name the function check_config_missing instead of ..._empty.\n\n> +test_expect_success 'setup bare parent fetch' '\n> +\tensure_fresh_upstream &&\n> +\tgit remote add upstream parent &&\n> +\tgit remote add up parent\n\nI don't think you ever use this \"up\". Either add a comment explaining\nwhy it's needed, or remove it.\n\n> +test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '\n> +\tgit fetch --set-upstream upstream master &&\n> +\tcheck_config master upstream refs/heads/master &&\n> +\tcheck_config_empty other\n> +'\n> +\n> +test_expect_success 'fetch --set-upstream upstream other sets branch other' '\n> +\tgit fetch --set-upstream upstream other &&\n> +\tcheck_config master upstream refs/heads/other &&\n> +\tcheck_config_empty other\n> +'\n\nThe first test sets the config for master, and the config is not reset\nbetween tests, so the second may read the config set by \"git fetch\"\nright above, or just a leftover config of the previous test.\n\nYou need a \"clear_config\" in between. Best is to put it at the beginning\nof tests so that it's clear that the test does not depend on what has\nbeen executed previously. There are several places where you really need\nit. It probably makes sense to use it at the start of every tests for\nconsistency and future-proof-ness.\n\n> +test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with the bad url' '\n> +\ttest_must_fail git fetch --set-upstream http://nosuchdomain.example.com &&\n> +\tcheck_config master upstream refs/heads/other &&\n> +\tcheck_config_empty other &&\n> +\tcheck_config_empty other2\n> +'\n\nIt would probably make sense to check what happens when running\n\n  git fetch --set-upstream <some-valid-url>\n\ni.e. use a URL instead of a named remote.\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"380438","messageId":"20190814134629.21096-1-git@matthieu-moy.fr","threadId":"50874","inReplyTo":"86zhoil3yw.fsf@univ-lyon1.fr","subject":"[PATCH] pull, fetch: add --set-upstream option","fromName":"Matthieu Moy","fromEmail":"git@matthieu-moy.fr","sentAt":"2019-08-14T13:46:29Z","receivedAt":"2019-08-14T14:06:40Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\n\nAdd the --set-upstream option to git pull/fetch\nwhich lets the user set the upstream configuration\n(branch.<current-branch-name>.merge and\nbranch.<current-branch-name>.remote) for the current branch.\n\nA typical use-case is:\n\n    git clone http://example.com/my-public-fork\n    git remote add main http://example.com/project-main-repo\n    git pull --set-upstream main master\n\nor, instead of the last line:\n\n    git fetch --set-upstream main master\n    git merge # or git rebase\n\nThis functionality is analog to push --set-upstream.\n\nSigned-off-by: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\nSigned-off-by: Nathan BERBEZIER <nathan.berbezier@etu.univ-lyon1.fr>\nSigned-off-by: Pablo CHABANNE <pablo.chabanne@etu.univ-lyon1.fr>\nSigned-off-by: Matthieu Moy <git@matthieu-moy.fr>\nPatch-edited-by: Matthieu Moy <git@matthieu-moy.fr>\n---\nThis is a followup on\nhttps://public-inbox.org/git/86zhoil3yw.fsf@univ-lyon1.fr/. It's\ninitially a student project, but students didn't get time to complete\nit. Still, I think the feature is interesting, and I finally get time\nto fix the remarks made up to now. This now looks good to me, but\nobviously needs other pairs of eyes.\n\nThanks,\n\n Documentation/fetch-options.txt |   7 ++\n builtin/fetch.c                 |  48 ++++++++-\n builtin/pull.c                  |   6 ++\n t/t5553-set-upstream.sh         | 178 ++++++++++++++++++++++++++++++++\n 4 files changed, 238 insertions(+), 1 deletion(-)\n create mode 100755 t/t5553-set-upstream.sh\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 3c9b4f9e09..99df1f3d4e 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -169,6 +169,13 @@ ifndef::git-pull[]\n \tDisable recursive fetching of submodules (this has the same effect as\n \tusing the `--recurse-submodules=no` option).\n \n+--set-upstream::\n+\tIf the remote is fetched successfully, pull and add upstream\n+\t(tracking) reference, used by argument-less\n+\tlinkgit:git-pull[1] and other commands. For more information,\n+\tsee `branch.<name>.merge` and `branch.<name>.remote` in\n+\tlinkgit:git-config[1].\n+\n --submodule-prefix=<path>::\n \tPrepend <path> to paths printed in informative messages\n \tsuch as \"Fetching submodule foo\".  This option is used\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 717dd14e89..5557ae1c04 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -23,6 +23,7 @@\n #include \"packfile.h\"\n #include \"list-objects-filter-options.h\"\n #include \"commit-reach.h\"\n+#include \"branch.h\"\n \n #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)\n \n@@ -50,7 +51,7 @@ static int fetch_prune_tags_config = -1; /* unspecified */\n static int prune_tags = -1; /* unspecified */\n #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n \n-static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n+static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative, set_upstream;\n static int progress = -1;\n static int enable_auto_gc = 1;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n@@ -123,6 +124,8 @@ static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n \tOPT_BOOL(0, \"all\", &all,\n \t\t N_(\"fetch from all remotes\")),\n+\tOPT_BOOL(0, \"set-upstream\", &set_upstream,\n+\t\t N_(\"set upstream for git pull/fetch\")),\n \tOPT_BOOL('a', \"append\", &append,\n \t\t N_(\"append to .git/FETCH_HEAD instead of overwriting\")),\n \tOPT_STRING(0, \"upload-pack\", &upload_pack, N_(\"path\"),\n@@ -1367,6 +1370,49 @@ static int do_fetch(struct transport *transport,\n \t\tretcode = 1;\n \t\tgoto cleanup;\n \t}\n+\n+\tif (set_upstream) {\n+\t\tstruct branch *branch = branch_get(\"HEAD\");\n+\t\tstruct ref *rm;\n+\t\tstruct ref *source_ref = NULL;\n+\n+\t\t/*\n+\t\t * We're setting the upstream configuration for the current branch. The\n+\t\t * relevent upstream is the fetched branch that is meant to be merged with\n+\t\t * the current one, i.e. the one fetched to FETCH_HEAD.\n+\t\t *\n+\t\t * When there are several such branches, consider the request ambiguous and\n+\t\t * err on the safe side by doing nothing and just emit a warning.\n+\t\t */\n+\t\tfor (rm = ref_map; rm; rm = rm->next) {\n+\t\t\tif (!rm->peer_ref) {\n+\t\t\t\tif (source_ref) {\n+\t\t\t\t\twarning(_(\"multiple branch detected, incompatible with --set-upstream\"));\n+\t\t\t\t\tgoto skip;\n+\t\t\t\t} else {\n+\t\t\t\t\tsource_ref = rm;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tif (source_ref) {\n+\t\t\tif (!strcmp(source_ref->name, \"HEAD\") || \n+\t\t\t\tstarts_with(source_ref->name, \"refs/heads/\")) {\n+\t\t\t\tinstall_branch_config(0, branch->name,\n+\t\t\t\t\t\t\t transport->remote->name,\n+\t\t\t\t\t\t\t source_ref->name);\n+\t\t\t} else if (starts_with(source_ref->name, \"refs/remotes/\")) {\n+\t\t\t\twarning(_(\"not setting upstream for a remote remote-tracking branch\"));\n+\t\t\t} else if (starts_with(source_ref->name, \"refs/tags/\")) {\n+\t\t\t\twarning(_(\"not setting upstream for a remote tag\"));\n+\t\t\t} else {\n+\t\t\t\twarning(_(\"unknown branch type\"));\n+\t\t\t}\n+\t\t} else {\n+\t\t\twarning(_(\"no source branch found.\\n\"\n+\t\t\t\t\"you need to specify exactly one branch with the --set-upstream option.\"));\n+\t\t}\n+\t}\n+ skip:\n \tfree_refs(ref_map);\n \n \t/* if neither --no-tags nor --tags was specified, do automated tag\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex f1eaf6e6ed..d25ff13a60 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -129,6 +129,7 @@ static char *opt_refmap;\n static char *opt_ipv4;\n static char *opt_ipv6;\n static int opt_show_forced_updates = -1;\n+static char *set_upstream;\n \n static struct option pull_options[] = {\n \t/* Shared options */\n@@ -243,6 +244,9 @@ static struct option pull_options[] = {\n \t\tPARSE_OPT_NOARG),\n \tOPT_BOOL(0, \"show-forced-updates\", &opt_show_forced_updates,\n \t\t N_(\"check for forced-updates on all updated branches\")),\n+\tOPT_PASSTHRU(0, \"set-upstream\", &set_upstream, NULL,\n+\t\tN_(\"set upstream for git pull/fetch\"),\n+\t\tPARSE_OPT_NOARG),\n \n \tOPT_END()\n };\n@@ -556,6 +560,8 @@ static int run_fetch(const char *repo, const char **refspecs)\n \t\targv_array_push(&args, \"--show-forced-updates\");\n \telse if (opt_show_forced_updates == 0)\n \t\targv_array_push(&args, \"--no-show-forced-updates\");\n+\tif (set_upstream)\n+\t\targv_array_push(&args, set_upstream);\n \n \tif (repo) {\n \t\targv_array_push(&args, repo);\ndiff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh\nnew file mode 100755\nindex 0000000000..bd1a94f494\n--- /dev/null\n+++ b/t/t5553-set-upstream.sh\n@@ -0,0 +1,178 @@\n+#!/bin/sh\n+\n+test_description='\"git fetch/pull --set-upstream\" basic tests.'\n+. ./test-lib.sh\n+\n+check_config () {\n+\tprintf \"%s\\n\" \"$2\" \"$3\" >\"expect.$1\" &&\n+\t{\n+\t\tgit config \"branch.$1.remote\" && git config \"branch.$1.merge\"\n+\t} >\"actual.$1\" &&\n+\ttest_cmp \"expect.$1\" \"actual.$1\"\n+}\n+\n+check_config_missing () {\n+\ttest_expect_code 1 git config \"branch.$1.remote\" &&\n+\ttest_expect_code 1 git config \"branch.$1.merge\"\n+}\n+\n+clear_config () {\n+\tfor branch in \"$@\"; do\n+\t\ttest_might_fail git config --unset-all \"branch.$branch.remote\"\n+\t\ttest_might_fail git config --unset-all \"branch.$branch.merge\"\n+\tdone\n+}\n+\n+ensure_fresh_upstream () {\n+\trm -rf parent && git init --bare parent\n+}\n+\n+test_expect_success 'setup bare parent fetch' '\n+\tensure_fresh_upstream &&\n+\tgit remote add upstream parent\n+'\n+\n+test_expect_success 'setup commit on master and other fetch' '\n+\ttest_commit one &&\n+\tgit push upstream master &&\n+\tgit checkout -b other &&\n+\ttest_commit two &&\n+\tgit push upstream other\n+'\n+\n+#tests for fetch --set-upstream\n+\n+test_expect_success 'fetch --set-upstream does not set upstream w/o branch' '\n+\tclear_config master other &&\n+\tgit checkout master &&\n+\tgit fetch --set-upstream upstream &&\n+\tcheck_config_missing master &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '\n+\tclear_config master other &&\n+\tgit fetch --set-upstream upstream master &&\n+\tcheck_config master upstream refs/heads/master &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'fetch --set-upstream upstream other sets branch other' '\n+\tclear_config master other &&\n+\tgit fetch --set-upstream upstream other &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'fetch --set-upstream master:other does not set the branch other2' '\n+\tclear_config other2 &&\n+\tgit fetch --set-upstream upstream master:other2 &&\n+\tcheck_config_missing other2\n+'\n+\n+test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with invalid url' '\n+\t# master explicitly not cleared, we check that it is not touched from previous value\n+\tclear_config other other2 &&\n+\ttest_must_fail git fetch --set-upstream http://nosuchdomain.example.com &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2\n+'\n+\n+test_expect_success 'fetch --set-upstream with valid URL sets upstream to URL' '\n+\tclear_config other other2 &&\n+\turl=\"file://'\"$PWD\"'\" &&\n+\tgit fetch --set-upstream \"$url\" &&\n+\tcheck_config master \"$url\" HEAD &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2\n+'\n+\n+#tests for pull --set-upstream\n+\n+test_expect_success 'setup bare parent pull' '\n+\tgit remote rm upstream &&\n+\tensure_fresh_upstream &&\n+\tgit remote add upstream parent\n+'\n+\n+test_expect_success 'setup commit on master and other pull' '\n+\ttest_commit three &&\n+\tgit push --tags upstream master &&\n+\ttest_commit four &&\n+\tgit push upstream other\n+'\n+\n+test_expect_success 'pull --set-upstream upstream master sets branch master but not other' '\n+\tclear_config master other &&\n+\tgit pull --set-upstream upstream master &&\n+\tcheck_config master upstream refs/heads/master &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'pull --set-upstream master:other2 does not set the branch other2' '\n+\tclear_config other2 &&\n+\tgit pull --set-upstream upstream master:other2 &&\n+\tcheck_config_missing other2\n+'\n+\n+test_expect_success 'pull --set-upstream upstream other sets branch master' '\n+\tclear_config master other &&\n+\tgit pull --set-upstream upstream other &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'pull --set-upstream upstream tag does not set the tag' '\n+\tclear_config three &&\n+\tgit pull --tags --set-upstream upstream three &&\n+\tcheck_config_missing three\n+'\n+\n+test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with invalid url' '\n+\t# master explicitly not cleared, we check that it is not touched from previous value\n+\tclear_config other other2 three &&\n+\ttest_must_fail git pull --set-upstream http://nosuchdomain.example.com &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2 &&\n+\tcheck_config_missing three\n+'\n+\n+test_expect_success 'pull --set-upstream upstream HEAD sets branch HEAD' '\n+\tclear_config master other &&\n+\tgit pull --set-upstream upstream HEAD &&\n+\tcheck_config master upstream HEAD &&\n+\tgit checkout other &&\n+\tgit pull --set-upstream upstream HEAD &&\n+\tcheck_config other upstream HEAD\n+'\n+\n+test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '\n+\tclear_config master three &&\n+\tgit pull --set-upstream upstream master three &&\n+\tcheck_config_missing master &&\n+\tcheck_config_missing three\n+'\n+\n+test_expect_success 'pull --set-upstream with valid URL sets upstream to URL' '\n+\tclear_config master other other2 &&\n+\tgit checkout master &&\n+\turl=\"file://'\"$PWD\"'\" &&\n+\tgit pull --set-upstream \"$url\" &&\n+\tcheck_config master \"$url\" HEAD &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2\n+'\n+\n+test_expect_success 'pull --set-upstream with valid URL and branch sets branch' '\n+\tclear_config master other other2 &&\n+\tgit checkout master &&\n+\turl=\"file://'\"$PWD\"'\" &&\n+\tgit pull --set-upstream \"$url\" master &&\n+\tcheck_config master \"$url\" refs/heads/master &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2\n+'\n+\n+test_done\n-- \n2.20.1.98.gecbdaf0\n\n"},{"id":"380448","messageId":"20190814171404.zqtd4xctjobgpzby@localhost.localdomain","threadId":"50874","inReplyTo":"20190814134629.21096-1-git@matthieu-moy.fr","subject":"Re: [PATCH] pull, fetch: add --set-upstream option","fromName":"Pratyush Yadav","fromEmail":"me@yadavpratyush.com","sentAt":"2019-08-14T17:14:04Z","receivedAt":"2019-08-14T17:14:15Z","isPatch":true,"sender":{"key":"me@yadavpratyush.com","avatar":"https://avatars.githubusercontent.com/u/8817931?v=4"},"body":"Hi Matthieu,\n\nThis is not really a review. Just some minor nitpicks I spotted while \nreading through.\n\nOn 14/08/19 03:46PM, Matthieu Moy wrote:\n> From: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\n> \n> Add the --set-upstream option to git pull/fetch\n> which lets the user set the upstream configuration\n> (branch.<current-branch-name>.merge and\n> branch.<current-branch-name>.remote) for the current branch.\n> \n> A typical use-case is:\n> \n>     git clone http://example.com/my-public-fork\n>     git remote add main http://example.com/project-main-repo\n>     git pull --set-upstream main master\n> \n> or, instead of the last line:\n> \n>     git fetch --set-upstream main master\n>     git merge # or git rebase\n> \n> This functionality is analog to push --set-upstream.\n> \n> Signed-off-by: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\n> Signed-off-by: Nathan BERBEZIER <nathan.berbezier@etu.univ-lyon1.fr>\n> Signed-off-by: Pablo CHABANNE <pablo.chabanne@etu.univ-lyon1.fr>\n> Signed-off-by: Matthieu Moy <git@matthieu-moy.fr>\n> Patch-edited-by: Matthieu Moy <git@matthieu-moy.fr>\n> ---\n> This is a followup on\n> https://public-inbox.org/git/86zhoil3yw.fsf@univ-lyon1.fr/. It's\n> initially a student project, but students didn't get time to complete\n> it. Still, I think the feature is interesting, and I finally get time\n> to fix the remarks made up to now. This now looks good to me, but\n> obviously needs other pairs of eyes.\n> \n> Thanks,\n> \n>  Documentation/fetch-options.txt |   7 ++\n>  builtin/fetch.c                 |  48 ++++++++-\n>  builtin/pull.c                  |   6 ++\n>  t/t5553-set-upstream.sh         | 178 ++++++++++++++++++++++++++++++++\n>  4 files changed, 238 insertions(+), 1 deletion(-)\n>  create mode 100755 t/t5553-set-upstream.sh\n> \n> diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\n> index 3c9b4f9e09..99df1f3d4e 100644\n> --- a/Documentation/fetch-options.txt\n> +++ b/Documentation/fetch-options.txt\n> @@ -169,6 +169,13 @@ ifndef::git-pull[]\n>  \tDisable recursive fetching of submodules (this has the same effect as\n>  \tusing the `--recurse-submodules=no` option).\n>  \n> +--set-upstream::\n> +\tIf the remote is fetched successfully, pull and add upstream\n> +\t(tracking) reference, used by argument-less\n> +\tlinkgit:git-pull[1] and other commands. For more information,\n> +\tsee `branch.<name>.merge` and `branch.<name>.remote` in\n> +\tlinkgit:git-config[1].\n> +\n>  --submodule-prefix=<path>::\n>  \tPrepend <path> to paths printed in informative messages\n>  \tsuch as \"Fetching submodule foo\".  This option is used\n> diff --git a/builtin/fetch.c b/builtin/fetch.c\n> index 717dd14e89..5557ae1c04 100644\n> --- a/builtin/fetch.c\n> +++ b/builtin/fetch.c\n> @@ -23,6 +23,7 @@\n>  #include \"packfile.h\"\n>  #include \"list-objects-filter-options.h\"\n>  #include \"commit-reach.h\"\n> +#include \"branch.h\"\n>  \n>  #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)\n>  \n> @@ -50,7 +51,7 @@ static int fetch_prune_tags_config = -1; /* unspecified */\n>  static int prune_tags = -1; /* unspecified */\n>  #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n>  \n> -static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n> +static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative, set_upstream;\n\nThis line is getting pretty long. I think it is a good idea to split it \ninto two.\n\n>  static int progress = -1;\n>  static int enable_auto_gc = 1;\n>  static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n> @@ -123,6 +124,8 @@ static struct option builtin_fetch_options[] = {\n>  \tOPT__VERBOSITY(&verbosity),\n>  \tOPT_BOOL(0, \"all\", &all,\n>  \t\t N_(\"fetch from all remotes\")),\n> +\tOPT_BOOL(0, \"set-upstream\", &set_upstream,\n> +\t\t N_(\"set upstream for git pull/fetch\")),\n>  \tOPT_BOOL('a', \"append\", &append,\n>  \t\t N_(\"append to .git/FETCH_HEAD instead of overwriting\")),\n>  \tOPT_STRING(0, \"upload-pack\", &upload_pack, N_(\"path\"),\n> @@ -1367,6 +1370,49 @@ static int do_fetch(struct transport *transport,\n>  \t\tretcode = 1;\n>  \t\tgoto cleanup;\n>  \t}\n> +\n> +\tif (set_upstream) {\n> +\t\tstruct branch *branch = branch_get(\"HEAD\");\n> +\t\tstruct ref *rm;\n> +\t\tstruct ref *source_ref = NULL;\n> +\n> +\t\t/*\n> +\t\t * We're setting the upstream configuration for the current branch. The\n> +\t\t * relevent upstream is the fetched branch that is meant to be merged with\n> +\t\t * the current one, i.e. the one fetched to FETCH_HEAD.\n> +\t\t *\n> +\t\t * When there are several such branches, consider the request ambiguous and\n> +\t\t * err on the safe side by doing nothing and just emit a warning.\n> +\t\t */\n\nThe comment lines cross the 80 column boundary. The usual convention in \nthis project is to try to keep lines below 80 columns. For strings IMO \nan exception can be allowed because breaking them up makes it harder to \ngrep for them. But comments are the easiest to format.\n\nAre you using a tab size of 4? That might explain why your line breaks \nare just after the 80 col boundary. The coding guidelines say you should \nmake your tab characters 8 columns wide.\n\n> +\t\tfor (rm = ref_map; rm; rm = rm->next) {\n> +\t\t\tif (!rm->peer_ref) {\n> +\t\t\t\tif (source_ref) {\n> +\t\t\t\t\twarning(_(\"multiple branch detected, incompatible with --set-upstream\"));\n> +\t\t\t\t\tgoto skip;\n> +\t\t\t\t} else {\n> +\t\t\t\t\tsource_ref = rm;\n> +\t\t\t\t}\n> +\t\t\t}\n> +\t\t}\n> +\t\tif (source_ref) {\n> +\t\t\tif (!strcmp(source_ref->name, \"HEAD\") || \n\nThis line has a trailing space.\n\n> +\t\t\t\tstarts_with(source_ref->name, \"refs/heads/\")) {\n> +\t\t\t\tinstall_branch_config(0, branch->name,\n> +\t\t\t\t\t\t\t transport->remote->name,\n> +\t\t\t\t\t\t\t source_ref->name);\n\nIn other places around this code, multi line function calls are aligned \nwith the opening parenthesis. It is a good idea to follow that \nconvention.\n\nSo this should change to something like:\n\n\t\t\t\tinstall_branch_config(0, branch->name,\n\t\t\t\t\t\t      transport->remote->name,\n\t\t\t\t\t\t      source_ref->name);\n \nMaybe this discrepancy is because you are using the wrong tab size?\n\n> +\t\t\t} else if (starts_with(source_ref->name, \"refs/remotes/\")) {\n> +\t\t\t\twarning(_(\"not setting upstream for a remote remote-tracking branch\"));\n> +\t\t\t} else if (starts_with(source_ref->name, \"refs/tags/\")) {\n> +\t\t\t\twarning(_(\"not setting upstream for a remote tag\"));\n> +\t\t\t} else {\n> +\t\t\t\twarning(_(\"unknown branch type\"));\n> +\t\t\t}\n\nNo need to wrap single line if statements in braces.\n\n> +\t\t} else {\n> +\t\t\twarning(_(\"no source branch found.\\n\"\n> +\t\t\t\t\"you need to specify exactly one branch with the --set-upstream option.\"));\n> +\t\t}\n> +\t}\n> + skip:\n>  \tfree_refs(ref_map);\n>  \n>  \t/* if neither --no-tags nor --tags was specified, do automated tag\n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index f1eaf6e6ed..d25ff13a60 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -129,6 +129,7 @@ static char *opt_refmap;\n>  static char *opt_ipv4;\n>  static char *opt_ipv6;\n>  static int opt_show_forced_updates = -1;\n> +static char *set_upstream;\n>  \n>  static struct option pull_options[] = {\n>  \t/* Shared options */\n> @@ -243,6 +244,9 @@ static struct option pull_options[] = {\n>  \t\tPARSE_OPT_NOARG),\n>  \tOPT_BOOL(0, \"show-forced-updates\", &opt_show_forced_updates,\n>  \t\t N_(\"check for forced-updates on all updated branches\")),\n> +\tOPT_PASSTHRU(0, \"set-upstream\", &set_upstream, NULL,\n> +\t\tN_(\"set upstream for git pull/fetch\"),\n> +\t\tPARSE_OPT_NOARG),\n>  \n>  \tOPT_END()\n>  };\n> @@ -556,6 +560,8 @@ static int run_fetch(const char *repo, const char **refspecs)\n>  \t\targv_array_push(&args, \"--show-forced-updates\");\n>  \telse if (opt_show_forced_updates == 0)\n>  \t\targv_array_push(&args, \"--no-show-forced-updates\");\n> +\tif (set_upstream)\n> +\t\targv_array_push(&args, set_upstream);\n>  \n>  \tif (repo) {\n>  \t\targv_array_push(&args, repo);\n> diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh\n> new file mode 100755\n> index 0000000000..bd1a94f494\n> --- /dev/null\n> +++ b/t/t5553-set-upstream.sh\n> @@ -0,0 +1,178 @@\n> +#!/bin/sh\n> +\n> +test_description='\"git fetch/pull --set-upstream\" basic tests.'\n> +. ./test-lib.sh\n> +\n> +check_config () {\n> +\tprintf \"%s\\n\" \"$2\" \"$3\" >\"expect.$1\" &&\n> +\t{\n> +\t\tgit config \"branch.$1.remote\" && git config \"branch.$1.merge\"\n> +\t} >\"actual.$1\" &&\n> +\ttest_cmp \"expect.$1\" \"actual.$1\"\n> +}\n> +\n> +check_config_missing () {\n> +\ttest_expect_code 1 git config \"branch.$1.remote\" &&\n> +\ttest_expect_code 1 git config \"branch.$1.merge\"\n> +}\n> +\n> +clear_config () {\n> +\tfor branch in \"$@\"; do\n> +\t\ttest_might_fail git config --unset-all \"branch.$branch.remote\"\n> +\t\ttest_might_fail git config --unset-all \"branch.$branch.merge\"\n> +\tdone\n> +}\n> +\n> +ensure_fresh_upstream () {\n> +\trm -rf parent && git init --bare parent\n> +}\n> +\n> +test_expect_success 'setup bare parent fetch' '\n> +\tensure_fresh_upstream &&\n> +\tgit remote add upstream parent\n> +'\n> +\n> +test_expect_success 'setup commit on master and other fetch' '\n> +\ttest_commit one &&\n> +\tgit push upstream master &&\n> +\tgit checkout -b other &&\n> +\ttest_commit two &&\n> +\tgit push upstream other\n> +'\n> +\n> +#tests for fetch --set-upstream\n\nAdd a space after the '#'. Same in other comments below.\n\n> +\n> +test_expect_success 'fetch --set-upstream does not set upstream w/o branch' '\n> +\tclear_config master other &&\n> +\tgit checkout master &&\n> +\tgit fetch --set-upstream upstream &&\n> +\tcheck_config_missing master &&\n> +\tcheck_config_missing other\n> +'\n> +\n> +test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '\n> +\tclear_config master other &&\n> +\tgit fetch --set-upstream upstream master &&\n> +\tcheck_config master upstream refs/heads/master &&\n> +\tcheck_config_missing other\n> +'\n> +\n> +test_expect_success 'fetch --set-upstream upstream other sets branch other' '\n> +\tclear_config master other &&\n> +\tgit fetch --set-upstream upstream other &&\n> +\tcheck_config master upstream refs/heads/other &&\n> +\tcheck_config_missing other\n> +'\n> +\n> +test_expect_success 'fetch --set-upstream master:other does not set the branch other2' '\n> +\tclear_config other2 &&\n> +\tgit fetch --set-upstream upstream master:other2 &&\n> +\tcheck_config_missing other2\n> +'\n> +\n> +test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with invalid url' '\n> +\t# master explicitly not cleared, we check that it is not touched from previous value\n> +\tclear_config other other2 &&\n> +\ttest_must_fail git fetch --set-upstream http://nosuchdomain.example.com &&\n> +\tcheck_config master upstream refs/heads/other &&\n> +\tcheck_config_missing other &&\n> +\tcheck_config_missing other2\n> +'\n> +\n> +test_expect_success 'fetch --set-upstream with valid URL sets upstream to URL' '\n> +\tclear_config other other2 &&\n> +\turl=\"file://'\"$PWD\"'\" &&\n> +\tgit fetch --set-upstream \"$url\" &&\n> +\tcheck_config master \"$url\" HEAD &&\n> +\tcheck_config_missing other &&\n> +\tcheck_config_missing other2\n> +'\n> +\n> +#tests for pull --set-upstream\n> +\n> +test_expect_success 'setup bare parent pull' '\n> +\tgit remote rm upstream &&\n> +\tensure_fresh_upstream &&\n> +\tgit remote add upstream parent\n> +'\n> +\n> +test_expect_success 'setup commit on master and other pull' '\n> +\ttest_commit three &&\n> +\tgit push --tags upstream master &&\n> +\ttest_commit four &&\n> +\tgit push upstream other\n> +'\n> +\n> +test_expect_success 'pull --set-upstream upstream master sets branch master but not other' '\n> +\tclear_config master other &&\n> +\tgit pull --set-upstream upstream master &&\n> +\tcheck_config master upstream refs/heads/master &&\n> +\tcheck_config_missing other\n> +'\n> +\n> +test_expect_success 'pull --set-upstream master:other2 does not set the branch other2' '\n> +\tclear_config other2 &&\n> +\tgit pull --set-upstream upstream master:other2 &&\n> +\tcheck_config_missing other2\n> +'\n> +\n> +test_expect_success 'pull --set-upstream upstream other sets branch master' '\n> +\tclear_config master other &&\n> +\tgit pull --set-upstream upstream other &&\n> +\tcheck_config master upstream refs/heads/other &&\n> +\tcheck_config_missing other\n> +'\n> +\n> +test_expect_success 'pull --set-upstream upstream tag does not set the tag' '\n> +\tclear_config three &&\n> +\tgit pull --tags --set-upstream upstream three &&\n> +\tcheck_config_missing three\n> +'\n> +\n> +test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with invalid url' '\n> +\t# master explicitly not cleared, we check that it is not touched from previous value\n> +\tclear_config other other2 three &&\n> +\ttest_must_fail git pull --set-upstream http://nosuchdomain.example.com &&\n> +\tcheck_config master upstream refs/heads/other &&\n> +\tcheck_config_missing other &&\n> +\tcheck_config_missing other2 &&\n> +\tcheck_config_missing three\n> +'\n> +\n> +test_expect_success 'pull --set-upstream upstream HEAD sets branch HEAD' '\n> +\tclear_config master other &&\n> +\tgit pull --set-upstream upstream HEAD &&\n> +\tcheck_config master upstream HEAD &&\n> +\tgit checkout other &&\n> +\tgit pull --set-upstream upstream HEAD &&\n> +\tcheck_config other upstream HEAD\n> +'\n> +\n> +test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '\n> +\tclear_config master three &&\n> +\tgit pull --set-upstream upstream master three &&\n> +\tcheck_config_missing master &&\n> +\tcheck_config_missing three\n> +'\n> +\n> +test_expect_success 'pull --set-upstream with valid URL sets upstream to URL' '\n> +\tclear_config master other other2 &&\n> +\tgit checkout master &&\n> +\turl=\"file://'\"$PWD\"'\" &&\n> +\tgit pull --set-upstream \"$url\" &&\n> +\tcheck_config master \"$url\" HEAD &&\n> +\tcheck_config_missing other &&\n> +\tcheck_config_missing other2\n> +'\n> +\n> +test_expect_success 'pull --set-upstream with valid URL and branch sets branch' '\n> +\tclear_config master other other2 &&\n> +\tgit checkout master &&\n> +\turl=\"file://'\"$PWD\"'\" &&\n> +\tgit pull --set-upstream \"$url\" master &&\n> +\tcheck_config master \"$url\" refs/heads/master &&\n> +\tcheck_config_missing other &&\n> +\tcheck_config_missing other2\n> +'\n> +\n> +test_done\n> -- \n> 2.20.1.98.gecbdaf0\n> \n\n-- \nRegards,\nPratyush Yadav\n"},{"id":"380449","messageId":"xmqqlfvv6417.fsf@gitster-ct.c.googlers.com","threadId":"50874","inReplyTo":"20190814134629.21096-1-git@matthieu-moy.fr","subject":"Re: [PATCH] pull, fetch: add --set-upstream option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-08-14T17:38:28Z","receivedAt":"2019-08-14T17:38:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <git@matthieu-moy.fr> writes:\n\n> From: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\n>\n> Add the --set-upstream option to git pull/fetch\n> which lets the user set the upstream configuration\n> (branch.<current-branch-name>.merge and\n> branch.<current-branch-name>.remote) for the current branch.\n>\n> A typical use-case is:\n>\n>     git clone http://example.com/my-public-fork\n>     git remote add main http://example.com/project-main-repo\n>     git pull --set-upstream main master\n>\n> or, instead of the last line:\n>\n>     git fetch --set-upstream main master\n>     git merge # or git rebase\n>\n> This functionality is analog to push --set-upstream.\n\nI was writing a one-paragraph summary for this topic, for the\n\"What's cooking\" report, and here is what I have:\n\n \"git fetch\" learned \"--set-upstream\" option to help those who first\n clone from a forked repository they intend to push to, add the true\n upstream via \"git remote add\" and then \"git fetch\" from it.\n\nAfter describing it like so, I cannot shake the feeling that the\nworkflow this intends to support feels somewhat backwards and\nsuboptimal.\n\n - Unless you rely on server-side \"fork\" like GitHub does, you would\n   first clone from the upstream, and then push to your \"fork\".  The\n   flow whose first step is to clone from your \"fork\", not from the\n   true upstream, feels backwards (cloning from upstream then adding\n   your fork as a secondary may be more natural, without need for\n   the complexity of --set-upstream to pull/fetch/push, no?).\n\n - The second step adds the true upstream using \"git remote\", and at\n   that point, in your mind you are quite clear that you want to\n   pull from there (and push to your own fork).  Not having the \"I\n   am adding this new remote; from now on, it is my upstream\"\n   feature at this step, and instead having to say that with your\n   first \"git pull\", feels backwards.  If this feature were instead\n   added to \"git remote\", then the last step in your example does\n   not even have to say \"main\" (and no need for this new option),\n   does it?\n\n"},{"id":"380688","messageId":"86blwlcylf.fsf@matthieu-moy.fr","threadId":"50874","inReplyTo":"xmqqlfvv6417.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] pull, fetch: add --set-upstream option","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@matthieu-moy.fr","sentAt":"2019-08-19T09:07:40Z","receivedAt":"2019-08-19T09:07:44Z","isPatch":true,"sender":{"key":"matthieu.moy@matthieu-moy.fr","avatar":"https://gravatar.com/avatar/0d82caa87c23154bfb465283a9678b89180ce8b6e07e9da84f9ee111183c7fbc?d=mp&s=160"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Matthieu Moy <git@matthieu-moy.fr> writes:\n>\n>> From: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\n>>\n>> Add the --set-upstream option to git pull/fetch\n>> which lets the user set the upstream configuration\n>> (branch.<current-branch-name>.merge and\n>> branch.<current-branch-name>.remote) for the current branch.\n>>\n>> A typical use-case is:\n>>\n>>     git clone http://example.com/my-public-fork\n>>     git remote add main http://example.com/project-main-repo\n>>     git pull --set-upstream main master\n>>\n>> or, instead of the last line:\n>>\n>>     git fetch --set-upstream main master\n>>     git merge # or git rebase\n>>\n>> This functionality is analog to push --set-upstream.\n>\n> I was writing a one-paragraph summary for this topic, for the\n> \"What's cooking\" report, and here is what I have:\n>\n>  \"git fetch\" learned \"--set-upstream\" option to help those who first\n>  clone from a forked repository they intend to push to, add the true\n>  upstream via \"git remote add\" and then \"git fetch\" from it.\n>\n> After describing it like so, I cannot shake the feeling that the\n> workflow this intends to support feels somewhat backwards and\n> suboptimal.\n>\n>  - Unless you rely on server-side \"fork\" like GitHub does,\n\nNote that these days, this is not a very restrictive statement ;-).\n\nAnd when you make a fork on GitHub or GitLab from the web UI, the next\nthing you see is the page of your fork with a button \"clone or download\"\npointing to your local copy's URL. So even though there are arguments to\nclone upstream first, it's also quite natural from the UI point of view\nto clone the local copy, and add upstream when needed.\n\n>    you would first clone from the upstream, and then push to your\n>    \"fork\". The flow whose first step is to clone from your \"fork\", not\n>    from the true upstream, feels backwards (cloning from upstream then\n>    adding your fork as a secondary may be more natural, without need for\n>    the complexity of --set-upstream to pull/fetch/push, no?).\n\nTo me, it depends on the involvement in the project. If I plan to send\nseveral contributions to a project, I'd usually clone the upstream and\nadd my fork. But I also often do:\n\n- Find a project on GitHub/GitLab/...\n- Think about a minor contribution I can make\n- Fork from the web UI\n- clone my fork\n- code, commit, push\n- make a PR\n\nOnly if my PR takes time to get accepted, I'll add upstream as a remote\nand pull from there to rebase my PR.\n\n>  - The second step adds the true upstream using \"git remote\", and at\n>    that point, in your mind you are quite clear that you want to\n>    pull from there (and push to your own fork).  Not having the \"I\n>    am adding this new remote; from now on, it is my upstream\"\n\nNote that you can also group \"remote add\" and \"pull\" by saying just\n\n  git pull --set-upstream http://example.com/project-main-repo master\n\n(I still tend to prefer the \"remote add\" + \"pull\" flow to name the\nremote, though).\n\n>    feature at this step, and instead having to say that with your\n>    first \"git pull\", feels backwards.  If this feature were instead\n>    added to \"git remote\", then the last step in your example does\n>    not even have to say \"main\" (and no need for this new option),\n>    does it?\n\nThere's already \"git remote add --track <branch> <remote> <url>\", but it\ndoes something different: it does not set the upstream information but\nonly sets the glob refspec to fetch only one branch from the remote.\n\nWe could add a new option like\n\n  git remote --set-upstream <branch> <remote> <url>\n\nThat would do\n\n  git remote add <remote> <url>\n  git branch --set-upstream-to=<branch>\n\nThat wouldn't make the commands really easier to type IMHO, as you would\nstill have to pull at some point, so it's:\n\n  git remote add main http://example.com/project-main-repo\n  git pull --set-upstream main master\n  \nVs\n\n  git remote add --set-upstream master main http://example.com/project-main-repo\n  git pull\n\nThe second is a bit shorter (saves the second instance of \"master\"), but\nI tend to prefer the first to avoid the overly long \"git remote add\"\ncommand.\n\nAlso, if one has several local branches, one may run just one \"git\nremote add\" and several \"git pull --set-upstream\".\n\nNote that there are other possible use-cases, like \"upstream was using a\nflow where 'master' was the main branch, but now commits to 'develop'\nbranch and only merges to 'master' for releases\", where you can just\n\n  git pull --set-upstream origin develop\n\nActually, since \"--set-upstream\" means \"next time, *pull* from this\nbranch\", it felt weird to have it in \"git *push*\" and not in \"git pull\".\nCertainly, not having \"git pull --set-upstream\" it \"git pull\" wasn't\nthat much bothering (otherwise, someone would have implemented it long\nago), but I still find it a nice-to-have shortcut.\n\nCheers,\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"380689","messageId":"86a7c5cykq.fsf@matthieu-moy.fr","threadId":"50874","inReplyTo":"20190814171404.zqtd4xctjobgpzby@localhost.localdomain","subject":"Re: [PATCH] pull, fetch: add --set-upstream option","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@matthieu-moy.fr","sentAt":"2019-08-19T09:08:05Z","receivedAt":"2019-08-19T09:08:08Z","isPatch":true,"sender":{"key":"matthieu.moy@matthieu-moy.fr","avatar":"https://gravatar.com/avatar/0d82caa87c23154bfb465283a9678b89180ce8b6e07e9da84f9ee111183c7fbc?d=mp&s=160"},"body":"Pratyush Yadav <me@yadavpratyush.com> writes:\n\n> This is not really a review. Just some minor nitpicks I spotted while \n> reading through.\n\nThanks for the comments.\n\n>> -static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n>> +static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative, set_upstream;\n>\n> This line is getting pretty long. I think it is a good idea to split it \n> into two.\n\nIndeed, and it was already >80 characters, I've split it.\n\n>> +\tif (set_upstream) {\n>> +\t\tstruct branch *branch = branch_get(\"HEAD\");\n>> +\t\tstruct ref *rm;\n>> +\t\tstruct ref *source_ref = NULL;\n>> +\n>> +\t\t/*\n>> +\t\t * We're setting the upstream configuration for the current branch. The\n>> +\t\t * relevent upstream is the fetched branch that is meant to be merged with\n>> +\t\t * the current one, i.e. the one fetched to FETCH_HEAD.\n>> +\t\t *\n>> +\t\t * When there are several such branches, consider the request ambiguous and\n>> +\t\t * err on the safe side by doing nothing and just emit a warning.\n>> +\t\t */\n>\n> The comment lines cross the 80 column boundary. The usual convention in \n> this project is to try to keep lines below 80 columns. For strings IMO \n> an exception can be allowed because breaking them up makes it harder to \n> grep for them. But comments are the easiest to format.\n>\n> Are you using a tab size of 4?\n\nI'm not, but it's possible that the original authors had. Anyway, I've\nwrapped it.\n\n>> +\t\tfor (rm = ref_map; rm; rm = rm->next) {\n>> +\t\t\tif (!rm->peer_ref) {\n>> +\t\t\t\tif (source_ref) {\n>> +\t\t\t\t\twarning(_(\"multiple branch detected, incompatible with --set-upstream\"));\n>> +\t\t\t\t\tgoto skip;\n>> +\t\t\t\t} else {\n>> +\t\t\t\t\tsource_ref = rm;\n>> +\t\t\t\t}\n>> +\t\t\t}\n>> +\t\t}\n>> +\t\tif (source_ref) {\n>> +\t\t\tif (!strcmp(source_ref->name, \"HEAD\") || \n>\n> This line has a trailing space.\n\nFixed.\n\n> So this should change to something like:\n>\n> \t\t\t\tinstall_branch_config(0, branch->name,\n> \t\t\t\t\t\t      transport->remote->name,\n> \t\t\t\t\t\t      source_ref->name);\n\nI've added a newline after the comma, I don't like mixing \"several\narguments on the same line\" and \"one argument per line\".\n\n>  \n> Maybe this discrepancy is because you are using the wrong tab size?\n>\n>> +\t\t\t} else if (starts_with(source_ref->name, \"refs/remotes/\")) {\n>> +\t\t\t\twarning(_(\"not setting upstream for a remote remote-tracking branch\"));\n>> +\t\t\t} else if (starts_with(source_ref->name, \"refs/tags/\")) {\n>> +\t\t\t\twarning(_(\"not setting upstream for a remote tag\"));\n>> +\t\t\t} else {\n>> +\t\t\t\twarning(_(\"unknown branch type\"));\n>> +\t\t\t}\n>\n> No need to wrap single line if statements in braces.\n\nFixed.\n\n>> +#tests for fetch --set-upstream\n>\n> Add a space after the '#'. Same in other comments below.\n\nFixed.\n\nThanks. Version 2 fixing all these follows.\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"},{"id":"380690","messageId":"20190819091120.15862-1-git@matthieu-moy.fr","threadId":"50874","inReplyTo":"86a7c5cykq.fsf@matthieu-moy.fr","subject":"[PATCH v2] pull, fetch: add --set-upstream option","fromName":"Matthieu Moy","fromEmail":"git@matthieu-moy.fr","sentAt":"2019-08-19T09:11:20Z","receivedAt":"2019-08-19T09:11:45Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"From: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\n\nAdd the --set-upstream option to git pull/fetch\nwhich lets the user set the upstream configuration\n(branch.<current-branch-name>.merge and\nbranch.<current-branch-name>.remote) for the current branch.\n\nA typical use-case is:\n\n    git clone http://example.com/my-public-fork\n    git remote add main http://example.com/project-main-repo\n    git pull --set-upstream main master\n\nor, instead of the last line:\n\n    git fetch --set-upstream main master\n    git merge # or git rebase\n\nThis is mostly equivalent to cloning project-main-repo (which sets\nupsteam) and then \"git remote add\" my-public-fork, but may feel more\nnatural for people using a hosting system which allows forking from\nthe web UI.\n\nThis functionality is analog to \"git push --set-upstream\".\n\nSigned-off-by: Corentin BOMPARD <corentin.bompard@etu.univ-lyon1.fr>\nSigned-off-by: Nathan BERBEZIER <nathan.berbezier@etu.univ-lyon1.fr>\nSigned-off-by: Pablo CHABANNE <pablo.chabanne@etu.univ-lyon1.fr>\nSigned-off-by: Matthieu Moy <git@matthieu-moy.fr>\nPatch-edited-by: Matthieu Moy <git@matthieu-moy.fr>\n---\nOnly style fixes and slightly improved commit message since v1.\nInterdiff below:\n\n  diff --git a/builtin/fetch.c b/builtin/fetch.c\n  index 5557ae1c04..54d6b01892 100644\n  --- a/builtin/fetch.c\n  +++ b/builtin/fetch.c\n  @@ -51,7 +51,8 @@ static int fetch_prune_tags_config = -1; /* unspecified */\n   static int prune_tags = -1; /* unspecified */\n   #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n   \n  -static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative, set_upstream;\n  +static int all, append, dry_run, force, keep, multiple, update_head_ok;\n  +static int verbosity, deepen_relative, set_upstream;\n   static int progress = -1;\n   static int enable_auto_gc = 1;\n   static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n  @@ -1377,12 +1378,14 @@ static int do_fetch(struct transport *transport,\n   \t\tstruct ref *source_ref = NULL;\n   \n   \t\t/*\n  -\t\t * We're setting the upstream configuration for the current branch. The\n  -\t\t * relevent upstream is the fetched branch that is meant to be merged with\n  -\t\t * the current one, i.e. the one fetched to FETCH_HEAD.\n  +\t\t * We're setting the upstream configuration for the\n  +\t\t * current branch. The relevent upstream is the\n  +\t\t * fetched branch that is meant to be merged with the\n  +\t\t * current one, i.e. the one fetched to FETCH_HEAD.\n   \t\t *\n  -\t\t * When there are several such branches, consider the request ambiguous and\n  -\t\t * err on the safe side by doing nothing and just emit a warning.\n  +\t\t * When there are several such branches, consider the\n  +\t\t * request ambiguous and err on the safe side by doing\n  +\t\t * nothing and just emit a warning.\n   \t\t */\n   \t\tfor (rm = ref_map; rm; rm = rm->next) {\n   \t\t\tif (!rm->peer_ref) {\n  @@ -1396,17 +1399,17 @@ static int do_fetch(struct transport *transport,\n   \t\t}\n   \t\tif (source_ref) {\n   \t\t\tif (!strcmp(source_ref->name, \"HEAD\") ||\n  -\t\t\t\tstarts_with(source_ref->name, \"refs/heads/\")) {\n  -\t\t\t\tinstall_branch_config(0, branch->name,\n  +\t\t\t    starts_with(source_ref->name, \"refs/heads/\"))\n  +\t\t\t\tinstall_branch_config(0,\n  +\t\t\t\t\t\t      branch->name,\n   \t\t\t\t\t\t      transport->remote->name,\n   \t\t\t\t\t\t      source_ref->name);\n  -\t\t\t} else if (starts_with(source_ref->name, \"refs/remotes/\")) {\n  +\t\t\telse if (starts_with(source_ref->name, \"refs/remotes/\"))\n   \t\t\t\twarning(_(\"not setting upstream for a remote remote-tracking branch\"));\n  -\t\t\t} else if (starts_with(source_ref->name, \"refs/tags/\")) {\n  +\t\t\telse if (starts_with(source_ref->name, \"refs/tags/\"))\n   \t\t\t\twarning(_(\"not setting upstream for a remote tag\"));\n  -\t\t\t} else {\n  +\t\t\telse\n   \t\t\t\twarning(_(\"unknown branch type\"));\n  -\t\t\t}\n   \t\t} else {\n   \t\t\twarning(_(\"no source branch found.\\n\"\n   \t\t\t\t\"you need to specify exactly one branch with the --set-upstream option.\"));\n  \n\n Documentation/fetch-options.txt |   7 ++\n builtin/fetch.c                 |  51 ++++++++-\n builtin/pull.c                  |   6 ++\n t/t5553-set-upstream.sh         | 178 ++++++++++++++++++++++++++++++++\n 4 files changed, 241 insertions(+), 1 deletion(-)\n create mode 100755 t/t5553-set-upstream.sh\n\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 3c9b4f9e09..99df1f3d4e 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -169,6 +169,13 @@ ifndef::git-pull[]\n \tDisable recursive fetching of submodules (this has the same effect as\n \tusing the `--recurse-submodules=no` option).\n \n+--set-upstream::\n+\tIf the remote is fetched successfully, pull and add upstream\n+\t(tracking) reference, used by argument-less\n+\tlinkgit:git-pull[1] and other commands. For more information,\n+\tsee `branch.<name>.merge` and `branch.<name>.remote` in\n+\tlinkgit:git-config[1].\n+\n --submodule-prefix=<path>::\n \tPrepend <path> to paths printed in informative messages\n \tsuch as \"Fetching submodule foo\".  This option is used\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 717dd14e89..54d6b01892 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -23,6 +23,7 @@\n #include \"packfile.h\"\n #include \"list-objects-filter-options.h\"\n #include \"commit-reach.h\"\n+#include \"branch.h\"\n \n #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)\n \n@@ -50,7 +51,8 @@ static int fetch_prune_tags_config = -1; /* unspecified */\n static int prune_tags = -1; /* unspecified */\n #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */\n \n-static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;\n+static int all, append, dry_run, force, keep, multiple, update_head_ok;\n+static int verbosity, deepen_relative, set_upstream;\n static int progress = -1;\n static int enable_auto_gc = 1;\n static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;\n@@ -123,6 +125,8 @@ static struct option builtin_fetch_options[] = {\n \tOPT__VERBOSITY(&verbosity),\n \tOPT_BOOL(0, \"all\", &all,\n \t\t N_(\"fetch from all remotes\")),\n+\tOPT_BOOL(0, \"set-upstream\", &set_upstream,\n+\t\t N_(\"set upstream for git pull/fetch\")),\n \tOPT_BOOL('a', \"append\", &append,\n \t\t N_(\"append to .git/FETCH_HEAD instead of overwriting\")),\n \tOPT_STRING(0, \"upload-pack\", &upload_pack, N_(\"path\"),\n@@ -1367,6 +1371,51 @@ static int do_fetch(struct transport *transport,\n \t\tretcode = 1;\n \t\tgoto cleanup;\n \t}\n+\n+\tif (set_upstream) {\n+\t\tstruct branch *branch = branch_get(\"HEAD\");\n+\t\tstruct ref *rm;\n+\t\tstruct ref *source_ref = NULL;\n+\n+\t\t/*\n+\t\t * We're setting the upstream configuration for the\n+\t\t * current branch. The relevent upstream is the\n+\t\t * fetched branch that is meant to be merged with the\n+\t\t * current one, i.e. the one fetched to FETCH_HEAD.\n+\t\t *\n+\t\t * When there are several such branches, consider the\n+\t\t * request ambiguous and err on the safe side by doing\n+\t\t * nothing and just emit a warning.\n+\t\t */\n+\t\tfor (rm = ref_map; rm; rm = rm->next) {\n+\t\t\tif (!rm->peer_ref) {\n+\t\t\t\tif (source_ref) {\n+\t\t\t\t\twarning(_(\"multiple branch detected, incompatible with --set-upstream\"));\n+\t\t\t\t\tgoto skip;\n+\t\t\t\t} else {\n+\t\t\t\t\tsource_ref = rm;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\tif (source_ref) {\n+\t\t\tif (!strcmp(source_ref->name, \"HEAD\") ||\n+\t\t\t    starts_with(source_ref->name, \"refs/heads/\"))\n+\t\t\t\tinstall_branch_config(0,\n+\t\t\t\t\t\t      branch->name,\n+\t\t\t\t\t\t      transport->remote->name,\n+\t\t\t\t\t\t      source_ref->name);\n+\t\t\telse if (starts_with(source_ref->name, \"refs/remotes/\"))\n+\t\t\t\twarning(_(\"not setting upstream for a remote remote-tracking branch\"));\n+\t\t\telse if (starts_with(source_ref->name, \"refs/tags/\"))\n+\t\t\t\twarning(_(\"not setting upstream for a remote tag\"));\n+\t\t\telse\n+\t\t\t\twarning(_(\"unknown branch type\"));\n+\t\t} else {\n+\t\t\twarning(_(\"no source branch found.\\n\"\n+\t\t\t\t\"you need to specify exactly one branch with the --set-upstream option.\"));\n+\t\t}\n+\t}\n+ skip:\n \tfree_refs(ref_map);\n \n \t/* if neither --no-tags nor --tags was specified, do automated tag\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex f1eaf6e6ed..d25ff13a60 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -129,6 +129,7 @@ static char *opt_refmap;\n static char *opt_ipv4;\n static char *opt_ipv6;\n static int opt_show_forced_updates = -1;\n+static char *set_upstream;\n \n static struct option pull_options[] = {\n \t/* Shared options */\n@@ -243,6 +244,9 @@ static struct option pull_options[] = {\n \t\tPARSE_OPT_NOARG),\n \tOPT_BOOL(0, \"show-forced-updates\", &opt_show_forced_updates,\n \t\t N_(\"check for forced-updates on all updated branches\")),\n+\tOPT_PASSTHRU(0, \"set-upstream\", &set_upstream, NULL,\n+\t\tN_(\"set upstream for git pull/fetch\"),\n+\t\tPARSE_OPT_NOARG),\n \n \tOPT_END()\n };\n@@ -556,6 +560,8 @@ static int run_fetch(const char *repo, const char **refspecs)\n \t\targv_array_push(&args, \"--show-forced-updates\");\n \telse if (opt_show_forced_updates == 0)\n \t\targv_array_push(&args, \"--no-show-forced-updates\");\n+\tif (set_upstream)\n+\t\targv_array_push(&args, set_upstream);\n \n \tif (repo) {\n \t\targv_array_push(&args, repo);\ndiff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh\nnew file mode 100755\nindex 0000000000..81975ad8f9\n--- /dev/null\n+++ b/t/t5553-set-upstream.sh\n@@ -0,0 +1,178 @@\n+#!/bin/sh\n+\n+test_description='\"git fetch/pull --set-upstream\" basic tests.'\n+. ./test-lib.sh\n+\n+check_config () {\n+\tprintf \"%s\\n\" \"$2\" \"$3\" >\"expect.$1\" &&\n+\t{\n+\t\tgit config \"branch.$1.remote\" && git config \"branch.$1.merge\"\n+\t} >\"actual.$1\" &&\n+\ttest_cmp \"expect.$1\" \"actual.$1\"\n+}\n+\n+check_config_missing () {\n+\ttest_expect_code 1 git config \"branch.$1.remote\" &&\n+\ttest_expect_code 1 git config \"branch.$1.merge\"\n+}\n+\n+clear_config () {\n+\tfor branch in \"$@\"; do\n+\t\ttest_might_fail git config --unset-all \"branch.$branch.remote\"\n+\t\ttest_might_fail git config --unset-all \"branch.$branch.merge\"\n+\tdone\n+}\n+\n+ensure_fresh_upstream () {\n+\trm -rf parent && git init --bare parent\n+}\n+\n+test_expect_success 'setup bare parent fetch' '\n+\tensure_fresh_upstream &&\n+\tgit remote add upstream parent\n+'\n+\n+test_expect_success 'setup commit on master and other fetch' '\n+\ttest_commit one &&\n+\tgit push upstream master &&\n+\tgit checkout -b other &&\n+\ttest_commit two &&\n+\tgit push upstream other\n+'\n+\n+# tests for fetch --set-upstream\n+\n+test_expect_success 'fetch --set-upstream does not set upstream w/o branch' '\n+\tclear_config master other &&\n+\tgit checkout master &&\n+\tgit fetch --set-upstream upstream &&\n+\tcheck_config_missing master &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '\n+\tclear_config master other &&\n+\tgit fetch --set-upstream upstream master &&\n+\tcheck_config master upstream refs/heads/master &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'fetch --set-upstream upstream other sets branch other' '\n+\tclear_config master other &&\n+\tgit fetch --set-upstream upstream other &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'fetch --set-upstream master:other does not set the branch other2' '\n+\tclear_config other2 &&\n+\tgit fetch --set-upstream upstream master:other2 &&\n+\tcheck_config_missing other2\n+'\n+\n+test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with invalid url' '\n+\t# master explicitly not cleared, we check that it is not touched from previous value\n+\tclear_config other other2 &&\n+\ttest_must_fail git fetch --set-upstream http://nosuchdomain.example.com &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2\n+'\n+\n+test_expect_success 'fetch --set-upstream with valid URL sets upstream to URL' '\n+\tclear_config other other2 &&\n+\turl=\"file://'\"$PWD\"'\" &&\n+\tgit fetch --set-upstream \"$url\" &&\n+\tcheck_config master \"$url\" HEAD &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2\n+'\n+\n+# tests for pull --set-upstream\n+\n+test_expect_success 'setup bare parent pull' '\n+\tgit remote rm upstream &&\n+\tensure_fresh_upstream &&\n+\tgit remote add upstream parent\n+'\n+\n+test_expect_success 'setup commit on master and other pull' '\n+\ttest_commit three &&\n+\tgit push --tags upstream master &&\n+\ttest_commit four &&\n+\tgit push upstream other\n+'\n+\n+test_expect_success 'pull --set-upstream upstream master sets branch master but not other' '\n+\tclear_config master other &&\n+\tgit pull --set-upstream upstream master &&\n+\tcheck_config master upstream refs/heads/master &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'pull --set-upstream master:other2 does not set the branch other2' '\n+\tclear_config other2 &&\n+\tgit pull --set-upstream upstream master:other2 &&\n+\tcheck_config_missing other2\n+'\n+\n+test_expect_success 'pull --set-upstream upstream other sets branch master' '\n+\tclear_config master other &&\n+\tgit pull --set-upstream upstream other &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_missing other\n+'\n+\n+test_expect_success 'pull --set-upstream upstream tag does not set the tag' '\n+\tclear_config three &&\n+\tgit pull --tags --set-upstream upstream three &&\n+\tcheck_config_missing three\n+'\n+\n+test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with invalid url' '\n+\t# master explicitly not cleared, we check that it is not touched from previous value\n+\tclear_config other other2 three &&\n+\ttest_must_fail git pull --set-upstream http://nosuchdomain.example.com &&\n+\tcheck_config master upstream refs/heads/other &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2 &&\n+\tcheck_config_missing three\n+'\n+\n+test_expect_success 'pull --set-upstream upstream HEAD sets branch HEAD' '\n+\tclear_config master other &&\n+\tgit pull --set-upstream upstream HEAD &&\n+\tcheck_config master upstream HEAD &&\n+\tgit checkout other &&\n+\tgit pull --set-upstream upstream HEAD &&\n+\tcheck_config other upstream HEAD\n+'\n+\n+test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '\n+\tclear_config master three &&\n+\tgit pull --set-upstream upstream master three &&\n+\tcheck_config_missing master &&\n+\tcheck_config_missing three\n+'\n+\n+test_expect_success 'pull --set-upstream with valid URL sets upstream to URL' '\n+\tclear_config master other other2 &&\n+\tgit checkout master &&\n+\turl=\"file://'\"$PWD\"'\" &&\n+\tgit pull --set-upstream \"$url\" &&\n+\tcheck_config master \"$url\" HEAD &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2\n+'\n+\n+test_expect_success 'pull --set-upstream with valid URL and branch sets branch' '\n+\tclear_config master other other2 &&\n+\tgit checkout master &&\n+\turl=\"file://'\"$PWD\"'\" &&\n+\tgit pull --set-upstream \"$url\" master &&\n+\tcheck_config master \"$url\" refs/heads/master &&\n+\tcheck_config_missing other &&\n+\tcheck_config_missing other2\n+'\n+\n+test_done\n-- \n2.20.1.98.gecbdaf0\n\n"},{"id":"380726","messageId":"xmqq1rxgyl99.fsf@gitster-ct.c.googlers.com","threadId":"50874","inReplyTo":"86blwlcylf.fsf@matthieu-moy.fr","subject":"Re: [PATCH] pull, fetch: add --set-upstream option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-08-19T20:04:50Z","receivedAt":"2019-08-19T20:04:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Matthieu Moy <Matthieu.Moy@matthieu-moy.fr> writes:\n\n> To me, it depends on the involvement in the project. If I plan to send\n> several contributions to a project, I'd usually clone the upstream and\n> add my fork. But I also often do:\n>\n> - Find a project on GitHub/GitLab/...\n> - Think about a minor contribution I can make\n> - Fork from the web UI\n> - clone my fork\n> - code, commit, push\n> - make a PR\n>\n> Only if my PR takes time to get accepted, I'll add upstream as a remote\n> and pull from there to rebase my PR.\n\nOK.\n\n>>  - The second step adds the true upstream using \"git remote\", and at\n>>    that point, in your mind you are quite clear that you want to\n>>    pull from there (and push to your own fork).  Not having the \"I\n>>    am adding this new remote; from now on, it is my upstream\"\n>\n> Note that you can also group \"remote add\" and \"pull\" by saying just\n>\n>   git pull --set-upstream http://example.com/project-main-repo master\n>\n> (I still tend to prefer the \"remote add\" + \"pull\" flow to name the\n> remote, though).\n\nI do too, and that's where my \"shouldn't this feature be part of\n'remote add' comes from.\n\n> That wouldn't make the commands really easier to type IMHO, as you would\n> still have to pull at some point, so it's:\n>\n>   git remote add main http://example.com/project-main-repo\n>   git pull --set-upstream main master\n>   \n> Vs\n>\n>   git remote add --set-upstream master main http://example.com/project-main-repo\n>   git pull\n>\n> The second is a bit shorter (saves the second instance of \"master\"), but\n> I tend to prefer the first to avoid the overly long \"git remote add\"\n> command.\n\nI do not particularly care about five extra keystrokes.  The reason\nI prefer the latter more is conceptual clarity of it saying \"I use\n'remote' to set things up, and then use 'pull' to get updated\" (as\nopposed to \"I use 'remote' to set things half-way up, and then use\nthe first 'pull' to finish setting things up and getting updated.\nI should remember that I do not need to give --set-upstream to later\n'pull' I used to get further updates\").\n\n> Actually, since \"--set-upstream\" means \"next time, *pull* from this\n> branch\", it felt weird to have it in \"git *push*\" and not in \"git pull\".\n> Certainly, not having \"git pull --set-upstream\" it \"git pull\" wasn't\n> that much bothering (otherwise, someone would have implemented it long\n> ago), but I still find it a nice-to-have shortcut.\n\nYeah, I do not think 'push --set-upstream' is a great feature,\neither, but since we have it already, I do not mind too much to have\nanother on the 'pull' side.  It just feels that we are piling band\naid for the lack of the right feature in the right command by adding\nit to wrong command(s).\n\n"},{"id":"380790","messageId":"86zhk4b6m2.fsf@matthieu-moy.fr","threadId":"50874","inReplyTo":"xmqq1rxgyl99.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH] pull, fetch: add --set-upstream option","fromName":"Matthieu Moy","fromEmail":"git@matthieu-moy.fr","sentAt":"2019-08-20T08:09:41Z","receivedAt":"2019-08-20T08:09:45Z","isPatch":true,"sender":{"key":"git@matthieu-moy.fr","avatar":"https://avatars.githubusercontent.com/u/14709?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> That wouldn't make the commands really easier to type IMHO, as you would\n>> still have to pull at some point, so it's:\n>>\n>>   git remote add main http://example.com/project-main-repo\n>>   git pull --set-upstream main master\n>>   \n>> Vs\n>>\n>>   git remote add --set-upstream master main http://example.com/project-main-repo\n>>   git pull\n>>\n>> The second is a bit shorter (saves the second instance of \"master\"), but\n>> I tend to prefer the first to avoid the overly long \"git remote add\"\n>> command.\n>\n> I do not particularly care about five extra keystrokes.  The reason\n> I prefer the latter more is conceptual clarity of it saying \"I use\n> 'remote' to set things up, and then use 'pull' to get updated\" (as\n> opposed to \"I use 'remote' to set things half-way up, and then use\n> the first 'pull' to finish setting things up and getting updated.\n> I should remember that I do not need to give --set-upstream to later\n> 'pull' I used to get further updates\").\n\nThat's a good argument to add a similar feature to \"git remote\", and\nit's a good idea for a microproject in the future actually. I admit I\ndidn't consider this possibility before this discussion, thanks.\n\nI think I'll still appreciate having the possibility to \"pull\n--set-upstream\" too:\n\n* \"git remote add\" is ran once for a remote, \"git pull --set-upstream\"\n  can be run several times for several branches.\n\n* In practice, even when \"remote add\" supports \"--set-upstream\", I'll\n  very likely forget it, and by the time I run \"git pull\", it'll be too\n  late to add --set-upstream to my \"remote add\" command.\n\n-- \nMatthieu Moy\nhttps://matthieu-moy.fr/\n"}]}