Merge branch 'nd/prune-in-worktree'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 01:47:53 +0000 (10:47 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Sep 2017 01:47:53 +0000 (10:47 +0900)
"git gc" and friends when multiple worktrees are used off of a
single repository did not consider the index and per-worktree refs
of other worktrees as the root for reachability traversal, making
objects that are in use only in other worktrees to be subject to
garbage collection.

* nd/prune-in-worktree:
refs.c: reindent get_submodule_ref_store()
refs.c: remove fallback-to-main-store code get_submodule_ref_store()
rev-list: expose and document --single-worktree
revision.c: --reflog add HEAD reflog from all worktrees
files-backend: make reflog iterator go through per-worktree reflog
revision.c: --all adds HEAD from all worktrees
refs: remove dead for_each_*_submodule()
refs.c: move for_each_remote_ref_submodule() to submodule.c
revision.c: use refs_for_each*() instead of for_each_*_submodule()
refs: add refs_head_ref()
refs: move submodule slash stripping code to get_submodule_ref_store
refs.c: refactor get_submodule_ref_store(), share common free block
revision.c: --indexed-objects add objects from all worktrees
revision.c: refactor add_index_objects_to_pending()
refs.c: use is_dir_sep() in resolve_gitlink_ref()
revision.h: new flag in struct rev_info wrt. worktree-related refs

13 files changed:
Documentation/rev-list-options.txt
Documentation/technical/api-ref-iteration.txt
reachable.c
refs.c
refs.h
refs/files-backend.c
revision.c
revision.h
submodule.c
t/t1407-worktree-ref-store.sh
t/t5304-prune.sh
worktree.c
worktree.h
index a6cf9eb380fabedd0e89b84d182e6e2c4798e503..7d860bfca1442e7957c41b7a7a5cd6f82141ed7e 100644 (file)
@@ -184,6 +184,14 @@ explicitly.
        Pretend as if all objects mentioned by reflogs are listed on the
        command line as `<commit>`.
 
+--single-worktree::
+       By default, all working trees will be examined by the
+       following options when there are more than one (see
+       linkgit:git-worktree[1]): `--all`, `--reflog` and
+       `--indexed-objects`.
+       This option forces them to examine the current working tree
+       only.
+
 --ignore-missing::
        Upon seeing an invalid object name in the input, pretend as if
        the bad input was not given.
index 37379d83376fc2342d1a81570b3de86b8e7dcd49..46c3d5c3550a1ba7c83b6937fe68538c3c12074e 100644 (file)
@@ -32,11 +32,8 @@ Iteration functions
 
 * `for_each_glob_ref_in()` the previous and `for_each_ref_in()` combined.
 
-* `head_ref_submodule()`, `for_each_ref_submodule()`,
-  `for_each_ref_in_submodule()`, `for_each_tag_ref_submodule()`,
-  `for_each_branch_ref_submodule()`, `for_each_remote_ref_submodule()`
-  do the same as the functions described above but for a specified
-  submodule.
+* Use `refs_` API for accessing submodules. The submodule ref store could
+  be obtained with `get_submodule_ref_store()`.
 
 * `for_each_rawref()` can be used to learn about broken ref and symref.
 
index d1ac5d97efdaff5dc971e38da278dcf1d820ef2c..88d7d679da9c837463f74d9a9277025b008635af 100644 (file)
@@ -10,6 +10,7 @@
 #include "progress.h"
 #include "list-objects.h"
 #include "packfile.h"
+#include "worktree.h"
 
 struct connectivity_progress {
        struct progress *progress;
@@ -177,6 +178,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)
diff --git a/refs.c b/refs.c
index b0106b8162c7b8e3770e04c212abb05f1afbd0c3..a2b97fba780debe440203ff78f8203b5d140d3c2 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -336,12 +336,6 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_tag_ref(get_submodule_ref_store(submodule),
-                                    fn, cb_data);
-}
-
 int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
@@ -352,12 +346,6 @@ int for_each_branch_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_branch_ref(get_submodule_ref_store(submodule),
-                                       fn, cb_data);
-}
-
 int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
