Skip to content

Commit e5dcbfd

Browse files
jaysoffiangitster
authored andcommitted
builtin-remote: new show output style for push refspecs
The existing output of "git remote show <remote>" with respect to push ref specs is basically just to show the raw refspec. This patch teaches the command to interpret the refspecs and show how each branch will be pushed to the destination. The output gives the user an idea of what "git push" should do if it is run w/o any arguments. Example new output: 1a. Typical output with no push refspec (i.e. matching branches only) $ git remote show origin * remote origin [...] Local refs configured for 'git push': master pushes to master (up to date) next pushes to next (local out of date) 1b. Same as above, w/o querying the remote: $ git remote show origin -n * remote origin [...] Local ref configured for 'git push' (status not queried): (matching) pushes to (matching) 2a. With a forcing refspec (+), and a new topic (something like push = refs/heads/*:refs/heads/*): $ git remote show origin * remote origin [...] Local refs configured for 'git push': master pushes to master (fast forwardable) new-topic pushes to new-topic (create) next pushes to next (local out of date) pu forces to pu (up to date) 2b. Same as above, w/o querying the remote $ git remote show origin -n * remote origin [...] Local refs configured for 'git push' (status not queried): master pushes to master new-topic pushes to new-topic next pushes to next pu forces to pu 3. With a remote configured as a mirror: * remote backup [...] Local refs will be mirrored by 'git push' Signed-off-by: Jay Soffian <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7ecbbf8 commit e5dcbfd

File tree

2 files changed

+207
-24
lines changed

2 files changed

+207
-24
lines changed

builtin-remote.c

Lines changed: 185 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ static const char * const builtin_remote_usage[] = {
2121

2222
#define GET_REF_STATES (1<<0)
2323
#define GET_HEAD_NAMES (1<<1)
24+
#define GET_PUSH_REF_STATES (1<<2)
2425

2526
static int verbose;
2627

@@ -220,7 +221,7 @@ static void read_branches(void)
220221

221222
struct ref_states {
222223
struct remote *remote;
223-
struct string_list new, stale, tracked, heads;
224+
struct string_list new, stale, tracked, heads, push;
224225
int queried;
225226
};
226227

@@ -275,6 +276,112 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
275276
return 0;
276277
}
277278

279+
struct push_info {
280+
char *dest;
281+
int forced;
282+
enum {
283+
PUSH_STATUS_CREATE = 0,
284+
PUSH_STATUS_DELETE,
285+
PUSH_STATUS_UPTODATE,
286+
PUSH_STATUS_FASTFORWARD,
287+
PUSH_STATUS_OUTOFDATE,
288+
PUSH_STATUS_NOTQUERIED,
289+
} status;
290+
};
291+
292+
static int get_push_ref_states(const struct ref *remote_refs,
293+
struct ref_states *states)
294+
{
295+
struct remote *remote = states->remote;
296+
struct ref *ref, *local_refs, *push_map, **push_tail;
297+
if (remote->mirror)
298+
return 0;
299+
300+
local_refs = get_local_heads();
301+
ref = push_map = copy_ref_list(remote_refs);
302+
while (ref->next)
303+
ref = ref->next;
304+
push_tail = &ref->next;
305+
306+
match_refs(local_refs, push_map, &push_tail, remote->push_refspec_nr,
307+
remote->push_refspec, MATCH_REFS_NONE);
308+
309+
states->push.strdup_strings = 1;
310+
for (ref = push_map; ref; ref = ref->next) {
311+
struct string_list_item *item;
312+
struct push_info *info;
313+
314+
if (!ref->peer_ref)
315+
continue;
316+
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
317+
318+
item = string_list_append(abbrev_branch(ref->peer_ref->name),
319+
&states->push);
320+
item->util = xcalloc(sizeof(struct push_info), 1);
321+
info = item->util;
322+
info->forced = ref->force;
323+
info->dest = xstrdup(abbrev_branch(ref->name));
324+
325+
if (is_null_sha1(ref->new_sha1)) {
326+
info->status = PUSH_STATUS_DELETE;
327+
} else if (!hashcmp(ref->old_sha1, ref->new_sha1))
328+
info->status = PUSH_STATUS_UPTODATE;
329+
else if (is_null_sha1(ref->old_sha1))
330+
info->status = PUSH_STATUS_CREATE;
331+
else if (has_sha1_file(ref->old_sha1) &&
332+
ref_newer(ref->new_sha1, ref->old_sha1))
333+
info->status = PUSH_STATUS_FASTFORWARD;
334+
else
335+
info->status = PUSH_STATUS_OUTOFDATE;
336+
// ref->peer_ref = NULL; /* local ref which is freed below */
337+
}
338+
free_refs(local_refs);
339+
free_refs(push_map);
340+
return 0;
341+
}
342+
343+
static int get_push_ref_states_noquery(struct ref_states *states)
344+
{
345+
int i;
346+
struct remote *remote = states->remote;
347+
struct string_list_item *item;
348+
struct push_info *info;
349+
350+
if (remote->mirror)
351+
return 0;
352+
353+
states->push.strdup_strings = 1;
354+
if (!remote->push_refspec_nr) {
355+
item = string_list_append("(matching)", &states->push);
356+
info = item->util = xcalloc(sizeof(struct push_info), 1);
357+
info->status = PUSH_STATUS_NOTQUERIED;
358+
info->dest = xstrdup(item->string);
359+
}
360+
for (i = 0; i < remote->push_refspec_nr; i++) {
361+
struct refspec *spec = remote->push + i;
362+
char buf[PATH_MAX];
363+
if (spec->matching)
364+
item = string_list_append("(matching)", &states->push);
365+
else if (spec->pattern) {
366+
snprintf(buf, (sizeof(buf)), "%s*", spec->src);
367+
item = string_list_append(buf, &states->push);
368+
snprintf(buf, (sizeof(buf)), "%s*", spec->dst);
369+
} else if (strlen(spec->src))
370+
item = string_list_append(spec->src, &states->push);
371+
else
372+
item = string_list_append("(delete)", &states->push);
373+
374+
info = item->util = xcalloc(sizeof(struct push_info), 1);
375+
info->forced = spec->force;
376+
info->status = PUSH_STATUS_NOTQUERIED;
377+
if (spec->pattern)
378+
info->dest = xstrdup(buf);
379+
else
380+
info->dest = xstrdup(spec->dst ? spec->dst : item->string);
381+
}
382+
return 0;
383+
}
384+
278385
static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
279386
{
280387
struct ref *ref, *matches;
@@ -644,12 +751,20 @@ static int rm(int argc, const char **argv)
644751
return result;
645752
}
646753

