commit db6d8ad82e16cafbf76d8b787390a449a357a068 from: Stefan Sperling date: Sat Mar 21 19:40:36 2020 UTC prevent existing tags from being overwritten by 'got fetch' by default commit - 6338a6a13e7e3c9768dfd2b2093150dfa4a83029 commit + db6d8ad82e16cafbf76d8b787390a449a357a068 blob - a9c53ae132abc93ff9fc4fcabe4b86fdee16f8d9 blob + e6c45980dc1fc0ae9b61801881ee230b26218854 --- got/got.1 +++ got/got.1 @@ -275,7 +275,7 @@ The maximum is 3. .It Cm cl Short alias for .Cm clone . -.It Cm fetch Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl d Oc Oo Fl l Oc Oo Fl r Ar repository-path Oc Oo Fl q Oc Oo Fl v Oc Op Ar remote-repository +.It Cm fetch Oo Fl a Oc Oo Fl b Ar branch Oc Oo Fl d Oc Oo Fl l Oc Oo Fl r Ar repository-path Oc Oo Fl t Oc Oo Fl q Oc Oo Fl v Oc Op Ar remote-repository Fetch new changes from a remote repository. If no .Ar remote-repository @@ -309,10 +309,10 @@ If those branches contained local commits, these commi reachable via a reference and will therefore be at risk of being discarded by Git's garbage collector. .Pp -In any case, existing references in the +In any case, references in the .Dq refs/tags/ -namespace will always be changed to match tags contained in the remote -repository. +namespace will always be fetched and mapped directly to local references +in the same namespace. .Pp The options for .Cm got fetch @@ -350,6 +350,11 @@ Cannot be used together with any of the other options .Fl v and .Fl r . +.It Fl t +Allow existing references in the +.Dq refs/tags +namespace to be updated if they have changed on the server. +If not specified, only new tag references will be created. .It Fl r Ar repository-path Use the repository at the specified path. If not specified, assume the repository is located at or above the current blob - b62a8f059984b904f52333190a6f2e03f1fb5d26 blob + 989af2eb06a657a2eef3af8bf6402edafbf95c9c --- got/got.c +++ got/got.c @@ -1322,7 +1322,7 @@ done: static const struct got_error * update_ref(struct got_reference *ref, struct got_object_id *new_id, - int verbosity, struct got_repository *repo) + int replace_tags, int verbosity, struct got_repository *repo) { const struct got_error *err = NULL; char *new_id_str = NULL; @@ -1332,6 +1332,15 @@ update_ref(struct got_reference *ref, struct got_objec if (err) goto done; + if (!replace_tags && + strncmp(got_ref_get_name(ref), "refs/tags/", 10) == 0) { + if (verbosity >= 0) { + printf("Rejecting update of existing tag %s: %s\n", + got_ref_get_name(ref), new_id_str); + } + goto done; + } + if (got_ref_is_symbolic(ref)) { struct got_reference *new_ref; err = got_ref_alloc(&new_ref, got_ref_get_name(ref), new_id); @@ -1376,7 +1385,7 @@ __dead static void usage_fetch(void) { fprintf(stderr, "usage: %s fetch [-a] [-b branch] [-d] [-l] " - "[-r repository-path] [-q] [-v] [remote-repository-name]\n", + "[-r repository-path] [-t] [-q] [-v] [remote-repository-name]\n", getprogname()); exit(1); } @@ -1453,13 +1462,13 @@ cmd_fetch(int argc, char *argv[]) pid_t fetchpid = -1; struct got_fetch_progress_arg fpa; int verbosity = 0, fetch_all_branches = 0, list_refs_only = 0; - int delete_refs = 0; + int delete_refs = 0, replace_tags = 0; TAILQ_INIT(&refs); TAILQ_INIT(&symrefs); TAILQ_INIT(&wanted_branches); - while ((ch = getopt(argc, argv, "ab:dlr:vq")) != -1) { + while ((ch = getopt(argc, argv, "ab:dlr:tvq")) != -1) { switch (ch) { case 'a': fetch_all_branches = 1; @@ -1483,6 +1492,9 @@ cmd_fetch(int argc, char *argv[]) optarg); got_path_strip_trailing_slashes(repo_path); break; + case 't': + replace_tags = 1; + break; case 'v': if (verbosity < 0) verbosity = 0; @@ -1660,7 +1672,8 @@ cmd_fetch(int argc, char *argv[]) if (error) goto done; } else { - error = update_ref(ref, id, verbosity, repo); + error = update_ref(ref, id, replace_tags, + verbosity, repo); got_ref_close(ref); if (error) goto done; @@ -1681,7 +1694,8 @@ cmd_fetch(int argc, char *argv[]) if (error) goto done; } else { - error = update_ref(ref, id, verbosity, repo); + error = update_ref(ref, id, replace_tags, + verbosity, repo); got_ref_close(ref); if (error) goto done; blob - bfb411bdc8a3b9b0b921b10871f4d18ef6f44ea5 blob + 374586a754ffabebdadd00bdc20a010dba7fbdd6 --- regress/cmdline/fetch.sh +++ regress/cmdline/fetch.sh @@ -489,10 +489,163 @@ function test_fetch_delete_branch { test_done "$testroot" "$ret" } + +function test_fetch_update_tag { + local testroot=`test_init fetch_update_tag` + local testurl=ssh://127.0.0.1/$testroot + local commit_id=`git_show_head $testroot/repo` + + + got branch -r $testroot/repo -c $commit_id foo + got ref -r $testroot/repo refs/hoo/boo/zoo $commit_id + got tag -r $testroot/repo -c $commit_id -m tag "1.0" >/dev/null + local tag_id=`got ref -r $testroot/repo -l \ + | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2` + + got clone -a -q $testurl/repo $testroot/repo-clone + ret="$?" + if [ "$ret" != "0" ]; then + echo "got clone command failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + echo "modified alpha on master" > $testroot/repo/alpha + git_commit $testroot/repo -m "modified alpha" + local commit_id2=`git_show_head $testroot/repo` + + got ref -r $testroot/repo -d "refs/tags/1.0" >/dev/null + got tag -r $testroot/repo -c $commit_id2 -m tag "1.0" >/dev/null + local tag_id2=`got ref -r $testroot/repo -l \ + | grep "^refs/tags/$tag" | tr -d ' ' | cut -d: -f2` + + got ref -l -r $testroot/repo-clone > $testroot/stdout + + echo "HEAD: refs/heads/master" > $testroot/stdout.expected + echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected + echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected + echo "refs/remotes/origin/foo: $commit_id" \ + >> $testroot/stdout.expected + echo "refs/remotes/origin/master: $commit_id" \ + >> $testroot/stdout.expected + # refs/hoo/boo/zoo is missing because it is outside of refs/heads + echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + got fetch -q -r $testroot/repo-clone + ret="$?" + if [ "$ret" != "0" ]; then + echo "got fetch command failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + got ref -l -r $testroot/repo-clone > $testroot/stdout + + echo "HEAD: refs/heads/master" > $testroot/stdout.expected + echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected + echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected + echo "refs/remotes/origin/foo: $commit_id" >> $testroot/stdout.expected + echo "refs/remotes/origin/master: $commit_id2" \ + >> $testroot/stdout.expected + # refs/hoo/boo/zoo is missing because it is outside of refs/heads + echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected + + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + got fetch -r $testroot/repo-clone 2> $testroot/stderr | \ + tail -n 1 > $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + echo "got fetch command failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + echo "Rejecting update of existing tag refs/tags/1.0: $tag_id2" \ + > $testroot/stdout.expected + + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + got ref -l -r $testroot/repo-clone > $testroot/stdout + + echo "HEAD: refs/heads/master" > $testroot/stdout.expected + echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected + echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected + echo "refs/remotes/origin/foo: $commit_id" >> $testroot/stdout.expected + echo "refs/remotes/origin/master: $commit_id2" \ + >> $testroot/stdout.expected + # refs/hoo/boo/zoo is missing because it is outside of refs/heads + echo "refs/tags/1.0: $tag_id" >> $testroot/stdout.expected + + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + got fetch -q -t -r $testroot/repo-clone > $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + echo "got fetch command failed unexpectedly" >&2 + test_done "$testroot" "$ret" + return 1 + fi + + echo -n > $testroot/stdout.expected + + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + test_done "$testroot" "$ret" + return 1 + fi + + got ref -l -r $testroot/repo-clone > $testroot/stdout + + echo "HEAD: refs/heads/master" > $testroot/stdout.expected + echo "refs/heads/foo: $commit_id" >> $testroot/stdout.expected + echo "refs/heads/master: $commit_id" >> $testroot/stdout.expected + echo "refs/remotes/origin/foo: $commit_id" >> $testroot/stdout.expected + echo "refs/remotes/origin/master: $commit_id2" \ + >> $testroot/stdout.expected + # refs/hoo/boo/zoo is missing because it is outside of refs/heads + echo "refs/tags/1.0: $tag_id2" >> $testroot/stdout.expected + + cmp -s $testroot/stdout $testroot/stdout.expected + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi + test_done "$testroot" "$ret" +} + run_test test_fetch_basic run_test test_fetch_list run_test test_fetch_branch run_test test_fetch_all run_test test_fetch_empty_packfile run_test test_fetch_delete_branch +run_test test_fetch_update_tag