@@ -368,12 +356,6 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
-                                       fn, cb_data);
-}
-
 int head_ref_namespaced(each_ref_fn fn, void *cb_data)
 {
        struct strbuf buf = STRBUF_INIT;
@@ -1266,19 +1248,13 @@ int refs_rename_ref_available(struct ref_store *refs,
        return ok;
 }
 
-int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+int refs_head_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
        struct object_id oid;
        int flag;
 
-       if (submodule) {
-               if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
-                       return fn("HEAD", &oid, 0, cb_data);
-
-               return 0;
-       }
-
-       if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
+       if (!refs_read_ref_full(refs, "HEAD", RESOLVE_REF_READING,
+                               oid.hash, &flag))
                return fn("HEAD", &oid, flag, cb_data);
 
        return 0;
@@ -1286,7 +1262,7 @@ int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 
 int head_ref(each_ref_fn fn, void *cb_data)
 {
-       return head_ref_submodule(NULL, fn, cb_data);
+       return refs_head_ref(get_main_ref_store(), fn, cb_data);
 }
 
 struct ref_iterator *refs_ref_iterator_begin(
@@ -1344,11 +1320,6 @@ int for_each_ref(each_ref_fn fn, void *cb_data)
        return refs_for_each_ref(get_main_ref_store(), fn, cb_data);
 }
 
-int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_ref(get_submodule_ref_store(submodule), fn, cb_data);
-}
-
 int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
                         each_ref_fn fn, void *cb_data)
 {
@@ -1370,23 +1341,15 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig
                               prefix, fn, 0, flag, cb_data);
 }
 
