@matheusbsbs / matheus-bagliano / commits / e5dcbfd9ab
Fork 0

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 <jaysoffian@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jay Soffian committed Feb 25, 2009 at 03:32 UTC e5dcbfd9ab7028c464909f26f523b85c1de912a2
2 files changed +207 -24
builtin-remote.c
+185 -16
@@ -21,6 +21,7 @@ static const char * const builtin_remote_usage[] = {
21
22 #define GET_REF_STATES (1<<0)
23 #define GET_HEAD_NAMES (1<<1)
24 +#define GET_PUSH_REF_STATES (1<<2)
25
26 static int verbose;
27
@@ -220,7 +221,7 @@ static void read_branches(void)
221
222 struct ref_states {
223 struct remote *remote;
223 - struct string_list new, stale, tracked, heads;
224 + struct string_list new, stale, tracked, heads, push;
225 int queried;
226 };
227
@@ -275,6 +276,112 @@ static int get_ref_states(const struct ref *remote_refs, struct ref_states *stat
276 return 0;
277 }
278
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 +
385 static int get_head_names(const struct ref *remote_refs, struct ref_states *states)
386 {
387 struct ref *ref, *matches;
@@ -644,12 +751,20 @@ static int rm(int argc, const char **argv)
751 return result;
752 }
753
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 +
761 static void free_remote_ref_states(struct ref_states *states)
762 {
763 string_list_clear(&states->new, 0);
764 string_list_clear(&states->stale, 0);
765 string_list_clear(&states->tracked, 0);
766 string_list_clear(&states->heads, 0);
767 + string_list_clear_func(&states->push, clear_push_info);
768 }
769
770 static int append_ref_to_tracked_list(const char *refname,
@@ -693,9 +808,12 @@ static int get_remote_ref_states(const char *name,
808 get_ref_states(remote_refs, states);
809 if (query & GET_HEAD_NAMES)
810 get_head_names(remote_refs, states);
811 + if (query & GET_PUSH_REF_STATES)
812 + get_push_ref_states(remote_refs, states);
813 } else {
814 for_each_ref(append_ref_to_tracked_list, states);
815 sort_string_list(&states->tracked);
816 + get_push_ref_states_noquery(states);
817 }
818
819 return 0;
@@ -704,7 +822,7 @@ static int get_remote_ref_states(const char *name,
822 struct show_info {
823 struct string_list *list;
824 struct ref_states *states;
707 - int width;
825 + int width, width2;
826 int any_rebase;
827 };
828
@@ -799,6 +917,58 @@ int show_local_info_item(struct string_list_item *item, void *cb_data)
917 return 0;
918 }
919
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 +
972 static int show(int argc, const char **argv)
973 {
974 int no_query = 0, result = 0, query_flag = 0;
@@ -817,7 +987,7 @@ static int show(int argc, const char **argv)
987 return show_all();
988
989 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);
991
992 memset(&states, 0, sizeof(states));
993 memset(&info, 0, sizeof(info));
@@ -867,19 +1037,18 @@ static int show(int argc, const char **argv)
1037 string_list_clear(info.list, 0);
1038
1039 /* 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);
1052
1053 free_remote_ref_states(&states);
1054 }
t/t5505-remote.sh
+22 -8
@@ -141,25 +141,34 @@ cat > test/expect << EOF
141 master new (next fetch will store in remotes/origin)
142 side tracked
143 Local branches configured for 'git pull':
144 + ahead merges with remote master
145 master merges with remote master
146 octopus merges with remote topic-a
147 and with remote topic-b
148 and with remote topic-c
149 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)
153 * remote two
154 URL: ../two
155 HEAD branch (remote HEAD is ambiguous, may be one of the following):
156 another
157 master
158 + Local refs configured for 'git push':
159 + ahead forces to master (fast forwardable)
160 + master pushes to another (up to date)
161 EOF
162
163 test_expect_success 'show' '
164 (cd test &&
165 git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
166 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 &&
172 git branch --track octopus origin/master &&
173 git branch --track rebase origin/master &&
174 git branch -d -r origin/master &&
@@ -170,8 +179,11 @@ test_expect_success 'show' '
179 echo 1 > file &&
180 test_tick &&
181 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 &&
184 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 &&
187 git remote show origin two > output &&
188 git branch -d rebase octopus &&
189 test_cmp expect output)
@@ -184,11 +196,13 @@ cat > test/expect << EOF
196 Remote branches: (status not queried)
197 master
198 side
187 - Local branch configured for 'git pull':
199 + Local branches configured for 'git pull':
200 + ahead merges with remote master
201 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
206 EOF
207
208 test_expect_success 'show -n' '