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
This commit is contained in:
Junio C Hamano 2021-02-08 14:05:55 -08:00
commit f2d156dc48
8 changed files with 111 additions and 44 deletions

View File

@ -726,7 +726,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
print_current_branch_name();
return 0;
} else if (list) {
/* git branch --local also shows HEAD when it is detached */
/* git branch --list also shows HEAD when it is detached */
if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached)
filter.kind |= FILTER_REFS_DETACHED_HEAD;
filter.name_patterns = argv;
@ -739,7 +739,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
*/
if (!sorting)
sorting = ref_default_sorting();
ref_sorting_icase_all(sorting, icase);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
ref_sorting_set_sort_flags_all(
sorting, REF_SORTING_DETACHED_HEAD_FIRST, 1);
print_ref_list(&filter, sorting, &format);
print_columns(&output, colopts, NULL);
string_list_clear(&output, 0);

View File

@ -70,7 +70,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
if (!sorting)
sorting = ref_default_sorting();
ref_sorting_icase_all(sorting, icase);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
filter.name_patterns = argv;

View File

@ -485,7 +485,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
}
if (!sorting)
sorting = ref_default_sorting();
ref_sorting_icase_all(sorting, icase);
ref_sorting_set_sort_flags_all(sorting, REF_SORTING_ICASE, icase);
filter.ignore_case = icase;
if (cmdmode == 'l') {
int ret;

View File

@ -1536,36 +1536,27 @@ char *get_head_description(void)
struct wt_status_state state;
memset(&state, 0, sizeof(state));
wt_status_get_state(the_repository, &state, 1);
/*
* The ( character must be hard-coded and not part of a localizable
* string, since the description is used as a sort key and compared
* with ref names.
*/
strbuf_addch(&desc, '(');
if (state.rebase_in_progress ||
state.rebase_interactive_in_progress) {
if (state.branch)
strbuf_addf(&desc, _("no branch, rebasing %s"),
strbuf_addf(&desc, _("(no branch, rebasing %s)"),
state.branch);
else
strbuf_addf(&desc, _("no branch, rebasing detached HEAD %s"),
strbuf_addf(&desc, _("(no branch, rebasing detached HEAD %s)"),
state.detached_from);
} else if (state.bisect_in_progress)
strbuf_addf(&desc, _("no branch, bisect started on %s"),
strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
state.branch);
else if (state.detached_from) {
if (state.detached_at)
strbuf_addstr(&desc, HEAD_DETACHED_AT);
strbuf_addf(&desc, _("(HEAD detached at %s)"),
state.detached_from);
else
strbuf_addstr(&desc, HEAD_DETACHED_FROM);
strbuf_addstr(&desc, state.detached_from);
}
else
strbuf_addstr(&desc, _("no branch"));
strbuf_addch(&desc, ')');
strbuf_addf(&desc, _("(HEAD detached from %s)"),
state.detached_from);
} else
strbuf_addstr(&desc, _("(no branch)"));
wt_status_state_free_buffers(&state);
return strbuf_detach(&desc, NULL);
}
@ -2350,12 +2341,24 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
return ret;
}
static int compare_detached_head(struct ref_array_item *a, struct ref_array_item *b)
{
if (!(a->kind ^ b->kind))
BUG("ref_kind_from_refname() should only mark one ref as HEAD");
if (a->kind & FILTER_REFS_DETACHED_HEAD)
return -1;
else if (b->kind & FILTER_REFS_DETACHED_HEAD)
return 1;
BUG("should have died in the xor check above");
return 0;
}
static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, struct ref_array_item *b)
{
struct atom_value *va, *vb;
int cmp;
int cmp_detached_head = 0;
cmp_type cmp_type = used_atom[s->atom].type;
int (*cmp_fn)(const char *, const char *);
struct strbuf err = STRBUF_INIT;
if (get_ref_atom_value(a, s->atom, &va, &err))
@ -2363,12 +2366,18 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
if (get_ref_atom_value(b, s->atom, &vb, &err))
die("%s", err.buf);
strbuf_release(&err);
cmp_fn = s->ignore_case ? strcasecmp : strcmp;
if (s->version)
if (s->sort_flags & REF_SORTING_DETACHED_HEAD_FIRST &&
((a->kind | b->kind) & FILTER_REFS_DETACHED_HEAD)) {
cmp = compare_detached_head(a, b);
cmp_detached_head = 1;
} else if (s->sort_flags & REF_SORTING_VERSION) {
cmp = versioncmp(va->s, vb->s);
else if (cmp_type == FIELD_STR)
} else if (cmp_type == FIELD_STR) {
int (*cmp_fn)(const char *, const char *);
cmp_fn = s->sort_flags & REF_SORTING_ICASE
? strcasecmp : strcmp;
cmp = cmp_fn(va->s, vb->s);
else {
} else {
if (va->value < vb->value)
cmp = -1;
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
cmp = 1;
}
return (s->reverse) ? -cmp : cmp;
return (s->sort_flags & REF_SORTING_REVERSE && !cmp_detached_head)
? -cmp : cmp;
}
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)
return cmp;
}
s = ref_sorting;
return s && s->ignore_case ?
return s && s->sort_flags & REF_SORTING_ICASE ?
strcasecmp(a->refname, b->refname) :
strcmp(a->refname, b->refname);
}
void ref_sorting_icase_all(struct ref_sorting *sorting, int flag)
void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting,
unsigned int mask, int on)
{
for (; sorting; sorting = sorting->next)
sorting->ignore_case = !!flag;
for (; sorting; sorting = sorting->next) {
if (on)
sorting->sort_flags |= mask;
else
sorting->sort_flags &= ~mask;
}
}
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)
*sorting_tail = s;
if (*arg == '-') {
s->reverse = 1;
s->sort_flags |= REF_SORTING_REVERSE;
arg++;
}
if (skip_prefix(arg, "version:", &arg) ||
skip_prefix(arg, "v:", &arg))
s->version = 1;
s->sort_flags |= REF_SORTING_VERSION;
s->atom = parse_sorting_atom(arg);
}

