{"thread":{"id":"50873","subject":"[PATCH] [WIP/RFC] add git pull and git fetch --set-upstream","startedAt":"2019-04-04T12:22:58Z","lastAt":"2019-04-04T12:22:58Z","messageCount":1,"participants":["Corentin BOMPARD"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"373117","messageId":"20190404122226.7266-1-corentin.bompard@etu.univ-lyon1.fr","threadId":"50873","inReplyTo":null,"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-04T12:22:26Z","receivedAt":"2019-04-04T12:22:58Z","isPatch":true,"sender":{"key":"corentin.bompard@etu.univ-lyon1.fr","avatar":"https://avatars.githubusercontent.com/u/23448477?v=4"},"body":"Adding 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 At the moment it works on overall but we'd like to have the\n community feedback about:\n \n * We decide to add the set-upstream as an argument for both git pull\n    and git fetch but we can also implement the set-upstream argument\n    in git fetch or in git pull only.\n\n * We took the ref_map entry which fetches locally to FETCH_HEAD \n    as the new current branch's config because git fetch store the \n    remote-tracking branch into FETCH_HEAD before call git merge.\n \n Or about things we missed and things we could improve.\n \n\n Documentation/fetch-options.txt |   5 ++\n builtin/fetch.c                 |  60 ++++++++++++++++-\n builtin/pull.c                  |   6 ++\n t/t5553-set-upstream.sh         | 141 ++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 211 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..ebe72ea1c 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,61 @@ 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 *target = NULL;\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+\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+\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+\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+\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+\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+\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+\t\t}\n+\t}\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..92350dfb0\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+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 branch other' '\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+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+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+\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+\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+'\n+\n+\n+#tests for pull --set-upstream\n+\n+\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+\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 other' '\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+'\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+\tgit pull --set-upstream upstream master three &&\n+\tcheck_config master upstream HEAD &&\n+\tcheck_config_empty three\n+'\n+\n+test_done\n-- \n2.11.0\n\n"}]}