Merge branch 'nb/branch-show-other-worktrees-head'
"git branch --list" learned to show branches that are checked out in other worktrees connected to the same repository prefixed with '+', similar to the way the currently checked out branch is shown with '*' in front. * nb/branch-show-other-worktrees-head: branch: add worktree info on verbose output branch: update output to include worktree info ref-filter: add worktreepath atom
This commit is contained in:
commit
99eea64583
@ -26,8 +26,10 @@ DESCRIPTION
|
||||
-----------
|
||||
|
||||
If `--list` is given, or if there are no non-option arguments, existing
|
||||
branches are listed; the current branch will be highlighted with an
|
||||
asterisk. Option `-r` causes the remote-tracking branches to be listed,
|
||||
branches are listed; the current branch will be highlighted in green and
|
||||
marked with an asterisk. Any branches checked out in linked worktrees will
|
||||
be highlighted in cyan and marked with a plus sign. Option `-r` causes the
|
||||
remote-tracking branches to be listed,
|
||||
and option `-a` shows both local and remote branches. If a `<pattern>`
|
||||
is given, it is used as a shell wildcard to restrict the output to
|
||||
matching branches. If multiple patterns are given, a branch is shown if
|
||||
@ -174,8 +176,10 @@ This option is only applicable in non-verbose mode.
|
||||
When in list mode,
|
||||
show sha1 and commit subject line for each head, along with
|
||||
relationship to upstream branch (if any). If given twice, print
|
||||
the name of the upstream branch, as well (see also `git remote
|
||||
show <remote>`).
|
||||
the path of the linked worktree (if any) and the name of the upstream
|
||||
branch, as well (see also `git remote show <remote>`). Note that the
|
||||
current worktree's HEAD will not have its path printed (it will always
|
||||
be your current directory).
|
||||
|
||||
-q::
|
||||
--quiet::
|
||||
|
@ -214,6 +214,11 @@ symref::
|
||||
`:lstrip` and `:rstrip` options in the same way as `refname`
|
||||
above.
|
||||
|
||||
worktreepath::
|
||||
The absolute path to the worktree in which the ref is checked
|
||||
out, if it is checked out in any linked worktree. Empty string
|
||||
otherwise.
|
||||
|
||||
In addition to the above, for commit and tag objects, the header
|
||||
field names (`tree`, `parent`, `object`, `type`, and `tag`) can
|
||||
be used to specify the value in the header field.
|
||||
|
@ -47,6 +47,7 @@ static char branch_colors[][COLOR_MAXLEN] = {
|
||||
GIT_COLOR_NORMAL, /* LOCAL */
|
||||
GIT_COLOR_GREEN, /* CURRENT */
|
||||
GIT_COLOR_BLUE, /* UPSTREAM */
|
||||
GIT_COLOR_CYAN, /* WORKTREE */
|
||||
};
|
||||
enum color_branch {
|
||||
BRANCH_COLOR_RESET = 0,
|
||||
@ -54,7 +55,8 @@ enum color_branch {
|
||||
BRANCH_COLOR_REMOTE = 2,
|
||||
BRANCH_COLOR_LOCAL = 3,
|
||||
BRANCH_COLOR_CURRENT = 4,
|
||||
BRANCH_COLOR_UPSTREAM = 5
|
||||
BRANCH_COLOR_UPSTREAM = 5,
|
||||
BRANCH_COLOR_WORKTREE = 6
|
||||
};
|
||||
|
||||
static const char *color_branch_slots[] = {
|
||||
@ -64,6 +66,7 @@ static const char *color_branch_slots[] = {
|
||||
[BRANCH_COLOR_LOCAL] = "local",
|
||||
[BRANCH_COLOR_CURRENT] = "current",
|
||||
[BRANCH_COLOR_UPSTREAM] = "upstream",
|
||||
[BRANCH_COLOR_WORKTREE] = "worktree",
|
||||
};
|
||||
|
||||
static struct string_list output = STRING_LIST_INIT_DUP;
|
||||
@ -342,9 +345,10 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
|
||||
struct strbuf local = STRBUF_INIT;
|
||||
struct strbuf remote = STRBUF_INIT;
|
||||
|
||||
strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else) %s%%(end)",
|
||||
branch_get_color(BRANCH_COLOR_CURRENT),
|
||||
branch_get_color(BRANCH_COLOR_LOCAL));
|
||||
strbuf_addf(&local, "%%(if)%%(HEAD)%%(then)* %s%%(else)%%(if)%%(worktreepath)%%(then)+ %s%%(else) %s%%(end)%%(end)",
|
||||
branch_get_color(BRANCH_COLOR_CURRENT),
|
||||
branch_get_color(BRANCH_COLOR_WORKTREE),
|
||||
branch_get_color(BRANCH_COLOR_LOCAL));
|
||||
strbuf_addf(&remote, " %s",
|
||||
branch_get_color(BRANCH_COLOR_REMOTE));
|
||||
|
||||
@ -363,9 +367,13 @@ static char *build_format(struct ref_filter *filter, int maxwidth, const char *r
|
||||
strbuf_addf(&local, " %s ", obname.buf);
|
||||
|
||||
if (filter->verbose > 1)
|
||||
{
|
||||
strbuf_addf(&local, "%%(if:notequals=*)%%(HEAD)%%(then)%%(if)%%(worktreepath)%%(then)(%s%%(worktreepath)%s) %%(end)%%(end)",
|
||||
branch_get_color(BRANCH_COLOR_WORKTREE), branch_get_color(BRANCH_COLOR_RESET));
|
||||
strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
|
||||
"%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
|
||||
branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
|
||||
}
|
||||
else
|
||||
strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
|
||||
|
||||
|
78
ref-filter.c
78
ref-filter.c
@ -20,6 +20,8 @@
|
||||
#include "commit-slab.h"
|
||||
#include "commit-graph.h"
|
||||
#include "commit-reach.h"
|
||||
#include "worktree.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
static struct ref_msg {
|
||||
const char *gone;
|
||||
@ -75,6 +77,27 @@ static struct expand_data {
|
||||
struct object_info info;
|
||||
} oi, oi_deref;
|
||||
|
||||
struct ref_to_worktree_entry {
|
||||
struct hashmap_entry ent; /* must be the first member! */
|
||||
struct worktree *wt; /* key is wt->head_ref */
|
||||
};
|
||||
|
||||
static int ref_to_worktree_map_cmpfnc(const void *unused_lookupdata,
|
||||
const void *existing_hashmap_entry_to_test,
|
||||
const void *key,
|
||||
const void *keydata_aka_refname)
|
||||
{
|
||||
const struct ref_to_worktree_entry *e = existing_hashmap_entry_to_test;
|
||||
const struct ref_to_worktree_entry *k = key;
|
||||
return strcmp(e->wt->head_ref,
|
||||
keydata_aka_refname ? keydata_aka_refname : k->wt->head_ref);
|
||||
}
|
||||
|
||||
static struct ref_to_worktree_map {
|
||||
struct hashmap map;
|
||||
struct worktree **worktrees;
|
||||
} ref_to_worktree_map;
|
||||
|
||||
/*
|
||||
* An atom is a valid field atom listed below, possibly prefixed with
|
||||
* a "*" to denote deref_tag().
|
||||
@ -480,6 +503,7 @@ static struct {
|
||||
{ "flag", SOURCE_NONE },
|
||||
{ "HEAD", SOURCE_NONE, FIELD_STR, head_atom_parser },
|
||||
{ "color", SOURCE_NONE, FIELD_STR, color_atom_parser },
|
||||
{ "worktreepath", SOURCE_NONE },
|
||||
{ "align", SOURCE_NONE, FIELD_STR, align_atom_parser },
|
||||
{ "end", SOURCE_NONE },
|
||||
{ "if", SOURCE_NONE, FIELD_STR, if_atom_parser },
|
||||
@ -1531,6 +1555,48 @@ static int get_object(struct ref_array_item *ref, int deref, struct object **obj
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void populate_worktree_map(struct hashmap *map, struct worktree **worktrees)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; worktrees[i]; i++) {
|
||||
if (worktrees[i]->head_ref) {
|
||||
struct ref_to_worktree_entry *entry;
|
||||
entry = xmalloc(sizeof(*entry));
|
||||
entry->wt = worktrees[i];
|
||||
hashmap_entry_init(entry, strhash(worktrees[i]->head_ref));
|
||||
|
||||
hashmap_add(map, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lazy_init_worktree_map(void)
|
||||
{
|
||||
if (ref_to_worktree_map.worktrees)
|
||||
return;
|
||||
|
||||
ref_to_worktree_map.worktrees = get_worktrees(0);
|
||||
hashmap_init(&(ref_to_worktree_map.map), ref_to_worktree_map_cmpfnc, NULL, 0);
|
||||
populate_worktree_map(&(ref_to_worktree_map.map), ref_to_worktree_map.worktrees);
|
||||
}
|
||||
|
||||
static char *get_worktree_path(const struct used_atom *atom, const struct ref_array_item *ref)
|
||||
{
|
||||
struct hashmap_entry entry;
|
||||
struct ref_to_worktree_entry *lookup_result;
|
||||
|
||||
lazy_init_worktree_map();
|
||||
|
||||
hashmap_entry_init(&entry, strhash(ref->refname));
|
||||
lookup_result = hashmap_get(&(ref_to_worktree_map.map), &entry, ref->refname);
|
||||
|
||||
if (lookup_result)
|
||||
return xstrdup(lookup_result->wt->path);
|
||||
else
|
||||
return xstrdup("");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the object referred by ref, and grab needed value.
|
||||
*/
|
||||
@ -1568,6 +1634,13 @@ static int populate_value(struct ref_array_item *ref, struct strbuf *err)
|
||||
|
||||
if (starts_with(name, "refname"))
|
||||
refname = get_refname(atom, ref);
|
||||
else if (!strcmp(name, "worktreepath")) {
|
||||
if (ref->kind == FILTER_REFS_BRANCHES)
|
||||
v->s = get_worktree_path(atom, ref);
|
||||
else
|
||||
v->s = xstrdup("");
|
||||
continue;
|
||||
}
|
||||
else if (starts_with(name, "symref"))
|
||||
refname = get_symref(atom, ref);
|
||||
else if (starts_with(name, "upstream")) {
|
||||
@ -2051,6 +2124,11 @@ void ref_array_clear(struct ref_array *array)
|
||||
free_array_item(array->items[i]);
|
||||
FREE_AND_NULL(array->items);
|
||||
array->nr = array->alloc = 0;
|
||||
if (ref_to_worktree_map.worktrees) {
|
||||
hashmap_free(&(ref_to_worktree_map.map), 1);
|
||||
free_worktrees(ref_to_worktree_map.worktrees);
|
||||
ref_to_worktree_map.worktrees = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
|
||||
|
@ -206,18 +206,22 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
|
||||
git worktree add -f bazdir2 baz &&
|
||||
git branch -M baz bam &&
|
||||
test $(git -C bazdir rev-parse --abbrev-ref HEAD) = bam &&
|
||||
test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam
|
||||
test $(git -C bazdir2 rev-parse --abbrev-ref HEAD) = bam &&
|
||||
rm -r bazdir bazdir2 &&
|
||||
git worktree prune
|
||||
'
|
||||
|
||||
test_expect_success 'git branch -M baz bam should succeed within a worktree in which baz is checked out' '
|
||||
git checkout -b baz &&
|
||||
git worktree add -f bazdir3 baz &&
|
||||
git worktree add -f bazdir baz &&
|
||||
(
|
||||
cd bazdir3 &&
|
||||
cd bazdir &&
|
||||
git branch -M baz bam &&
|
||||
test $(git rev-parse --abbrev-ref HEAD) = bam
|
||||
) &&
|
||||
test $(git rev-parse --abbrev-ref HEAD) = bam
|
||||
test $(git rev-parse --abbrev-ref HEAD) = bam &&
|
||||
rm -r bazdir &&
|
||||
git worktree prune
|
||||
'
|
||||
|
||||
test_expect_success 'git branch -M master should work when master is checked out' '
|
||||
@ -804,7 +808,9 @@ test_expect_success 'test deleting branch without config' '
|
||||
test_expect_success 'deleting currently checked out branch fails' '
|
||||
git worktree add -b my7 my7 &&
|
||||
test_must_fail git -C my7 branch -d my7 &&
|
||||
test_must_fail git branch -d my7
|
||||
test_must_fail git branch -d my7 &&
|
||||
rm -r my7 &&
|
||||
git worktree prune
|
||||
'
|
||||
|
||||
test_expect_success 'test --track without .fetch entries' '
|
||||
|
@ -136,10 +136,13 @@ test_expect_success 'git branch `--show-current` works properly with worktrees'
|
||||
branch-two
|
||||
EOF
|
||||
git checkout branch-one &&
|
||||
git worktree add worktree branch-two &&
|
||||
test_when_finished "
|
||||
git worktree remove worktree_dir
|
||||
" &&
|
||||
git worktree add worktree_dir branch-two &&
|
||||
{
|
||||
git branch --show-current &&
|
||||
git -C worktree branch --show-current
|
||||
git -C worktree_dir branch --show-current
|
||||
} >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
@ -284,6 +287,24 @@ test_expect_success 'git branch --format option' '
|
||||
test_i18ncmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'worktree colors correct' '
|
||||
cat >expect <<-EOF &&
|
||||
* <GREEN>(HEAD detached from fromtag)<RESET>
|
||||
ambiguous<RESET>
|
||||
branch-one<RESET>
|
||||
+ <CYAN>branch-two<RESET>
|
||||
master<RESET>
|
||||
ref-to-branch<RESET> -> branch-one
|
||||
ref-to-remote<RESET> -> origin/branch-one
|
||||
EOF
|
||||
git worktree add worktree_dir branch-two &&
|
||||
git branch --color >actual.raw &&
|
||||
rm -r worktree_dir &&
|
||||
git worktree prune &&
|
||||
test_decode_color <actual.raw >actual &&
|
||||
test_i18ncmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success "set up color tests" '
|
||||
echo "<RED>master<RESET>" >expect.color &&
|
||||
echo "master" >expect.bare &&
|
||||
@ -308,4 +329,23 @@ test_expect_success '--color overrides auto-color' '
|
||||
test_cmp expect.color actual
|
||||
'
|
||||
|
||||
test_expect_success 'verbose output lists worktree path' '
|
||||
one=$(git rev-parse --short HEAD) &&
|
||||
two=$(git rev-parse --short master) &&
|
||||
cat >expect <<-EOF &&
|
||||
* (HEAD detached from fromtag) $one one
|
||||
ambiguous $one one
|
||||
branch-one $two two
|
||||
+ branch-two $one ($(pwd)/worktree_dir) one
|
||||
master $two two
|
||||
ref-to-branch $two two
|
||||
ref-to-remote $two two
|
||||
EOF
|
||||
git worktree add worktree_dir branch-two &&
|
||||
git branch -vv >actual &&
|
||||
rm -r worktree_dir &&
|
||||
git worktree prune &&
|
||||
test_i18ncmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
@ -441,4 +441,17 @@ test_expect_success '--merged is incompatible with --no-merged' '
|
||||
test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
|
||||
'
|
||||
|
||||
test_expect_success 'validate worktree atom' '
|
||||
cat >expect <<-EOF &&
|
||||
master: $(pwd)
|
||||
master_worktree: $(pwd)/worktree_dir
|
||||
side: not checked out
|
||||
EOF
|
||||
git worktree add -b master_worktree worktree_dir master &&
|
||||
git for-each-ref --format="%(refname:short): %(if)%(worktreepath)%(then)%(worktreepath)%(else)not checked out%(end)" refs/heads/ >actual &&
|
||||
rm -r worktree_dir &&
|
||||
git worktree prune &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_done
|
||||
|
Loading…
Reference in New Issue
Block a user