-int for_each_ref_in_submodule(const char *submodule, const char *prefix,
-                             each_ref_fn fn, void *cb_data)
-{
-       return refs_for_each_ref_in(get_submodule_ref_store(submodule),
-                                   prefix, fn, cb_data);
-}
-
-int for_each_fullref_in_submodule(const char *submodule, const char *prefix,
-                                 each_ref_fn fn, void *cb_data,
-                                 unsigned int broken)
+int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+                            each_ref_fn fn, void *cb_data,
+                            unsigned int broken)
 {
        unsigned int flag = 0;
 
        if (broken)
                flag = DO_FOR_EACH_INCLUDE_BROKEN;
-       return do_for_each_ref(get_submodule_ref_store(submodule),
-                              prefix, fn, 0, flag, cb_data);
+       return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
 }
 
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
@@ -1521,25 +1484,10 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
 int resolve_gitlink_ref(const char *submodule, const char *refname,
                        unsigned char *sha1)
 {
-       size_t len = strlen(submodule);
        struct ref_store *refs;
        int flags;
 
-       while (len && submodule[len - 1] == '/')
-               len--;
-
-       if (!len)
-               return -1;
-
-       if (submodule[len]) {
-               /* We need to strip off one or more trailing slashes */
-               char *stripped = xmemdupz(submodule, len);
-
-               refs = get_submodule_ref_store(stripped);
-               free(stripped);
-       } else {
-               refs = get_submodule_ref_store(submodule);
-       }
+       refs = get_submodule_ref_store(submodule);
 
        if (!refs)
                return -1;
@@ -1654,31 +1602,32 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
 {
        struct strbuf submodule_sb = STRBUF_INIT;
        struct ref_store *refs;
-       int ret;
+       char *to_free = NULL;
+       size_t len;
 
-       if (!submodule || !*submodule) {
-               /*
-                * FIXME: This case is ideally not allowed. But that
-                * can't happen until we clean up all the callers.
-                */
-               return get_main_ref_store();
-       }
+       if (!submodule)
+               return NULL;
+
+       len = strlen(submodule);
+       while (len && is_dir_sep(submodule[len - 1]))
+               len--;
+       if (!len)
+               return NULL;
+
+       if (submodule[len])
+               /* We need to strip off one or more trailing slashes */
+               submodule = to_free = xmemdupz(submodule, len);
 
        refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
        if (refs)
-               return refs;
+               goto done;
 
        strbuf_addstr(&submodule_sb, submodule);
-       ret = is_nonbare_repository_dir(&submodule_sb);
-       strbuf_release(&submodule_sb);
-       if (!ret)
-               return NULL;
+       if (!is_nonbare_repository_dir(&submodule_sb))
+               goto done;
 
-       ret = submodule_to_gitdir(&submodule_sb, submodule);
-       if (ret) {
-               strbuf_release(&submodule_sb);
-               return NULL;
-       }
+       if (submodule_to_gitdir(&submodule_sb, submodule))
+               goto done;
 
        /* assume that add_submodule_odb() has been called */
        refs = ref_store_init(submodule_sb.buf,
@@ -1686,7 +1635,10 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
        register_ref_store_map(&submodule_ref_stores, "submodule",
                               refs, submodule);
 
+done:
        strbuf_release(&submodule_sb);
+       free(to_free);
+
        return refs;
 }
 
diff --git a/refs.h b/refs.h
index 6daa78eb50ddc195c6d5bfb568d0b05ab08f7923..78a26400b61b0046c72a64b985b9aa6443f27021 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -275,6 +275,8 @@ typedef int each_ref_fn(const char *refname,
  * modifies the reference also returns a nonzero value to immediately
  * stop the iteration. Returned references are sorted.
  */
+int refs_head_ref(struct ref_store *refs,
+                 each_ref_fn fn, void *cb_data);
 int refs_for_each_ref(struct ref_store *refs,
                      each_ref_fn fn, void *cb_data);
 int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
@@ -289,6 +291,9 @@ int refs_for_each_remote_ref(struct ref_store *refs,
 int head_ref(each_ref_fn fn, void *cb_data);
 int for_each_ref(each_ref_fn fn, void *cb_data);
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
+int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
+                            each_ref_fn fn, void *cb_data,
+                            unsigned int broken);
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
                        unsigned int broken);
 int for_each_tag_ref(each_ref_fn fn, void *cb_data);
@@ -299,21 +304,6 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
                         const char *prefix, void *cb_data);
 
-int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
-int for_each_ref_submodule(const char *submodule,
-                          each_ref_fn fn, void *cb_data);
-int for_each_ref_in_submodule(const char *submodule, const char *prefix,
-                             each_ref_fn fn, void *cb_data);
-int for_each_fullref_in_submodule(const char *submodule, const char *prefix,
-                                 each_ref_fn fn, void *cb_data,
-                                 unsigned int broken);
-int for_each_tag_ref_submodule(const char *submodule,
-                              each_ref_fn fn, void *cb_data);
-int for_each_branch_ref_submodule(const char *submodule,
-                                 each_ref_fn fn, void *cb_data);
-int for_each_remote_ref_submodule(const char *submodule,
-                                 each_ref_fn fn, void *cb_data);
-
 int head_ref_namespaced(each_ref_fn fn, void *cb_data);
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
 
index 3a7bf6a146b04456238e330f314c30e224d4c6e6..cda790dcbc9389121093077059eafce514eda23a 100644 (file)
@@ -106,15 +106,6 @@ static void files_reflog_path(struct files_ref_store *refs,
                              struct strbuf *sb,
                              const char *refname)
 {
-       if (!refname) {
-               /*
-                * FIXME: of course this is wrong in multi worktree
-                * setting. To be fixed real soon.
-                */
-               strbuf_addf(sb, "%s/logs", refs->gitcommondir);
-               return;
-       }
-
        switch (ref_type(refname)) {
        case REF_TYPE_PER_WORKTREE:
        case REF_TYPE_PSEUDOREF:
@@ -2059,23 +2050,63 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
        files_reflog_iterator_abort
 };
 
-static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
+                                                 const char *gitdir)
 {
-       struct files_ref_store *refs =
-               files_downcast(ref_store, REF_STORE_READ,
-                              "reflog_iterator_begin");
        struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
        struct ref_iterator *ref_iterator = &iter->base;
        struct strbuf sb = STRBUF_INIT;
 
        base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
-       files_reflog_path(refs, &sb, NULL);
+       strbuf_addf(&sb, "%s/logs", gitdir);
        iter->dir_iterator = dir_iterator_begin(sb.buf);
        iter->ref_store = ref_store;
        strbuf_release(&sb);
+
        return ref_iterator;
 }
 
+static enum iterator_selection reflog_iterator_select(
+       struct ref_iterator *iter_worktree,
+       struct ref_iterator *iter_common,
+       void *cb_data)
+{
+       if (iter_worktree) {
+               /*
+                * We're a bit loose here. We probably should ignore
+                * common refs if they are accidentally added as
+                * per-worktree refs.
+                */
+               return ITER_SELECT_0;
+       } else if (iter_common) {
+               if (ref_type(iter_common->refname) == REF_TYPE_NORMAL)
+                       return ITER_SELECT_1;
+
+               /*
+                * The main ref store may contain main worktree's
+                * per-worktree refs, which should be ignored
+                */
+               return ITER_SKIP_1;
+       } else
+               return ITER_DONE;
+}
+
+static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+{
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ,
+                              "reflog_iterator_begin");
+
+       if (!strcmp(refs->gitdir, refs->gitcommondir)) {
+               return reflog_iterator_begin(ref_store, refs->gitcommondir);
+       } else {
+               return merge_ref_iterator_begin(
+                       reflog_iterator_begin(ref_store, refs->gitdir),
+                       reflog_iterator_begin(ref_store, refs->gitcommondir),
+                       reflog_iterator_select, refs);
+       }
+}
+
 /*
  * If update is a direct update of head_ref (the reference pointed to
  * by HEAD), then add an extra REF_LOG_ONLY update for HEAD.
index 94a5e98525187a2d7f1c6f4e9a22f4cb3bb0cf84..f9a90d71d2008415e80b30c9e5d3b2743af5438f 100644 (file)
@@ -20,6 +20,7 @@
 #include "cache-tree.h"
 #include "bisect.h"
 #include "packfile.h"
+#include "worktree.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -1132,6 +1133,7 @@ struct all_refs_cb {
        int warned_bad_reflog;
        struct rev_info *all_revs;
        const char *name_for_errormsg;
+       struct ref_store *refs;
 };
 
 int ref_excluded(struct string_list *ref_excludes, const char *path)
@@ -1168,6 +1170,7 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->all_revs = revs;
        cb->all_flags = flags;
        revs->rev_input_given = 1;
+       cb->refs = NULL;
 }
 
 void clear_ref_exclusion(struct string_list **ref_excludes_p)
@@ -1188,12 +1191,19 @@ void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
        string_list_append(*ref_excludes_p, exclude);
 }
 
-static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
-               int (*for_each)(const char *, each_ref_fn, void *))
+static void handle_refs(struct ref_store *refs,
+                       struct rev_info *revs, unsigned flags,
+                       int (*for_each)(struct ref_store *, each_ref_fn, void *))
 {
        struct all_refs_cb cb;
+
+       if (!refs) {
+               /* this could happen with uninitialized submodules */
+               return;
+       }
+
        init_all_refs_cb(&cb, revs, flags);
