Skip to content

Commit f2d156d

Browse files
committed
Merge branch 'ab/branch-sort' into maint
The implementation of "git branch --sort" wrt the detached HEAD display has always been hacky, which has been cleaned up. * ab/branch-sort: branch: show "HEAD detached" first under reverse sort branch: sort detached HEAD based on a flag ref-filter: move ref_sorting flags to a bitfield ref-filter: move "cmp_fn" assignment into "else if" arm ref-filter: add braces to if/else if/else chain branch tests: add to --sort tests branch: change "--local" to "--list" in comment
2 parents 171675a + 4045f65 commit f2d156d

File tree

8 files changed

+111
-44
lines changed

8 files changed

+111
-44
lines changed

builtin/branch.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
726726
print_current_branch_name();
727727
return 0;
728728
} else if (list) {
729-
/* git branch --local also shows HEAD when it is detached */
729+
/* git branch --list also shows HEAD when it is detached */
730730
if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
731731
filter.kind |= FILTER_REFS_DETACHED_HEAD;
732732
filter.name_patterns = argv;
@@ -739,7 +739,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
739739
*/
740740
if (!sorting)
741741
sorting = ref_default_sorting();
742-
ref_sorting_icase_all(sorting, icase);
742+
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
743+
ref_sorting_set_sort_flags_all(
744+
sorting, REF_SORTING_DETACHED_HEAD_FIRST, 1);
743745
print_ref_list(&filter, sorting, &format);
744746
print_columns(&output, colopts, NULL);
745747
string_list_clear(&output, 0);

builtin/for-each-ref.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
7070

7171
if (!sorting)
7272
sorting = ref_default_sorting();
73-
ref_sorting_icase_all(sorting, icase);
73+
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
7474
filter.ignore_case = icase;
7575

7676
filter.name_patterns = argv;

builtin/tag.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
485485
}
486486
if (!sorting)
487487
sorting = ref_default_sorting();
488-
ref_sorting_icase_all(sorting, icase);
488+
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
489489
filter.ignore_case = icase;
490490
if (cmdmode == 'l') {
491491
int ret;

ref-filter.c

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,36 +1536,27 @@ char *get_head_description(void)
15361536
struct wt_status_state state;
15371537
memset(&state, 0, sizeof(state));
15381538
wt_status_get_state(the_repository, &state, 1);
1539-
1540-
/*
1541-
* The ( character must be hard-coded and not part of a localizable
1542-
* string, since the description is used as a sort key and compared
1543-
* with ref names.
1544-
*/
1545-
strbuf_addch(&desc, '(');
15461539
if (state.rebase_in_progress ||
15471540
state.rebase_interactive_in_progress) {
15481541
if (state.branch)
1549-
strbuf_addf(&desc, _("no branch, rebasing %s"),
1542+
strbuf_addf(&desc, _("(no branch, rebasing %s)"),
15501543
state.branch);
15511544
else
1552-
strbuf_addf(&desc, _("no branch, rebasing detached HEAD %s"),
1545+
strbuf_addf(&desc, _("(no branch, rebasing detached HEAD %s)"),
15531546
state.detached_from);
15541547
} else if (state.bisect_in_progress)
1555-
strbuf_addf(&desc, _("no branch, bisect started on %s"),
1548+
strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
15561549
state.branch);
15571550
else if (state.detached_from) {
15581551
if (state.detached_at)
1559-
strbuf_addstr(&desc, HEAD_DETACHED_AT);
1552+
strbuf_addf(&desc, _("(HEAD detached at %s)"),
1553+
state.detached_from);
15601554
else
1561-
strbuf_addstr(&desc, HEAD_DETACHED_FROM);
1562-
strbuf_addstr(&desc, state.detached_from);
1563-
}
1564-
else
1565-
strbuf_addstr(&desc, _("no branch"));
1566-
strbuf_addch(&desc, ')');
1555+
strbuf_addf(&desc, _("(HEAD detached from %s)"),
1556+
state.detached_from);
1557+
} else
1558+
strbuf_addstr(&desc, _("(no branch)"));
15671559

1568-
wt_status_state_free_buffers(&state);
15691560
return strbuf_detach(&desc, NULL);
15701561
}
15711562

@@ -2350,25 +2341,43 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
23502341
return ret;
23512342
}
23522343

