revision.c: --all adds HEAD from all worktrees
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>
Wed, 23 Aug 2017 12:36:59 +0000 (19:36 +0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 24 Aug 2017 21:56:43 +0000 (14:56 -0700)
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>
reachable.c
revision.c
submodule.c
t/t5304-prune.sh
worktree.c
worktree.h
index c62efbfd43b9c4ddc25d6768d55bddc745f5c14b..492e87b9fa00c392ef799567dc6ef60e2dfb18a5 100644 (file)
@@ -9,6 +9,7 @@
 #include "cache-tree.h"
 #include "progress.h"
 #include "list-objects.h"
+#include "worktree.h"
 
 struct connectivity_progress {
        struct progress *progress;
@@ -176,6 +177,7 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
 
        /* detached HEAD is not included in the list above */
        head_ref(add_one_ref, revs);
+       other_head_refs(add_one_ref, revs);
 
        /* Add all reflog info */
        if (mark_reflog)
index 8d045162661a81e779b18e1295d470f4194d2f50..0e9844485720ecd77b0e46bac242f691019b0bef 100644 (file)
@@ -2133,6 +2133,14 @@ static int handle_revision_pseudo_opt(const char *submodule,
        int argcount;
 
        if (submodule) {
+               /*
+                * We need some something like get_submodule_worktrees()
+                * before we can go through all worktrees of a submodule,
+                * .e.g with adding all HEADs from --all, which is not
+                * supported right now, so stick to single worktree.
+                */
+               if (!revs->single_worktree)
+                       die("BUG: --single-worktree cannot be used together with submodule");
                refs = get_submodule_ref_store(submodule);
        } else
                refs = get_main_ref_store();
@@ -2150,6 +2158,12 @@ static int handle_revision_pseudo_opt(const char *submodule,
        if (!strcmp(arg, "--all")) {
                handle_refs(refs, revs, *flags, refs_for_each_ref);
                handle_refs(refs, revs, *flags, refs_head_ref);
+               if (!revs->single_worktree) {
+                       struct all_refs_cb cb;
+
+                       init_all_refs_cb(&cb, revs, *flags);
+                       other_head_refs(handle_one_ref, &cb);
+               }
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--branches")) {
                handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
index 98e1f9d3c7b61bb14527fb67b82cb5aa0ebe4ead..61a38adcd477205c1d8edd0aae0d2e89248e9cd3 100644 (file)
@@ -1685,6 +1685,8 @@ static int find_first_merges(struct object_array *result, const char *path,
                        oid_to_hex(&a->object.oid));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
+       /* FIXME: can't handle linked worktrees in submodules yet */
+       revs.single_worktree = path != NULL;
        setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
 
        /* save all revisions from the above list that contain b */
index cba45c7be91279f11a5ad603e9c21b334d7cb615..683bdb031c7d7913b6e4e69e20c9faecf2bc1bbc 100755 (executable)
@@ -292,4 +292,16 @@ test_expect_success 'prune: handle index in multiple worktrees' '
        test_cmp second-worktree/blob actual
 '
 
+test_expect_success 'prune: handle HEAD in multiple worktrees' '
+       git worktree add --detach third-worktree &&
+       echo "new blob for third-worktree" >third-worktree/blob &&
+       git -C third-worktree add blob &&
+       git -C third-worktree commit -m "third" &&
+       rm .git/worktrees/third-worktree/index &&
+       test_must_fail git -C third-worktree show :blob &&
+       git prune --expire=now &&
+       git -C third-worktree show HEAD:blob >actual &&
+       test_cmp third-worktree/blob actual
+'
+
 test_done
index e28ffbeb096a85d67b2e885c48ab096340b5d295..389e2e952cb92fbf1f42b717310ed186fc704eb5 100644 (file)
@@ -386,3 +386,25 @@ int submodule_uses_worktrees(const char *path)
        closedir(dir);
        return ret;
 }
+
+int other_head_refs(each_ref_fn fn, void *cb_data)
+{
+       struct worktree **worktrees, **p;
+       int ret = 0;
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+               struct ref_store *refs;
+
+               if (wt->is_current)
+                       continue;
+
+               refs = get_worktree_ref_store(wt);
+               ret = refs_head_ref(refs, fn, cb_data);
+               if (ret)
+                       break;
+       }
+       free_worktrees(worktrees);
+       return ret;
+}
index 5ea5e503fbe491a76d3c8ce279b7e281a5d07d3e..9276c81ae7cb5c42a86fd9ebbdb28720be409e47 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef WORKTREE_H
 #define WORKTREE_H
 
+#include "refs.h"
+
 struct worktree {
        char *path;
        char *id;
@@ -70,6 +72,12 @@ extern void free_worktrees(struct worktree **);
 extern const struct worktree *find_shared_symref(const char *symref,
                                                 const char *target);
 
+/*
+ * Similar to head_ref() for all HEADs _except_ one from the current
+ * worktree, which is covered by head_ref().
+ */
+int other_head_refs(each_ref_fn fn, void *cb_data);
+
 int is_worktree_being_rebased(const struct worktree *wt, const char *target);
 int is_worktree_being_bisected(const struct worktree *wt, const char *target);