Skip to content

Commit aedcb7d

Browse files
KarthikNayakgitster
authored andcommitted
branch.c: use 'ref-filter' APIs
Make 'branch.c' use 'ref-filter' APIs for iterating through refs sorting. This removes most of the code used in 'branch.c' replacing it with calls to the 'ref-filter' library. Make 'branch.c' use the 'filter_refs()' function provided by 'ref-filter' to filter out tags based on the options set. We provide a sorting option provided for 'branch.c' by using the sorting options provided by 'ref-filter'. Also by default, we sort by 'refname'. Since 'HEAD' is alphabatically before 'refs/...' we end up with an array consisting of the 'HEAD' ref then the local branches and finally the remote-tracking branches. Also remove the 'ignore' variable from ref_array_item as it was previously used for the '--merged' option and now that is handled by ref-filter. Modify some of the tests in t1430 to check the stderr for a warning regarding the broken ref. This is done as ref-filter throws a warning for broken refs rather than directly printing them. Add tests and documentation for the same. Mentored-by: Christian Couder <[email protected]> Mentored-by: Matthieu Moy <[email protected]> Signed-off-by: Karthik Nayak <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1511b22 commit aedcb7d

File tree

6 files changed

+78
-192
lines changed

6 files changed

+78
-192
lines changed

Documentation/git-branch.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ SYNOPSIS
1111
'git branch' [--color[=<when>] | --no-color] [-r | -a]
1212
[--list] [-v [--abbrev=<length> | --no-abbrev]]
1313
[--column[=<options>] | --no-column]
14-
[(--merged | --no-merged | --contains) [<commit>]] [<pattern>...]
14+
[(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>] [<pattern>...]
1515
'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
1616
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
1717
'git branch' --unset-upstream [<branchname>]
@@ -229,6 +229,16 @@ start-point is either a local or remote-tracking branch.
229229
The new name for an existing branch. The same restrictions as for
230230
<branchname> apply.
231231

232+
--sort=<key>::
233+
Sort based on the key given. Prefix `-` to sort in descending
234+
order of the value. You may use the --sort=<key> option
235+
multiple times, in which case the last key becomes the primary
236+
key. The keys supported are the same as those in `git
237+
for-each-ref`. Sort order defaults to sorting based on the
238+
full refname (including `refs/...` prefix). This lists
239+
detached HEAD (if present) first, then local branches and
240+
finally remote-tracking branches.
241+
232242

233243
Examples
234244
--------

builtin/branch.c

Lines changed: 35 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -270,125 +270,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
270270
return(ret);
271271
}
272272

273-
static char *resolve_symref(const char *src, const char *prefix)
274-
{
275-
unsigned char sha1[20];
276-
int flag;
277-
const char *dst;
278-
279-
dst = resolve_ref_unsafe(src, 0, sha1, &flag);
280-
if (!(dst && (flag & REF_ISSYMREF)))
281-
return NULL;
282-
if (prefix)
283-
skip_prefix(dst, prefix, &dst);
284-
return xstrdup(dst);
285-
}
286-
287-
static int match_patterns(const char **pattern, const char *refname)
288-
{
289-
if (!*pattern)
290-
return 1; /* no pattern always matches */
291-
while (*pattern) {
292-
if (!wildmatch(*pattern, refname, 0, NULL))
293-
return 1;
294-
pattern++;
295-
}
296-
return 0;
297-
}
298-
299-
/*
300-
* Allocate memory for a new ref_array_item and insert that into the
301-
* given ref_array. Doesn't take the objectname unlike
302-
* new_ref_array_item(). This is a temporary function which will be
303-
* removed when we port branch.c to use ref-filter APIs.
304-
*/
305-
static struct ref_array_item *ref_array_append(struct ref_array *array, const char *refname)
306-
{
307-
size_t len = strlen(refname);
308-
struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);
309-
memcpy(ref->refname, refname, len);
310-
ref->refname[len] = '\0';
311-
REALLOC_ARRAY(array->items, array->nr + 1);
312-
array->items[array->nr++] = ref;
313-
return ref;
314-
}
315-
316-
static int append_ref(const char *refname, const struct object_id *oid, int flags, void *cb_data)
317-
{
318-
struct ref_filter_cbdata *cb = (struct ref_filter_cbdata *)(cb_data);
319-
struct ref_filter *filter = cb->filter;
320-
struct ref_array *array = cb->array;
321-
struct ref_array_item *item;
322-
struct commit *commit;
323-
int kind, i;
324-
const char *prefix, *orig_refname = refname;
325-
326-
static struct {
327-
int kind;
328-
const char *prefix;
329-
} ref_kind[] = {
330-
{ FILTER_REFS_BRANCHES, "refs/heads/" },
331-
{ FILTER_REFS_REMOTES, "refs/remotes/" },
332-
};
333-
334-
/* Detect kind */
335-
for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
336-
prefix = ref_kind[i].prefix;
337-
if (skip_prefix(refname, prefix, &refname)) {
338-
kind = ref_kind[i].kind;
339-
break;
340-
}
341-
}
342-
if (ARRAY_SIZE(ref_kind) <= i) {
343-
if (!strcmp(refname, "HEAD"))
344-
kind = FILTER_REFS_DETACHED_HEAD;
345-
else
346-
return 0;
347-
}
348-
349-
/* Don't add types the caller doesn't want */
350-
if ((kind & filter->kind) == 0)
351-
return 0;
352-
353-
if (!match_patterns(filter->name_patterns, refname))
354-
return 0;
355-
356-
commit = NULL;
357-
if (filter->verbose || filter->with_commit || filter->merge != REF_FILTER_MERGED_NONE) {
358-
commit = lookup_commit_reference_gently(oid->hash, 1);
359-
if (!commit)
360-
return 0;
361-
362-
/* Filter with with_commit if specified */
363-
if (!is_descendant_of(commit, filter->with_commit))
364-
return 0;
365-
366-
if (filter->merge != REF_FILTER_MERGED_NONE)
367-
add_pending_object(array->revs,
368-
(struct object *)commit, refname);
369-
}
370-
371-
item = ref_array_append(array, refname);
372-
373-
/* Record the new item */
374-
item->kind = kind;
375-
item->commit = commit;
376-
item->symref = resolve_symref(orig_refname, prefix);
377-
item->ignore = 0;
378-
379-
return 0;
380-
}
381-
382-
static int ref_cmp(const void *r1, const void *r2)
383-
{
384-
struct ref_array_item *c1 = *((struct ref_array_item **)r1);
385-
struct ref_array_item *c2 = *((struct ref_array_item **)r2);
386-
387-
if (c1->kind != c2->kind)
388-
return c1->kind - c2->kind;
389-
return strcmp(c1->refname, c2->refname);
390-
}
391-
392273
static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
393274
int show_upstream_ref)
394275
{
@@ -452,7 +333,7 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
452333
}
453334