2344+
static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b)
2345+
{
2346+
if (!(a->kind ^ b->kind))
2347+
BUG("ref_kind_from_refname() should only mark one ref as HEAD");
2348+
if (a->kind & FILTER_REFS_DETACHED_HEAD)
2349+
return -1;
2350+
else if (b->kind & FILTER_REFS_DETACHED_HEAD)
2351+
return 1;
2352+
BUG("should have died in the xor check above");
2353+
return 0;
2354+
}
2355+
23532356
static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)
23542357
{
23552358
struct atom_value *va, *vb;
23562359
int cmp;
2360+
int cmp_detached_head = 0;
23572361
cmp_type cmp_type = used_atom[s->atom].type;
2358-
int (*cmp_fn)(const char *, const char *);
23592362
struct strbuf err = STRBUF_INIT;
23602363

23612364
if (get_ref_atom_value(a, s->atom, &va, &err))
23622365
die("%s", err.buf);
23632366
if (get_ref_atom_value(b, s->atom, &vb, &err))
23642367
die("%s", err.buf);
23652368
strbuf_release(&err);
2366-
cmp_fn = s->ignore_case ? strcasecmp : strcmp;
2367-
if (s->version)
2369+
if (s->sort_flags & REF_SORTING_DETACHED_HEAD_FIRST &&
2370+
((a->kind | b->kind) & FILTER_REFS_DETACHED_HEAD)) {
2371+
cmp = compare_detached_head(a, b);
2372+
cmp_detached_head = 1;
2373+
} else if (s->sort_flags & REF_SORTING_VERSION) {
23682374
cmp = versioncmp(va->s, vb->s);
2369-
else if (cmp_type == FIELD_STR)
2375+
} else if (cmp_type == FIELD_STR) {
2376+
int (*cmp_fn)(const char *, const char *);
2377+
cmp_fn = s->sort_flags & REF_SORTING_ICASE
2378+
? strcasecmp : strcmp;
23702379
cmp = cmp_fn(va->s, vb->s);
2371-
else {
2380+
} else {
23722381
if (va->value < vb->value)
23732382
cmp = -1;
23742383
else if (va->value == vb->value)
@@ -2377,7 +2386,8 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
23772386
cmp = 1;
23782387
}
23792388

2380-
return (s->reverse) ? -cmp : cmp;
2389+
return (s->sort_flags & REF_SORTING_REVERSE && !cmp_detached_head)
2390+
? -cmp : cmp;
23812391
}
23822392

23832393
static int compare_refs(const void *a_, const void *b_, void *ref_sorting)
@@ -2392,15 +2402,20 @@ static int compare_refs(const void *a_, const void *b_, void *ref_sorting)
23922402
return cmp;
23932403
}
23942404
s = ref_sorting;
2395-
return s && s->ignore_case ?
2405+
return s && s->sort_flags & REF_SORTING_ICASE ?
23962406
strcasecmp(a->refname, b->refname) :
23972407
strcmp(a->refname, b->refname);
23982408
}
23992409

2400-
void ref_sorting_icase_all(struct ref_sorting *sorting, int flag)
2410+
void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting,
2411+
unsigned int mask, int on)
24012412
{
2402-
for (; sorting; sorting = sorting->next)
2403-
sorting->ignore_case = !!flag;
2413+
for (; sorting; sorting = sorting->next) {
2414+
if (on)
2415+
sorting->sort_flags |= mask;
2416+
else
2417+
sorting->sort_flags &= ~mask;
2418+
}
24042419
}
24052420

24062421
void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
@@ -2537,12 +2552,12 @@ void parse_ref_sorting(struct ref_sorting **sorting_tail, const char *arg)
25372552
*sorting_tail = s;
25382553

25392554
if (*arg == '-') {
2540-
s->reverse = 1;
2555+
s->sort_flags |= REF_SORTING_REVERSE;
25412556
arg++;
25422557
}
25432558
if (skip_prefix(arg, "version:", &arg) ||
25442559
skip_prefix(arg, "v:", &arg))
2545-
s->version = 1;
2560+
s->sort_flags |= REF_SORTING_VERSION;
25462561
s->atom = parse_sorting_atom(arg);
25472562
}
25482563

