]> git.ipfire.org Git - thirdparty/git.git/commitdiff
remote: handle negative refspecs in git remote show
authorJacob Keller <jacob.e.keller@intel.com>
Fri, 17 Jun 2022 00:20:31 +0000 (17:20 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 17 Jun 2022 17:03:59 +0000 (10:03 -0700)
By default, the git remote show command will query data from remotes to
show data about what might be done on a future git fetch. This process
currently does not handle negative refspecs. This can be confusing,
because the show command will list refs as if they would be fetched. For
example if the fetch refspec "^refs/heads/pr/*", it still displays the
following:

  * remote jdk19
    Fetch URL: git@github.com:openjdk/jdk19.git
    Push  URL: git@github.com:openjdk/jdk19.git
    HEAD branch: master
    Remote branches:
      master tracked
      pr/1   new (next fetch will store in remotes/jdk19)
      pr/2   new (next fetch will store in remotes/jdk19)
      pr/3   new (next fetch will store in remotes/jdk19)
    Local ref configured for 'git push':
      master pushes to master (fast-forwardable)

Fix this by adding an additional check inside of get_ref_states. If a
ref matches one of the negative refspecs, mark it as skipped instead of
marking it as new or tracked.

With this change, we now report remote branches that are skipped due to
negative refspecs properly:

  * remote jdk19
    Fetch URL: git@github.com:openjdk/jdk19.git
    Push  URL: git@github.com:openjdk/jdk19.git
    HEAD branch: master
    Remote branches:
      master tracked
      pr/1   skipped
      pr/2   skipped
      pr/3   skipped
    Local ref configured for 'git push':
      master pushes to master (fast-forwardable)

By showing the refs as skipped, it helps clarify that these references
won't actually be fetched.

This does not properly handle refs going stale due to a newly added
negative refspec. In addition, git remote prune doesn't handle that
negative refspec case either. Fixing that requires digging into
get_stale_heads and handling the case of a ref which exists on the
remote but is omitted due to a negative refspec locally.

Add a new test case which covers the functionality above, as well as a
new expected failure indicating the poor overlap with stale refs.

Reported-by: Pavel Rappo <pavel.rappo@gmail.com>
Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/remote.c
remote.c
remote.h
t/t5505-remote.sh

index d4b69fe77898977d4b4dead8059c667ee17110cd..d9b8746cb3cb6cf78c8790971ad8f23e6a91d2e3 100644 (file)
@@ -344,12 +344,13 @@ static void read_branches(void)
 
 struct ref_states {
        struct remote *remote;
-       struct string_list new_refs, stale, tracked, heads, push;
+       struct string_list new_refs, skipped, stale, tracked, heads, push;
        int queried;
 };
 
 #define REF_STATES_INIT { \
        .new_refs = STRING_LIST_INIT_DUP, \
+       .skipped = STRING_LIST_INIT_DUP, \
        .stale = STRING_LIST_INIT_DUP, \
        .tracked = STRING_LIST_INIT_DUP, \
        .heads = STRING_LIST_INIT_DUP, \
@@ -368,7 +369,9 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
                                states->remote->fetch.raw[i]);
 
        for (ref = fetch_map; ref; ref = ref->next) {
-               if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
+               if (omit_name_by_refspec(ref->name, &states->remote->fetch))
+                       string_list_append(&states->skipped, abbrev_branch(ref->name));
+               else if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
                        string_list_append(&states->new_refs, abbrev_branch(ref->name));
                else
                        string_list_append(&states->tracked, abbrev_branch(ref->name));
@@ -383,6 +386,7 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
        free_refs(fetch_map);
 
        string_list_sort(&states->new_refs);
+       string_list_sort(&states->skipped);
        string_list_sort(&states->tracked);
        string_list_sort(&states->stale);
 
@@ -941,6 +945,7 @@ static void clear_push_info(void *util, const char *string)
 static void free_remote_ref_states(struct ref_states *states)
 {
        string_list_clear(&states->new_refs, 0);
+       string_list_clear(&states->skipped, 0);
        string_list_clear(&states->stale, 1);
        string_list_clear(&states->tracked, 0);
        string_list_clear(&states->heads, 0);
@@ -1035,6 +1040,8 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data)
                        arg = states->remote->name;
                } else if (string_list_has_string(&states->tracked, name))
                        arg = _(" tracked");