-       for_each(submodule, handle_one_ref, &cb);
+       for_each(refs, handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
@@ -1229,17 +1239,41 @@ static int handle_one_reflog(const char *path, const struct object_id *oid,
        struct all_refs_cb *cb = cb_data;
        cb->warned_bad_reflog = 0;
        cb->name_for_errormsg = path;
-       for_each_reflog_ent(path, handle_one_reflog_ent, cb_data);
+       refs_for_each_reflog_ent(cb->refs, path,
+                                handle_one_reflog_ent, cb_data);
        return 0;
 }
 
+static void add_other_reflogs_to_pending(struct all_refs_cb *cb)
+{
+       struct worktree **worktrees, **p;
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+
+               if (wt->is_current)
+                       continue;
+
+               cb->refs = get_worktree_ref_store(wt);
+               refs_for_each_reflog(cb->refs,
+                                    handle_one_reflog,
+                                    cb);
+       }
+       free_worktrees(worktrees);
+}
+
 void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
 {
        struct all_refs_cb cb;
 
        cb.all_revs = revs;
        cb.all_flags = flags;
+       cb.refs = get_main_ref_store();
        for_each_reflog(handle_one_reflog, &cb);
+
+       if (!revs->single_worktree)
+               add_other_reflogs_to_pending(&cb);
 }
 
 static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