ref-filter.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,12 @@ struct atom_value;
2828
struct ref_sorting {
2929
struct ref_sorting *next;
3030
int atom; /* index into used_atom array (internal) */
31-
unsigned reverse : 1,
32-
ignore_case : 1,
33-
version : 1;
31+
enum {
32+
REF_SORTING_REVERSE = 1<<0,
33+
REF_SORTING_ICASE = 1<<1,
34+
REF_SORTING_VERSION = 1<<2,
35+
REF_SORTING_DETACHED_HEAD_FIRST = 1<<3,
36+
} sort_flags;
3437
};
3538

3639
struct ref_array_item {
@@ -109,8 +112,8 @@ void ref_array_clear(struct ref_array *array);
109112
int verify_ref_format(struct ref_format *format);
110113
/* Sort the given ref_array as per the ref_sorting provided */
111114
void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
112-
/* Set the ignore_case flag for all elements of a sorting list */
113-
void ref_sorting_icase_all(struct ref_sorting *sorting, int flag);
115+
/* Set REF_SORTING_* sort_flags for all elements of a sorting list */
116+
void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, unsigned int mask, int on);
114117
/* Based on the given format and quote_style, fill the strbuf */
115118
int format_ref_array_item(struct ref_array_item *info,
116119
const struct ref_format *format,

t/t3203-branch-output.sh

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,63 @@ EOF
210210
test_i18ncmp expect actual
211211
'
212212

213-
test_expect_success 'git branch `--sort` option' '
213+
test_expect_success 'git branch `--sort=[-]objectsize` option' '
214214
cat >expect <<-\EOF &&
215215
* (HEAD detached from fromtag)
216216
branch-two
217217
branch-one
218218
main
219219
EOF
220220
git branch --sort=objectsize >actual &&
221+
test_i18ncmp expect actual &&
222+
223+
cat >expect <<-\EOF &&
224+
* (HEAD detached from fromtag)
225+
branch-one
226+
main
227+
branch-two
228+
EOF
229+
git branch --sort=-objectsize >actual &&
230+
test_i18ncmp expect actual
231+
'
232+
233+
test_expect_success 'git branch `--sort=[-]type` option' '
234+
cat >expect <<-\EOF &&
235+
* (HEAD detached from fromtag)
236+
branch-one
237+
branch-two
238+
main
239+
EOF
240+
git branch --sort=type >actual &&
241+
test_i18ncmp expect actual &&
242+
243+
cat >expect <<-\EOF &&
244+
* (HEAD detached from fromtag)
245+
branch-one
246+
branch-two
247+
main
248+
EOF
249+
git branch --sort=-type >actual &&
250+
test_i18ncmp expect actual
251+
'
252+
253+
test_expect_success 'git branch `--sort=[-]version:refname` option' '
254+
cat >expect <<-\EOF &&
255+
* (HEAD detached from fromtag)
256+
branch-one
257+
branch-two
258+
main
259+
EOF
260+
git branch --sort=version:refname >actual &&
261+
test_i18ncmp expect actual &&
262+
263+
cat >expect <<-\EOF &&
264+
* (HEAD detached from fromtag)
265+
main
266+
branch-two
267+
branch-one
268+
EOF
269+
git branch --sort=-version:refname >actual &&
221270
test_i18ncmp expect actual
222271
'
223272

wt-status.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,9 +1742,9 @@ static void wt_longstatus_print(struct wt_status *s)
17421742
} else if (s->state.detached_from) {
17431743
branch_name = s->state.detached_from;
17441744
if (s->state.detached_at)
1745-
on_what = HEAD_DETACHED_AT;
1745+
on_what = _("HEAD detached at ");
17461746
else
1747-
on_what = HEAD_DETACHED_FROM;
1747+
on_what = _("HEAD detached from ");
17481748
} else {
17491749
branch_name = "";
17501750
on_what = _("Not currently on any branch.");

wt-status.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ enum wt_status_format {
7777
STATUS_FORMAT_UNSPECIFIED
7878
};
7979

80-
#define HEAD_DETACHED_AT _("HEAD detached at ")
81-
#define HEAD_DETACHED_FROM _("HEAD detached from ")
8280
#define SPARSE_CHECKOUT_DISABLED -1
8381

8482
struct wt_status_state {

0 commit comments

Comments
 (0)