revision.c: --all adds HEAD from all worktrees

Unless single_worktree is set, --all now adds HEAD from all worktrees. Since reachable.c code does not use setup_revisions(), we need to call other_head_refs_submodule() explicitly there to have the same effect on "git prune", so that we won't accidentally delete objects needed by some other HEADs. A new FIXME is added because we would need something like int refs_other_head_refs(struct ref_store *, each_ref_fn, cb_data); in addition to other_head_refs() to handle it, which might require int get_submodule_worktrees(const char *submodule, int flags); It could be a separate topic to reduce the scope of this one. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Aug 23, 2017 at 19:36 UTC d0c39a49ccb5dfe7feba4325c3374d99ab123c59
6 files changed +60
reachable.c
+2
@@ -9,6 +9,7 @@
9 #include "cache-tree.h"
10 #include "progress.h"
11 #include "list-objects.h"
12 +#include "worktree.h"
13
14 struct connectivity_progress {
15 struct progress *progress;
@@ -176,6 +177,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
177
178 /* detached HEAD is not included in the list above */
179 head_ref(add_one_ref, revs);
180 + other_head_refs(add_one_ref, revs);
181
182 /* Add all reflog info */
183 if (mark_reflog)
revision.c
+14
@@ -2133,6 +2133,14 @@ static int handle_revision_pseudo_opt(const char *submodule,
2133 int argcount;
2134
2135 if (submodule) {
2136 + /*
2137 + * We need some something like get_submodule_worktrees()
2138 + * before we can go through all worktrees of a submodule,
2139 + * .e.g with adding all HEADs from --all, which is not
2140 + * supported right now, so stick to single worktree.
2141 + */
2142 + if (!revs->single_worktree)
2143 + die("BUG: --single-worktree cannot be used together with submodule");
2144 refs = get_submodule_ref_store(submodule);
2145 } else
2146 refs = get_main_ref_store();
@@ -2150,6 +2158,12 @@ static int handle_revision_pseudo_opt(const char *submodule,
2158 if (!strcmp(arg, "--all")) {
2159 handle_refs(refs, revs, *flags, refs_for_each_ref);
2160 handle_refs(refs, revs, *flags, refs_head_ref);
2161 + if (!revs->single_worktree) {
2162 + struct all_refs_cb cb;
2163 +
2164 + init_all_refs_cb(&cb, revs, *flags);
2165 + other_head_refs(handle_one_ref, &cb);
2166 + }
2167 clear_ref_exclusion(&revs->ref_excludes);
2168 } else if (!strcmp(arg, "--branches")) {
2169 handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
submodule.c
+2
@@ -1685,6 +1685,8 @@ static int find_first_merges(struct object_array *result, const char *path,
1685 oid_to_hex(&a->object.oid));
1686 init_revisions(&revs, NULL);
1687 rev_opts.submodule = path;
1688 + /* FIXME: can't handle linked worktrees in submodules yet */
1689 + revs.single_worktree = path != NULL;
1690 setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
1691
1692 /* save all revisions from the above list that contain b */
t/t5304-prune.sh
+12
@@ -292,4 +292,16 @@ test_expect_success 'prune: handle index in multiple worktrees' '
292 test_cmp second-worktree/blob actual
293 '
294
295 +test_expect_success 'prune: handle HEAD in multiple worktrees' '
296 + git worktree add --detach third-worktree &&
297 + echo "new blob for third-worktree" >third-worktree/blob &&
298 + git -C third-worktree add blob &&
299 + git -C third-worktree commit -m "third" &&
300 + rm .git/worktrees/third-worktree/index &&
301 + test_must_fail git -C third-worktree show :blob &&
302 + git prune --expire=now &&
303 + git -C third-worktree show HEAD:blob >actual &&
304 + test_cmp third-worktree/blob actual
305 +'
306 +
307 test_done
worktree.c
+22
@@ -386,3 +386,25 @@ int submodule_uses_worktrees(const char *path)
386 closedir(dir);
387 return ret;
388 }
389 +
390 +int other_head_refs(each_ref_fn fn, void *cb_data)
391 +{
392 + struct worktree **worktrees, **p;
393 + int ret = 0;
394 +
395 + worktrees = get_worktrees(0);
396 + for (p = worktrees; *p; p++) {
397 + struct worktree *wt = *p;
398 + struct ref_store *refs;
399 +
400 + if (wt->is_current)
401 + continue;
402 +
403 + refs = get_worktree_ref_store(wt);
404 + ret = refs_head_ref(refs, fn, cb_data);
405 + if (ret)
406 + break;
407 + }
408 + free_worktrees(worktrees);
409 + return ret;
410 +}
worktree.h
+8
@@ -1,6 +1,8 @@
1 #ifndef WORKTREE_H
2 #define WORKTREE_H
3
4 +#include "refs.h"
5 +
6 struct worktree {
7 char *path;
8 char *id;
@@ -70,6 +72,12 @@ extern void free_worktrees(struct worktree **);
72 extern const struct worktree *find_shared_symref(const char *symref,
73 const char *target);
74
75 +/*
76 + * Similar to head_ref() for all HEADs _except_ one from the current
77 + * worktree, which is covered by head_ref().
78 + */
79 +int other_head_refs(each_ref_fn fn, void *cb_data);
80 +
81 int is_worktree_being_rebased(const struct worktree *wt, const char *target);
82 int is_worktree_being_bisected(const struct worktree *wt, const char *target);
83