754+
void clear_push_info(void *util, const char *string)
755+
{
756+
struct push_info *info = util;
757+
free(info->dest);
758+
free(info);
759+
}
760+
647761
static void free_remote_ref_states(struct ref_states *states)
648762
{
649763
string_list_clear(&states->new, 0);
650764
string_list_clear(&states->stale, 0);
651765
string_list_clear(&states->tracked, 0);
652766
string_list_clear(&states->heads, 0);
767+
string_list_clear_func(&states->push, clear_push_info);
653768
}
654769

655770
static int append_ref_to_tracked_list(const char *refname,
@@ -693,9 +808,12 @@ static int get_remote_ref_states(const char *name,
693808
get_ref_states(remote_refs, states);
694809
if (query & GET_HEAD_NAMES)
695810
get_head_names(remote_refs, states);
811+
if (query & GET_PUSH_REF_STATES)
812+
get_push_ref_states(remote_refs, states);
696813
} else {
697814
for_each_ref(append_ref_to_tracked_list, states);
698815
sort_string_list(&states->tracked);
816+
get_push_ref_states_noquery(states);
699817
}
700818

701819
return 0;
@@ -704,7 +822,7 @@ static int get_remote_ref_states(const char *name,
704822
struct show_info {
705823
struct string_list *list;
706824
struct ref_states *states;
707-
int width;
825+
int width, width2;
708826
int any_rebase;
709827
};
710828

@@ -799,6 +917,58 @@ int show_local_info_item(struct string_list_item *item, void *cb_data)
799917
return 0;
800918
}
801919

920+
int add_push_to_show_info(struct string_list_item *push_item, void *cb_data)
921+
{
922+
struct show_info *show_info = cb_data;
923+
struct push_info *push_info = push_item->util;
924+
struct string_list_item *item;
925+
int n;
926+
if ((n = strlen(push_item->string)) > show_info->width)
927+
show_info->width = n;
928+
if ((n = strlen(push_info->dest)) > show_info->width2)
929+
show_info->width2 = n;
930+
item = string_list_append(push_item->string, show_info->list);
931+
item->util = push_item->util;
932+
return 0;
933+
}
934+
935+
int show_push_info_item(struct string_list_item *item, void *cb_data)
936+
{
937+
struct show_info *show_info = cb_data;
938+
struct push_info *push_info = item->util;
939+
char *src = item->string, *status = NULL;
940+
941+
switch (push_info->status) {
942+
case PUSH_STATUS_CREATE:
943+
status = "create";
944+
break;
945+
case PUSH_STATUS_DELETE:
946+
status = "delete";
947+
src = "(none)";
948+
break;
949+
case PUSH_STATUS_UPTODATE:
950+
status = "up to date";
951+
break;
952+
case PUSH_STATUS_FASTFORWARD:
953+
status = "fast forwardable";
954+
break;
955+
case PUSH_STATUS_OUTOFDATE:
956+
status = "local out of date";
957+
break;
958+
case PUSH_STATUS_NOTQUERIED:
959+
break;
960+
}
961+
if (status)
962+
printf(" %-*s %s to %-*s (%s)\n", show_info->width, src,
963+
push_info->forced ? "forces" : "pushes",
964+
show_info->width2, push_info->dest, status);
965+
else
966+
printf(" %-*s %s to %s\n", show_info->width, src,
967+
push_info->forced ? "forces" : "pushes",
968+
push_info->dest);
969+
return 0;
970+
}
971+
802972
static int show(int argc, const char **argv)
803973
{
804974
int no_query = 0, result = 0, query_flag = 0;
@@ -817,7 +987,7 @@ static int show(int argc, const char **argv)
817987
return show_all();
818988

819989
if (!no_query)
820-
query_flag = (GET_REF_STATES | GET_HEAD_NAMES);
990+
query_flag = (GET_REF_STATES | GET_HEAD_NAMES | GET_PUSH_REF_STATES);
821991

822992
memset(&states, 0, sizeof(states));
823993
memset(&info, 0, sizeof(info));
@@ -867,19 +1037,18 @@ static int show(int argc, const char **argv)
8671037
string_list_clear(info.list, 0);
8681038

8691039
/* git push info */
870-
if (states.remote->push_refspec_nr) {
871-
printf(" Local branch%s pushed with 'git push'\n",
872-
states.remote->push_refspec_nr > 1 ?
873-
"es" : "");
874-
for (i = 0; i < states.remote->push_refspec_nr; i++) {
875-
struct refspec *spec = states.remote->push + i;
876-
printf(" %s%s%s%s\n",
877-
spec->force ? "+" : "",
878-
abbrev_branch(spec->src),
879-
spec->dst ? ":" : "",
880-
spec->dst ? abbrev_branch(spec->dst) : "");
881-
}
882-
}
1040+
if (states.remote->mirror)
1041+
printf(" Local refs will be mirrored by 'git push'\n");
1042+
1043+
info.width = info.width2 = 0;
1044+
for_each_string_list(add_push_to_show_info, &states.push, &info);
1045+
sort_string_list(info.list);
1046+
if (info.list->nr)
1047+
printf(" Local ref%s configured for 'git push'%s:\n",
1048+
info.list->nr > 1 ? "s" : "",
1049+
no_query ? " (status not queried)" : "");
1050+
for_each_string_list(show_push_info_item, info.list, &info);
1051+
string_list_clear(info.list, 0);
8831052

8841053
free_remote_ref_states(&states);
8851054
}

t/t5505-remote.sh

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,25 +141,34 @@ cat > test/expect << EOF
141141
master new (next fetch will store in remotes/origin)
142142
side tracked
143143
Local branches configured for 'git pull':
144+
ahead merges with remote master
144145
master merges with remote master
145146
octopus merges with remote topic-a
146147
and with remote topic-b
147148
and with remote topic-c
148149
rebase rebases onto remote master
149-
Local branches pushed with 'git push'
150-
master:upstream
151-
+refs/tags/lastbackup
150+
Local refs configured for 'git push':
151+
master pushes to master (local out of date)
152+
master pushes to upstream (create)
152153
* remote two
153154
URL: ../two
154155
HEAD branch (remote HEAD is ambiguous, may be one of the following):
155156
another
156157
master
158+
Local refs configured for 'git push':
159+
ahead forces to master (fast forwardable)
160+
master pushes to another (up to date)
157161
EOF
158162

159163
test_expect_success 'show' '
160164
(cd test &&
161165
git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
162166
git fetch &&
167+
git checkout -b ahead origin/master &&
168+
echo 1 >> file &&
169+
test_tick &&
170+
git commit -m update file &&
171+
git checkout master &&
163172
git branch --track octopus origin/master &&
164173
git branch --track rebase origin/master &&
165174
git branch -d -r origin/master &&
@@ -170,8 +179,11 @@ test_expect_success 'show' '
170179
echo 1 > file &&
171180
test_tick &&
172181
git commit -m update file) &&
173-
git config remote.origin.push refs/heads/master:refs/heads/upstream &&
182+
git config --add remote.origin.push : &&
183+
git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
174184
git config --add remote.origin.push +refs/tags/lastbackup &&
185+
git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
186+
git config --add remote.two.push refs/heads/master:refs/heads/another &&
175187
git remote show origin two > output &&
176188
git branch -d rebase octopus &&
177189
test_cmp expect output)
@@ -184,11 +196,13 @@ cat > test/expect << EOF
184196
Remote branches: (status not queried)
185197
master
186198
side
187-
Local branch configured for 'git pull':
199+
Local branches configured for 'git pull':
200+
ahead merges with remote master
188201
master merges with remote master
189-
Local branches pushed with 'git push'
190-
master:upstream
191-
+refs/tags/lastbackup
202+
Local refs configured for 'git push' (status not queried):
203+
(matching) pushes to (matching)
204+
refs/heads/master pushes to refs/heads/upstream
205+
refs/tags/lastbackup forces to refs/tags/lastbackup
192206
EOF
193207

194208
test_expect_success 'show -n' '

0 commit comments

Comments
 (0)