View File

@ -28,9 +28,12 @@ struct atom_value;
struct ref_sorting {
struct ref_sorting *next;
int atom; /* index into used_atom array (internal) */
unsigned reverse : 1,
ignore_case : 1,
version : 1;
enum {
REF_SORTING_REVERSE = 1<<0,
REF_SORTING_ICASE = 1<<1,
REF_SORTING_VERSION = 1<<2,
REF_SORTING_DETACHED_HEAD_FIRST = 1<<3,
} sort_flags;
};
struct ref_array_item {
@ -109,8 +112,8 @@ void ref_array_clear(struct ref_array *array);
int verify_ref_format(struct ref_format *format);
/* Sort the given ref_array as per the ref_sorting provided */
void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
/* Set the ignore_case flag for all elements of a sorting list */
void ref_sorting_icase_all(struct ref_sorting *sorting, int flag);
/* Set REF_SORTING_* sort_flags for all elements of a sorting list */
void ref_sorting_set_sort_flags_all(struct ref_sorting *sorting, unsigned int mask, int on);
/* Based on the given format and quote_style, fill the strbuf */
int format_ref_array_item(struct ref_array_item *info,
const struct ref_format *format,

View File

@ -210,7 +210,7 @@ EOF
test_i18ncmp expect actual
'
test_expect_success 'git branch `--sort` option' '
test_expect_success 'git branch `--sort=[-]objectsize` option' '
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-two
@ -218,6 +218,55 @@ test_expect_success 'git branch `--sort` option' '
main
EOF
git branch --sort=objectsize >actual &&
test_i18ncmp expect actual &&
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
main
branch-two
EOF
git branch --sort=-objectsize >actual &&
test_i18ncmp expect actual
'
test_expect_success 'git branch `--sort=[-]type` option' '
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git branch --sort=type >actual &&
test_i18ncmp expect actual &&
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git branch --sort=-type >actual &&
test_i18ncmp expect actual
'
test_expect_success 'git branch `--sort=[-]version:refname` option' '
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
branch-one
branch-two
main
EOF
git branch --sort=version:refname >actual &&
test_i18ncmp expect actual &&
cat >expect <<-\EOF &&
* (HEAD detached from fromtag)
main
branch-two
branch-one
EOF
git branch --sort=-version:refname >actual &&
test_i18ncmp expect actual
'

View File

@ -1742,9 +1742,9 @@ static void wt_longstatus_print(struct wt_status *s)
} else if (s->state.detached_from) {
branch_name = s->state.detached_from;
if (s->state.detached_at)
on_what = HEAD_DETACHED_AT;
on_what = _("HEAD detached at ");
else
on_what = HEAD_DETACHED_FROM;
on_what = _("HEAD detached from ");
} else {
branch_name = "";
on_what = _("Not currently on any branch.");

View File

@ -77,8 +77,6 @@ enum wt_status_format {
STATUS_FORMAT_UNSPECIFIED
};
#define HEAD_DETACHED_AT _("HEAD detached at ")
#define HEAD_DETACHED_FROM _("HEAD detached from ")
#define SPARSE_CHECKOUT_DISABLED -1
struct wt_status_state {