@@ -1263,13 +1297,13 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
 
 }
 
-void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
+static void do_add_index_objects_to_pending(struct rev_info *revs,
+                                           struct index_state *istate)
 {
        int i;
 
-       read_cache();
-       for (i = 0; i < active_nr; i++) {
-               struct cache_entry *ce = active_cache[i];
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
                struct blob *blob;
 
                if (S_ISGITLINK(ce->ce_mode))
@@ -1282,13 +1316,39 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
                                             ce->ce_mode, ce->name);
        }
 
-       if (active_cache_tree) {
+       if (istate->cache_tree) {
                struct strbuf path = STRBUF_INIT;
-               add_cache_tree(active_cache_tree, revs, &path);
+               add_cache_tree(istate->cache_tree, revs, &path);
                strbuf_release(&path);
        }
 }
 
+void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
+{
+       struct worktree **worktrees, **p;
+
+       read_cache();
+       do_add_index_objects_to_pending(revs, &the_index);
+
+       if (revs->single_worktree)
+               return;
+
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+               struct index_state istate = { NULL };
+
+               if (wt->is_current)
+                       continue; /* current index already taken care of */
+
+               if (read_index_from(&istate,
+                                   worktree_git_path(wt, "index")) > 0)
+                       do_add_index_objects_to_pending(revs, &istate);
+               discard_index(&istate);
+       }
+       free_worktrees(worktrees);
+}
+
 static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
                            int exclude_parent)
 {
@@ -2069,23 +2129,25 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
        ctx->argc -= n;
 }
 
-static int for_each_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data, const char *term) {
+static int for_each_bisect_ref(struct ref_store *refs, each_ref_fn fn,
+                              void *cb_data, const char *term)
+{
        struct strbuf bisect_refs = STRBUF_INIT;
        int status;
        strbuf_addf(&bisect_refs, "refs/bisect/%s", term);
-       status = for_each_fullref_in_submodule(submodule, bisect_refs.buf, fn, cb_data, 0);
+       status = refs_for_each_fullref_in(refs, bisect_refs.buf, fn, cb_data, 0);
        strbuf_release(&bisect_refs);
        return status;
 }
 
-static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return for_each_bisect_ref(submodule, fn, cb_data, term_bad);
+       return for_each_bisect_ref(refs, fn, cb_data, term_bad);
 }
 