454335
static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
455-
struct ref_filter *filter)
336+
struct ref_filter *filter, const char *refname)
456337
{
457338
struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
458339
const char *sub = _(" **** invalid ref ****");
@@ -464,7 +345,7 @@ static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
464345
}
465346

466347
if (item->kind == FILTER_REFS_BRANCHES)
467-
fill_tracking_info(&stat, item->refname, filter->verbose > 1);
348+
fill_tracking_info(&stat, refname, filter->verbose > 1);
468349

469350
strbuf_addf(out, " %s %s%s",
470351
find_unique_abbrev(item->commit->object.sha1, filter->abbrev),
@@ -504,8 +385,8 @@ static char *get_head_description(void)
504385
return strbuf_detach(&desc, NULL);
505386
}
506387

507-
static void print_ref_item(struct ref_array_item *item, int maxwidth,
508-
struct ref_filter *filter, const char *remote_prefix)
388+
static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
389+
struct ref_filter *filter, const char *remote_prefix)
509390
{
510391
char c;
511392
int current = 0;
@@ -515,17 +396,16 @@ static void print_ref_item(struct ref_array_item *item, int maxwidth,
515396
const char *desc = item->refname;
516397
char *to_free = NULL;
517398

518-
if (item->ignore)
519-
return;
520-
521399
switch (item->kind) {
522400
case FILTER_REFS_BRANCHES:
523-
if (!filter->detached && !strcmp(item->refname, head))
401+
skip_prefix(desc, "refs/heads/", &desc);
402+
if (!filter->detached && !strcmp(desc, head))
524403
current = 1;
525404
else
526405
color = BRANCH_COLOR_LOCAL;
527406
break;
528407
case FILTER_REFS_REMOTES:
408+
skip_prefix(desc, "refs/remotes/", &desc);
529409
color = BRANCH_COLOR_REMOTE;
530410
prefix = remote_prefix;
531411
break;
@@ -554,11 +434,13 @@ static void print_ref_item(struct ref_array_item *item, int maxwidth,
554434
strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
555435
name.buf, branch_get_color(BRANCH_COLOR_RESET));
556436

557-
if (item->symref)
558-
strbuf_addf(&out, " -> %s", item->symref);
437+
if (item->symref) {
438+
skip_prefix(item->symref, "refs/remotes/", &desc);
439+
strbuf_addf(&out, " -> %s", desc);
440+
}
559441
else if (filter->verbose)
560442
/* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
561-
add_verbose_info(&out, item, filter);
443+
add_verbose_info(&out, item, filter, desc);
562444
if (column_active(colopts)) {
563445
assert(!filter->verbose && "--column and --verbose are incompatible");
564446
string_list_append(&output, out.buf);
@@ -575,11 +457,13 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
575457
int i, max = 0;
576458
for (i = 0; i < refs->nr; i++) {
577459
struct ref_array_item *it = refs->items[i];
460+
const char *desc = it->refname;
578461
int w;
579462

580-
if (it->ignore)
581-
continue;
582-
w = utf8_strwidth(it->refname);
463+
skip_prefix(it->refname, "refs/heads/", &desc);
464+
skip_prefix(it->refname, "refs/remotes/", &desc);
465+
w = utf8_strwidth(desc);
466+
583467
if (it->kind == FILTER_REFS_REMOTES)
584468
w += remote_bonus;
585469
if (w > max)
@@ -588,14 +472,12 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
588472
return max;
589473
}
590474

591-
static void print_ref_list(struct ref_filter *filter)
475+
static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)
592476
{
593477
int i;
594478
struct ref_array array;
595-
struct ref_filter_cbdata data;
596479
int maxwidth = 0;
597480
const char *remote_prefix = "";
598-
struct rev_info revs;
599481

600482
/*
601483
* If we are listing more than just remote branches,
@@ -606,54 +488,26 @@ static void print_ref_list(struct ref_filter *filter)
606488
remote_prefix = "remotes/";
607489

608490
memset(&array, 0, sizeof(array));
609-
if (filter->merge != REF_FILTER_MERGED_NONE)
610-
init_revisions(&revs, NULL);
611-
612-
data.array = &array;
613-
data.filter = filter;
614-
array.revs = &revs;
615-
616-
/*
617-
* First we obtain all regular branch refs and if the HEAD is
618-
* detached then we insert that ref to the end of the ref_fist
619-
* so that it can be printed and removed first.
620-
*/
621-
for_each_rawref(append_ref, &data);
622-
if (filter->detached)
623-
head_ref(append_ref, &data);
624-
/*
625-
* The following implementation is currently duplicated in ref-filter. It
626-
* will eventually be removed when we port branch.c to use ref-filter APIs.
627-
*/
628-
if (filter->merge != REF_FILTER_MERGED_NONE) {
629-
filter->merge_commit->object.flags |= UNINTERESTING;
630-
add_pending_object(&revs, &filter->merge_commit->object, "");
631-
revs.limited = 1;
632-
633-
if (prepare_revision_walk(&revs))
634-
die(_("revision walk setup failed"));
635-
636-
for (i = 0; i < array.nr; i++) {
637-
struct ref_array_item *item = array.items[i];
638-
struct commit *commit = item->commit;
639-
int is_merged = !!(commit->object.flags & UNINTERESTING);
640-
item->ignore = is_merged != (filter->merge == REF_FILTER_MERGED_INCLUDE);
641-
}
642491

643-
for (i = 0; i < array.nr; i++) {
644-
struct ref_array_item *item = array.items[i];
645-
clear_commit_marks(item->commit, ALL_REV_FLAGS);
646-
}
647-
clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS);
648-
}
492+
verify_ref_format("%(refname)%(symref)");
493+
filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
649494

650495
if (filter->verbose)
651496
maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
652497

653-
qsort(array.items, array.nr, sizeof(struct ref_array_item *), ref_cmp);
498+
/*
499+
* If no sorting parameter is given then we default to sorting
500+
* by 'refname'. This would give us an alphabetically sorted
501+
* array with the 'HEAD' ref at the beginning followed by
502+
* local branches 'refs/heads/...' and finally remote-tacking
503+
* branches 'refs/remotes/...'.
504+
*/
505+
if (!sorting)
506+
sorting = ref_default_sorting();
507+
ref_array_sort(sorting, &array);
654508

655509
for (i = 0; i < array.nr; i++)
656-
print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
510+
format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
657511

658512
ref_array_clear(&array);
659513
}
@@ -755,6 +609,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
755609
const char *new_upstream = NULL;
756610
enum branch_track track;
757611
struct ref_filter filter;
612+
static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
758613

759614
struct option options[] = {
760615
OPT_GROUP(N_("Generic options")),
@@ -789,6 +644,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
789644
OPT_MERGED(&filter, N_("print only branches that are merged")),
790645
OPT_NO_MERGED(&filter, N_("print only branches that are not merged")),
791646
OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")),
647+
OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
648+
N_("field name to sort on"), &parse_opt_ref_sorting),
792649
OPT_END(),
793650
};
794651

@@ -847,7 +704,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
847704
if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
848705
filter.kind |= FILTER_REFS_DETACHED_HEAD;
849706
filter.name_patterns = argv;
850-
print_ref_list(&filter);
707+
print_ref_list(&filter, sorting);
851708
print_columns(&output, colopts, NULL);
852709
string_list_clear(&output, 0);
853710
return 0;

ref-filter.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1331,7 +1331,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
13311331
* obtain the commit using the 'oid' available and discard all
13321332
* non-commits early. The actual filtering is done later.
13331333
*/
1334-
if (filter->merge_commit || filter->with_commit) {
1334+
if (filter->merge_commit || filter->with_commit || filter->verbose) {
13351335
commit = lookup_commit_reference_gently(oid->hash, 1);
13361336
if (!commit)
13371337
return 0;

ref-filter.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ struct ref_array_item {
3636
unsigned char objectname[20];
3737
int flag;
3838
unsigned int kind;
39-
int ignore : 1; /* To be removed in the next patch */
4039
const char *symref;
4140
struct commit *commit;
4241
struct atom_value *value;

0 commit comments

Comments
 (0)