+               else if (string_list_has_string(&states->skipped, name))
+                       arg = _(" skipped");
                else if (string_list_has_string(&states->stale, name))
                        arg = _(" stale (use 'git remote prune' to remove)");
                else
@@ -1308,6 +1315,7 @@ static int show(int argc, const char **argv)
                /* remote branch info */
                info.width = 0;
                for_each_string_list(&info.states.new_refs, add_remote_to_show_info, &info);
+               for_each_string_list(&info.states.skipped, add_remote_to_show_info, &info);
                for_each_string_list(&info.states.tracked, add_remote_to_show_info, &info);
                for_each_string_list(&info.states.stale, add_remote_to_show_info, &info);
                if (info.list.nr)
index 9b9bbfe80ec2ef376b79f71e023471d1a5018eef..63bddc598940fd4f040204bd93ad32ca826acfae 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -850,7 +850,7 @@ static int refspec_match(const struct refspec_item *refspec,
        return !strcmp(refspec->src, name);
 }
 
-static int omit_name_by_refspec(const char *name, struct refspec *rs)
+int omit_name_by_refspec(const char *name, struct refspec *rs)
 {
        int i;
 
index dd4402436f1f2e1bb06d37c932ca8f9e3afcf48a..448675e11259b1cc109af9a7d562930246c164ac 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -247,6 +247,12 @@ int resolve_remote_symref(struct ref *ref, struct ref *list);
  */
 struct ref *ref_remove_duplicates(struct ref *ref_map);
 
+/*
+ * Check whether a name matches any negative refspec in rs. Returns 1 if the
+ * name matches at least one negative refspec, and 0 otherwise.
+ */
+int omit_name_by_refspec(const char *name, struct refspec *rs);
+
 /*
  * Remove all entries in the input list which match any negative refspec in
  * the refspec list.
index fff14e13ed43b385f1ce7c0d01c0bea815c954d5..0aad0eb4d26cfa28cd99bf1d76a3d6ebf5b6297d 100755 (executable)
@@ -302,6 +302,52 @@ test_expect_success 'show' '
        )
 '
 
+cat >expect <<EOF
+* remote origin
+  Fetch URL: $(pwd)/one
+  Push  URL: $(pwd)/one
+  HEAD branch: main
+  Remote branches:
+    main skipped
+    side tracked
+  Local branches configured for 'git pull':
+    ahead merges with remote main
+    main  merges with remote main
+  Local refs configured for 'git push':
+    main pushes to main     (local out of date)
+    main pushes to upstream (create)
+EOF
+
+test_expect_success 'show with negative refspecs' '
+       test_when_finished "git -C test config --unset-all --fixed-value remote.origin.fetch ^refs/heads/main" &&
+       git -C test config --add remote.origin.fetch ^refs/heads/main &&
+       git -C test remote show origin >output &&
+       test_cmp expect output
+'
+
+cat >expect <<EOF
+* remote origin
+  Fetch URL: $(pwd)/one
+  Push  URL: $(pwd)/one
+  HEAD branch: main
+  Remote branches:
+    main new (next fetch will store in remotes/origin)
+    side stale (use 'git remote prune' to remove)
+  Local branches configured for 'git pull':
+    ahead merges with remote main
+    main  merges with remote main
+  Local refs configured for 'git push':
+    main pushes to main     (local out of date)
+    main pushes to upstream (create)
+EOF
+
+test_expect_failure 'show stale with negative refspecs' '
+       test_when_finished "git -C test config --unset-all --fixed-value remote.origin.fetch ^refs/heads/side" &&
+       git -C test config --add remote.origin.fetch ^refs/heads/side &&
+       git -C test remote show origin >output &&
+       test_cmp expect output
+'
+
 cat >test/expect <<EOF
 * remote origin
   Fetch URL: $(pwd)/one