-static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return for_each_bisect_ref(submodule, fn, cb_data, term_good);
+       return for_each_bisect_ref(refs, fn, cb_data, term_good);
 }
 
 static int handle_revision_pseudo_opt(const char *submodule,
@@ -2094,8 +2156,22 @@ static int handle_revision_pseudo_opt(const char *submodule,
 {
        const char *arg = argv[0];
        const char *optarg;
+       struct ref_store *refs;
        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();
+
        /*
         * NOTE!
         *
@@ -2107,22 +2183,29 @@ static int handle_revision_pseudo_opt(const char *submodule,
         * register it in the list at the top of handle_revision_opt.
         */
        if (!strcmp(arg, "--all")) {
-               handle_refs(submodule, revs, *flags, for_each_ref_submodule);
-               handle_refs(submodule, revs, *flags, head_ref_submodule);
+               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(submodule, revs, *flags, for_each_branch_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_branch_ref);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--bisect")) {
                read_bisect_terms(&term_bad, &term_good);
-               handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
-               handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
+               handle_refs(refs, revs, *flags, for_each_bad_bisect_ref);
+               handle_refs(refs, revs, *flags ^ (UNINTERESTING | BOTTOM),
+                           for_each_good_bisect_ref);
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
-               handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_tag_ref);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--remotes")) {
-               handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
+               handle_refs(refs, revs, *flags, refs_for_each_remote_ref);
                clear_ref_exclusion(&revs->ref_excludes);
        } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
                struct all_refs_cb cb;
@@ -2169,6 +2252,8 @@ static int handle_revision_pseudo_opt(const char *submodule,
                        return error("invalid argument to --no-walk");
        } else if (!strcmp(arg, "--do-walk")) {
                revs->no_walk = 0;
+       } else if (!strcmp(arg, "--single-worktree")) {
+               revs->single_worktree = 1;
        } else {
                return 0;
        }
index bc18487d6fff5e86dff60748c084995a77d2b588..3a3d3e2cf824bf0ae404204c1ea81dad813d5f93 100644 (file)
@@ -96,6 +96,7 @@ struct rev_info {
                        topo_order:1,
                        simplify_merges:1,
                        simplify_by_decoration:1,
+                       single_worktree:1,
                        tag_objects:1,
                        tree_objects:1,
                        blob_objects:1,
index 3cea8221e0bc3dbe77b157384adcecdfc077c7dc..075c55f3ca8101e06731ef3b45e51d11ec00c7f6 100644 (file)
@@ -69,6 +69,13 @@ int is_staging_gitmodules_ok(const struct index_state *istate)
        return 1;
 }
 
+static int for_each_remote_ref_submodule(const char *submodule,
+                                        each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
+                                       fn, cb_data);
+}
+
 /*
  * Try to update the "path" entry in the "submodule.<name>" section of the
  * .gitmodules file. Return 0 only if a .gitmodules file was found, a section
@@ -1627,6 +1634,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 5df06f355641ad7a38daf34f9fc841e7fe4f8a0f..8842d0329fb7947e811d4ffccff3bba9e8885d5d 100755 (executable)
@@ -49,4 +49,34 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
        test_cmp expected actual
 '
 
+test_expect_success 'for_each_reflog()' '
+       echo $_z40 > .git/logs/PSEUDO-MAIN &&
+       mkdir -p     .git/logs/refs/bisect &&
+       echo $_z40 > .git/logs/refs/bisect/random &&
+
+       echo $_z40 > .git/worktrees/wt/logs/PSEUDO-WT &&
+       mkdir -p     .git/worktrees/wt/logs/refs/bisect &&
+       echo $_z40 > .git/worktrees/wt/logs/refs/bisect/wt-random &&
+
+       $RWT for-each-reflog | cut -c 42- | sort >actual &&
+       cat >expected <<-\EOF &&
+       HEAD 0x1
+       PSEUDO-WT 0x0
+       refs/bisect/wt-random 0x0
+       refs/heads/master 0x0
+       refs/heads/wt-master 0x0
+       EOF
+       test_cmp expected actual &&
+
+       $RMAIN for-each-reflog | cut -c 42- | sort >actual &&
+       cat >expected <<-\EOF &&
+       HEAD 0x1
+       PSEUDO-MAIN 0x0
+       refs/bisect/random 0x0
+       refs/heads/master 0x0
+       refs/heads/wt-master 0x0
+       EOF
+       test_cmp expected actual
+'
+
 test_done
index 133b5842b1aee8e8e00eb0ca7e1b2d32ff4a4c9a..6694c19a1eecf10117b843bbacbc5bb47924c9c8 100755 (executable)
@@ -283,4 +283,41 @@ test_expect_success 'prune: handle alternate object database' '
        git -C B prune
 '
 
+test_expect_success 'prune: handle index in multiple worktrees' '
+       git worktree add second-worktree &&
+       echo "new blob for second-worktree" >second-worktree/blob &&
+       git -C second-worktree add blob &&
+       git prune --expire=now &&
+       git -C second-worktree show :blob >actual &&
+       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_expect_success 'prune: handle HEAD reflog in multiple worktrees' '
+       git config core.logAllRefUpdates true &&
+       echo "lost blob for third-worktree" >expected &&
+       (
+               cd third-worktree &&
+               cat ../expected >blob &&
+               git add blob &&
+               git commit -m "second commit in third" &&
+               git reset --hard HEAD^
+       ) &&
+       git prune --expire=now &&
+       SHA1=`git hash-object expected` &&
+       git -C third-worktree show "$SHA1" >actual &&
+       test_cmp expected actual
+'
+
 test_done
index c0c5a2b3735c89fc98d7aa29863eb4a28651d808..8aaeea03771cfefa9edd019d0dc5146fef17a9c7 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);