Skip to content

Commit 2c80a82

Browse files
jacob-kellergitster
authored andcommitted
remote: handle negative refspecs in git remote show
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: [email protected]:openjdk/jdk19.git Push URL: [email protected]: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: [email protected]:openjdk/jdk19.git Push URL: [email protected]: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 <[email protected]> Signed-off-by: Jacob Keller <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 8168d5e commit 2c80a82

File tree

4 files changed

+63
-3
lines changed

4 files changed

+63
-3
lines changed

builtin/remote.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,12 +344,13 @@ static void read_branches(void)
344344

345345
struct ref_states {
346346
struct remote *remote;
347-
struct string_list new_refs, stale, tracked, heads, push;
347+
struct string_list new_refs, skipped, stale, tracked, heads, push;
348348
int queried;
349349
};
350350

351351
#define REF_STATES_INIT { \
352352
.new_refs = STRING_LIST_INIT_DUP, \
353+
.skipped = STRING_LIST_INIT_DUP, \
353354
.stale = STRING_LIST_INIT_DUP, \
354355
.tracked = STRING_LIST_INIT_DUP, \
355356
.heads = STRING_LIST_INIT_DUP, \
@@ -368,7 +369,9 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
368369
states->remote->fetch.raw[i]);
369370

370371
for (ref = fetch_map; ref; ref = ref->next) {
371-
if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
372+
if (omit_name_by_refspec(ref->name, &states->remote->fetch))
373+
string_list_append(&states->skipped, abbrev_branch(ref->name));
374+
else if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
372375
string_list_append(&states->new_refs, abbrev_branch(ref->name));
373376
else
374377
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
383386
free_refs(fetch_map);
384387

385388
string_list_sort(&states->new_refs);
389+
string_list_sort(&states->skipped);
386390
string_list_sort(&states->tracked);
387391
string_list_sort(&states->stale);
388392

@@ -941,6 +945,7 @@ static void clear_push_info(void *util, const char *string)
941945
static void free_remote_ref_states(struct ref_states *states)
942946
{
943947
string_list_clear(&states->new_refs, 0);
948+
string_list_clear(&states->skipped, 0);
944949
string_list_clear(&states->stale, 1);
945950
string_list_clear(&states->tracked, 0);
946951
string_list_clear(&states->heads, 0);
@@ -1035,6 +1040,8 @@ static int show_remote_info_item(struct string_list_item *item, void *cb_data)
10351040
arg = states->remote->name;
10361041
} else if (string_list_has_string(&states->tracked, name))
10371042
arg = _(" tracked");
1043+
else if (string_list_has_string(&states->skipped, name))
1044+
arg = _(" skipped");
10381045
else if (string_list_has_string(&states->stale, name))
10391046
arg = _(" stale (use 'git remote prune' to remove)");
10401047
else
@@ -1308,6 +1315,7 @@ static int show(int argc, const char **argv)
13081315
/* remote branch info */
13091316
info.width = 0;
13101317
for_each_string_list(&info.states.new_refs, add_remote_to_show_info, &info);
1318+
for_each_string_list(&info.states.skipped, add_remote_to_show_info, &info);
13111319
for_each_string_list(&info.states.tracked, add_remote_to_show_info, &info);
13121320
for_each_string_list(&info.states.stale, add_remote_to_show_info, &info);
13131321
if (info.list.nr)

remote.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ static int refspec_match(const struct refspec_item *refspec,
850850
return !strcmp(refspec->src, name);
851851
}
852852

853-
static int omit_name_by_refspec(const char *name, struct refspec *rs)
853+
int omit_name_by_refspec(const char *name, struct refspec *rs)
854854
{
855855
int i;
856856

remote.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,12 @@ int resolve_remote_symref(struct ref *ref, struct ref *list);
247247
*/
248248
struct ref *ref_remove_duplicates(struct ref *ref_map);
249249

250+
/*
251+
* Check whether a name matches any negative refspec in rs. Returns 1 if the
252+
* name matches at least one negative refspec, and 0 otherwise.
253+
*/
254+
int omit_name_by_refspec(const char *name, struct refspec *rs);
255+
250256
/*
251257
* Remove all entries in the input list which match any negative refspec in
252258
* the refspec list.

t/t5505-remote.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,52 @@ test_expect_success 'show' '
302302
)
303303
'
304304

305+
cat >expect <<EOF
306+
* remote origin
307+
Fetch URL: $(pwd)/one
308+
Push URL: $(pwd)/one
309+
HEAD branch: main
310+
Remote branches:
311+
main skipped
312+
side tracked
313+
Local branches configured for 'git pull':
314+
ahead merges with remote main
315+
main merges with remote main
316+
Local refs configured for 'git push':
317+
main pushes to main (local out of date)
318+
main pushes to upstream (create)
319+
EOF
320+
321+
test_expect_success 'show with negative refspecs' '
322+
test_when_finished "git -C test config --unset-all --fixed-value remote.origin.fetch ^refs/heads/main" &&
323+
git -C test config --add remote.origin.fetch ^refs/heads/main &&
324+
git -C test remote show origin >output &&
325+
test_cmp expect output
326+
'
327+
328+
cat >expect <<EOF
329+
* remote origin
330+
Fetch URL: $(pwd)/one
331+
Push URL: $(pwd)/one
332+
HEAD branch: main
333+
Remote branches:
334+
main new (next fetch will store in remotes/origin)
335+
side stale (use 'git remote prune' to remove)
336+
Local branches configured for 'git pull':
337+
ahead merges with remote main
338+
main merges with remote main
339+
Local refs configured for 'git push':
340+
main pushes to main (local out of date)
341+
main pushes to upstream (create)
342+
EOF
343+
344+
test_expect_failure 'show stale with negative refspecs' '
345+
test_when_finished "git -C test config --unset-all --fixed-value remote.origin.fetch ^refs/heads/side" &&
346+
git -C test config --add remote.origin.fetch ^refs/heads/side &&
347+
git -C test remote show origin >output &&
348+
test_cmp expect output
349+
'
350+
305351
cat >test/expect <<EOF
306352
* remote origin
307353
Fetch URL: $(pwd)/one

0 commit comments

Comments
 (0)