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

1  2 
reachable.c
refs.c
refs/files-backend.c
revision.c
submodule.c
worktree.c
diff --combined reachable.c
index d1ac5d97efdaff5dc971e38da278dcf1d820ef2c,492e87b9fa00c392ef799567dc6ef60e2dfb18a5..88d7d679da9c837463f74d9a9277025b008635af
@@@ -9,7 -9,7 +9,8 @@@
  #include "cache-tree.h"
  #include "progress.h"
  #include "list-objects.h"
 +#include "packfile.h"
+ #include "worktree.h"
  
  struct connectivity_progress {
        struct progress *progress;
@@@ -177,6 -177,7 +178,7 @@@ void mark_reachable_objects(struct rev_
  
        /* 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 --combined refs.c
index b0106b8162c7b8e3770e04c212abb05f1afbd0c3,206af61d6241c25d29c1a5b5db1a049edf56d62e..a2b97fba780debe440203ff78f8203b5d140d3c2
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -336,12 -336,6 +336,6 @@@ int for_each_tag_ref(each_ref_fn fn, vo
        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 +346,6 @@@ int for_each_branch_ref(each_ref_fn fn
        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 +356,6 @@@ int for_each_remote_ref(each_ref_fn fn
        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;
@@@ -579,21 -561,6 +561,21 @@@ enum ref_type ref_type(const char *refn
         return REF_TYPE_NORMAL;
  }
  
 +long get_files_ref_lock_timeout_ms(void)
 +{
 +      static int configured = 0;
 +
 +      /* The default timeout is 100 ms: */
 +      static int timeout_ms = 100;
 +
 +      if (!configured) {
 +              git_config_get_int("core.filesreflocktimeout", &timeout_ms);
 +              configured = 1;
 +      }
 +
 +      return timeout_ms;
 +}
 +
  static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
                           const unsigned char *old_sha1, struct strbuf *err)
  {
        strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
  
        filename = git_path("%s", pseudoref);
 -      fd = hold_lock_file_for_update(&lock, filename, LOCK_DIE_ON_ERROR);
 +      fd = hold_lock_file_for_update_timeout(&lock, filename,
 +                                             LOCK_DIE_ON_ERROR,
 +                                             get_files_ref_lock_timeout_ms());
        if (fd < 0) {
                strbuf_addf(err, "could not open '%s' for writing: %s",
                            filename, strerror(errno));
@@@ -651,9 -616,8 +633,9 @@@ static int delete_pseudoref(const char 
                int fd;
                unsigned char actual_old_sha1[20];
  
 -              fd = hold_lock_file_for_update(&lock, filename,
 -                                             LOCK_DIE_ON_ERROR);
 +              fd = hold_lock_file_for_update_timeout(
 +                              &lock, filename, LOCK_DIE_ON_ERROR,
 +                              get_files_ref_lock_timeout_ms());
                if (fd < 0)
                        die_errno(_("Could not open '%s' for writing"), filename);
                if (read_ref(pseudoref, actual_old_sha1))
@@@ -1266,19 -1230,13 +1248,13 @@@ int refs_rename_ref_available(struct re
        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;
  
  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 -1302,6 +1320,6 @@@ int for_each_ref(each_ref_fn fn, void *
        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 -1323,15 +1341,15 @@@ int for_each_fullref_in(const char *pre
                               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 -1466,10 +1484,10 @@@ const char *resolve_ref_unsafe(const ch
  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 -1584,32 +1602,32 @@@ struct ref_store *get_submodule_ref_sto
  {
        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,
        register_ref_store_map(&submodule_ref_stores, "submodule",
                               refs, submodule);
  
+ done:
        strbuf_release(&submodule_sb);
+       free(to_free);
        return refs;
  }
  
diff --combined refs/files-backend.c
index 3a7bf6a146b04456238e330f314c30e224d4c6e6,d4d22882efc981284fc39f1be77ef74228d94896..cda790dcbc9389121093077059eafce514eda23a
@@@ -106,15 -106,6 +106,6 @@@ static void files_reflog_path(struct fi
                              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:
@@@ -537,9 -528,7 +528,9 @@@ retry
        if (!lock->lk)
                lock->lk = xcalloc(1, sizeof(struct lock_file));
  
 -      if (hold_lock_file_for_update(lock->lk, ref_file.buf, LOCK_NO_DEREF) < 0) {
 +      if (hold_lock_file_for_update_timeout(
 +                          lock->lk, ref_file.buf, LOCK_NO_DEREF,
 +                          get_files_ref_lock_timeout_ms()) < 0) {
                if (errno == ENOENT && --attempts_remaining > 0) {
                        /*
                         * Maybe somebody just deleted one of the
@@@ -867,9 -856,7 +858,9 @@@ static int create_reflock(const char *p
  {
        struct lock_file *lk = cb;
  
 -      return hold_lock_file_for_update(lk, path, LOCK_NO_DEREF) < 0 ? -1 : 0;
 +      return hold_lock_file_for_update_timeout(
 +                      lk, path, LOCK_NO_DEREF,
 +                      get_files_ref_lock_timeout_ms()) < 0 ? -1 : 0;
  }
  
  /*
@@@ -2059,23 -2046,63 +2050,63 @@@ static struct ref_iterator_vtable files
        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.
@@@ -2099,10 -2126,11 +2130,10 @@@ static int split_head_update(struct ref
  
        /*
         * First make sure that HEAD is not already in the
 -       * transaction. This insertion is O(N) in the transaction
 +       * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per transaction.
         */
 -      item = string_list_insert(affected_refnames, "HEAD");
 -      if (item->util) {
 +      if (string_list_has_string(affected_refnames, "HEAD")) {
                /* An entry already existed */
                strbuf_addf(err,
                            "multiple updates for 'HEAD' (including one "
                        update->new_oid.hash, update->old_oid.hash,
                        update->msg);
  
 +      /*
 +       * Add "HEAD". This insertion is O(N) in the transaction
 +       * size, but it happens at most once per transaction.
 +       * Add new_update->refname instead of a literal "HEAD".
 +       */
 +      if (strcmp(new_update->refname, "HEAD"))
 +              BUG("%s unexpectedly not 'HEAD'", new_update->refname);
 +      item = string_list_insert(affected_refnames, new_update->refname);
        item->util = new_update;
  
        return 0;
@@@ -2151,12 -2171,13 +2182,12 @@@ static int split_symref_update(struct f
  
        /*
         * First make sure that referent is not already in the
 -       * transaction. This insertion is O(N) in the transaction
 +       * transaction. This check is O(lg N) in the transaction
         * size, but it happens at most once per symref in a
         * transaction.
         */
 -      item = string_list_insert(affected_refnames, referent);
 -      if (item->util) {
 -              /* An entry already existed */
 +      if (string_list_has_string(affected_refnames, referent)) {
 +              /* An entry already exists */
                strbuf_addf(err,
                            "multiple updates for '%s' (including one "
                            "via symref '%s') are not allowed",
        update->flags |= REF_LOG_ONLY | REF_NODEREF;
        update->flags &= ~REF_HAVE_OLD;
  
 +      /*
 +       * Add the referent. This insertion is O(N) in the transaction
 +       * size, but it happens at most once per symref in a
 +       * transaction. Make sure to add new_update->refname, which will
 +       * be valid as long as affected_refnames is in use, and NOT
 +       * referent, which might soon be freed by our caller.
 +       */
 +      item = string_list_insert(affected_refnames, new_update->refname);
 +      if (item->util)
 +              BUG("%s unexpectedly found in affected_refnames",
 +                  new_update->refname);
        item->util = new_update;
  
        return 0;
@@@ -2273,7 -2283,7 +2304,7 @@@ static int lock_ref_for_update(struct f
        struct strbuf referent = STRBUF_INIT;
        int mustexist = (update->flags & REF_HAVE_OLD) &&
                !is_null_oid(&update->old_oid);
 -      int ret;
 +      int ret = 0;
        struct ref_lock *lock;
  
        files_assert_main_repository(refs, "lock_ref_for_update");
                ret = split_head_update(update, transaction, head_ref,
                                        affected_refnames, err);
                if (ret)
 -                      return ret;
 +                      goto out;
        }
  
        ret = lock_raw_ref(refs, update->refname, mustexist,
                strbuf_addf(err, "cannot lock ref '%s': %s",
                            original_update_refname(update), reason);
                free(reason);
 -              return ret;
 +              goto out;
        }
  
        update->backend_data = lock;
                                        strbuf_addf(err, "cannot lock ref '%s': "
                                                    "error reading reference",
                                                    original_update_refname(update));
 -                                      return -1;
 +                                      ret = TRANSACTION_GENERIC_ERROR;
 +                                      goto out;
                                }
                        } else if (check_old_oid(update, &lock->old_oid, err)) {
 -                              return TRANSACTION_GENERIC_ERROR;
 +                              ret = TRANSACTION_GENERIC_ERROR;
 +                              goto out;
                        }
                } else {
                        /*
                                                  referent.buf, transaction,
                                                  affected_refnames, err);
                        if (ret)
 -                              return ret;
 +                              goto out;
                }
        } else {
                struct ref_update *parent_update;
  
 -              if (check_old_oid(update, &lock->old_oid, err))
 -                      return TRANSACTION_GENERIC_ERROR;
 +              if (check_old_oid(update, &lock->old_oid, err)) {
 +                      ret = TRANSACTION_GENERIC_ERROR;
 +                      goto out;
 +              }
  
                /*
                 * If this update is happening indirectly because of a
                                    "cannot update ref '%s': %s",
                                    update->refname, write_err);
                        free(write_err);
 -                      return TRANSACTION_GENERIC_ERROR;
 +                      ret = TRANSACTION_GENERIC_ERROR;
 +                      goto out;
                } else {
                        update->flags |= REF_NEEDS_COMMIT;
                }
                if (close_ref(lock)) {
                        strbuf_addf(err, "couldn't close '%s.lock'",
                                    update->refname);
 -                      return TRANSACTION_GENERIC_ERROR;
 +                      ret = TRANSACTION_GENERIC_ERROR;
 +                      goto out;
                }
        }
 -      return 0;
 +
 +out:
 +      strbuf_release(&referent);
 +      return ret;
  }
  
  /*
diff --combined revision.c
index 94a5e98525187a2d7f1c6f4e9a22f4cb3bb0cf84,6eba4131b49609506651a69c4dc8f7f0ff1bf07a..f9a90d71d2008415e80b30c9e5d3b2743af5438f
@@@ -19,7 -19,7 +19,8 @@@
  #include "dir.h"
  #include "cache-tree.h"
  #include "bisect.h"
 +#include "packfile.h"
+ #include "worktree.h"
  
  volatile show_early_output_fn_t show_early_output;
  
@@@ -1132,6 -1132,7 +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 -1169,7 +1170,7 @@@ static void init_all_refs_cb(struct all
        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 -1190,19 +1191,19 @@@ void add_ref_exclusion(struct string_li
        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 -1238,41 +1239,41 @@@ static int handle_one_reflog(const cha
        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,
  
  }
  
- 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))
                                             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 -2128,25 +2129,25 @@@ void parse_revision_opt(struct rev_inf
        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,
  {
        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!
         *
         * 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;
                        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;
        }
diff --combined submodule.c
index 3cea8221e0bc3dbe77b157384adcecdfc077c7dc,61a38adcd477205c1d8edd0aae0d2e89248e9cd3..075c55f3ca8101e06731ef3b45e51d11ec00c7f6
@@@ -69,6 -69,13 +69,13 @@@ int is_staging_gitmodules_ok(const stru
        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
@@@ -165,18 -172,31 +172,18 @@@ void set_diffopt_flags_from_submodule_c
  {
        const struct submodule *submodule = submodule_from_path(&null_oid, path);
        if (submodule) {
 -              if (submodule->ignore)
 -                      handle_ignore_submodules_arg(diffopt, submodule->ignore);
 -              else if (is_gitmodules_unmerged(&the_index))
 -                      DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
 -      }
 -}
 +              const char *ignore;
 +              char *key;
  
 -/* For loading from the .gitmodules file. */
 -static int git_modules_config(const char *var, const char *value, void *cb)
 -{
 -      if (starts_with(var, "submodule."))
 -              return parse_submodule_config_option(var, value);
 -      return 0;
 -}
 +              key = xstrfmt("submodule.%s.ignore", submodule->name);
 +              if (repo_config_get_string_const(the_repository, key, &ignore))
 +                      ignore = submodule->ignore;
 +              free(key);
  
 -/* Loads all submodule settings from the config. */
 -int submodule_config(const char *var, const char *value, void *cb)
 -{
 -      if (!strcmp(var, "submodule.recurse")) {
 -              int v = git_config_bool(var, value) ?
 -                      RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
 -              config_update_recurse_submodules = v;
 -              return 0;
 -      } else {
 -              return git_modules_config(var, value, cb);
 +              if (ignore)
 +                      handle_ignore_submodules_arg(diffopt, ignore);
 +              else if (is_gitmodules_unmerged(&the_index))
 +                      DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
        }
  }
  
@@@ -208,6 -228,55 +215,6 @@@ int option_parse_recurse_submodules_wor
        return 0;
  }
  
 -void load_submodule_cache(void)
 -{
 -      if (config_update_recurse_submodules == RECURSE_SUBMODULES_OFF)
 -              return;
 -
 -      gitmodules_config();
 -      git_config(submodule_config, NULL);
 -}
 -
 -static int gitmodules_cb(const char *var, const char *value, void *data)
 -{
 -      struct repository *repo = data;
 -      return submodule_config_option(repo, var, value);
 -}
 -
 -void repo_read_gitmodules(struct repository *repo)
 -{
 -      if (repo->worktree) {
 -              char *gitmodules;
 -
 -              if (repo_read_index(repo) < 0)
 -                      return;
 -
 -              gitmodules = repo_worktree_path(repo, GITMODULES_FILE);
 -
 -              if (!is_gitmodules_unmerged(repo->index))
 -                      git_config_from_file(gitmodules_cb, gitmodules, repo);
 -
 -              free(gitmodules);
 -      }
 -}
 -
 -void gitmodules_config(void)
 -{
 -      repo_read_gitmodules(the_repository);
 -}
 -
 -void gitmodules_config_oid(const struct object_id *commit_oid)
 -{
 -      struct strbuf rev = STRBUF_INIT;
 -      struct object_id oid;
 -
 -      if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {
 -              git_config_from_blob_oid(submodule_config, rev.buf,
 -                                       &oid, NULL);
 -      }
 -      strbuf_release(&rev);
 -}
 -
  /*
   * Determine if a submodule has been initialized at a given 'path'
   */
@@@ -336,38 -405,24 +343,38 @@@ void die_path_inside_submodule(const st
        }
  }
  
 -int parse_submodule_update_strategy(const char *value,
 -              struct submodule_update_strategy *dst)
 +enum submodule_update_type parse_submodule_update_type(const char *value)
  {
 -      free((void*)dst->command);
 -      dst->command = NULL;
        if (!strcmp(value, "none"))
 -              dst->type = SM_UPDATE_NONE;
 +              return SM_UPDATE_NONE;
        else if (!strcmp(value, "checkout"))
 -              dst->type = SM_UPDATE_CHECKOUT;
 +              return SM_UPDATE_CHECKOUT;
        else if (!strcmp(value, "rebase"))
 -              dst->type = SM_UPDATE_REBASE;
 +              return SM_UPDATE_REBASE;
        else if (!strcmp(value, "merge"))
 -              dst->type = SM_UPDATE_MERGE;
 -      else if (skip_prefix(value, "!", &value)) {
 -              dst->type = SM_UPDATE_COMMAND;
 -              dst->command = xstrdup(value);
 -      } else
 +              return SM_UPDATE_MERGE;
 +      else if (*value == '!')
 +              return SM_UPDATE_COMMAND;
 +      else
 +              return SM_UPDATE_UNSPECIFIED;
 +}
 +
 +int parse_submodule_update_strategy(const char *value,
 +              struct submodule_update_strategy *dst)
 +{
 +      enum submodule_update_type type;
 +
 +      free((void*)dst->command);
 +      dst->command = NULL;
 +
 +      type = parse_submodule_update_type(value);
 +      if (type == SM_UPDATE_UNSPECIFIED)
                return -1;
 +
 +      dst->type = type;
 +      if (type == SM_UPDATE_COMMAND)
 +              dst->command = xstrdup(value + 1);
 +
        return 0;
  }
  
@@@ -430,7 -485,9 +437,7 @@@ static int prepare_submodule_summary(st
        return prepare_revision_walk(rev);
  }
  
 -static void print_submodule_summary(struct rev_info *rev, FILE *f,
 -              const char *line_prefix,
 -              const char *del, const char *add, const char *reset)
 +static void print_submodule_summary(struct rev_info *rev, struct diff_options *o)
  {
        static const char format[] = "  %m %s";
        struct strbuf sb = STRBUF_INIT;
                ctx.date_mode = rev->date_mode;
                ctx.output_encoding = get_log_output_encoding();
                strbuf_setlen(&sb, 0);
 -              strbuf_addstr(&sb, line_prefix);
 -              if (commit->object.flags & SYMMETRIC_LEFT) {
 -                      if (del)
 -                              strbuf_addstr(&sb, del);
 -              }
 -              else if (add)
 -                      strbuf_addstr(&sb, add);
                format_commit_message(commit, format, &sb, &ctx);
 -              if (reset)
 -                      strbuf_addstr(&sb, reset);
                strbuf_addch(&sb, '\n');
 -              fprintf(f, "%s", sb.buf);
 +              if (commit->object.flags & SYMMETRIC_LEFT)
 +                      diff_emit_submodule_del(o, sb.buf);
 +              else
 +                      diff_emit_submodule_add(o, sb.buf);
        }
        strbuf_release(&sb);
  }
@@@ -473,9 -536,11 +480,9 @@@ void prepare_submodule_repo_env(struct 
   * attempt to lookup both the left and right commits and put them into the
   * left and right pointers.
   */
 -static void show_submodule_header(FILE *f, const char *path,
 -              const char *line_prefix,
 +static void show_submodule_header(struct diff_options *o, const char *path,
                struct object_id *one, struct object_id *two,
 -              unsigned dirty_submodule, const char *meta,
 -              const char *reset,
 +              unsigned dirty_submodule,
                struct commit **left, struct commit **right,
                struct commit_list **merge_bases)
  {
        int fast_forward = 0, fast_backward = 0;
  
        if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
 -              fprintf(f, "%sSubmodule %s contains untracked content\n",
 -                      line_prefix, path);
 +              diff_emit_submodule_untracked(o, path);
 +
        if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
 -              fprintf(f, "%sSubmodule %s contains modified content\n",
 -                      line_prefix, path);
 +              diff_emit_submodule_modified(o, path);
  
        if (is_null_oid(one))
                message = "(new submodule)";
        }
  
  output_header:
 -      strbuf_addf(&sb, "%s%sSubmodule %s ", line_prefix, meta, path);
 +      strbuf_addf(&sb, "Submodule %s ", path);
        strbuf_add_unique_abbrev(&sb, one->hash, DEFAULT_ABBREV);
        strbuf_addstr(&sb, (fast_backward || fast_forward) ? ".." : "...");
        strbuf_add_unique_abbrev(&sb, two->hash, DEFAULT_ABBREV);
        if (message)
 -              strbuf_addf(&sb, " %s%s\n", message, reset);
 +              strbuf_addf(&sb, " %s\n", message);
        else
 -              strbuf_addf(&sb, "%s:%s\n", fast_backward ? " (rewind)" : "", reset);
 -      fwrite(sb.buf, sb.len, 1, f);
 +              strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");
 +      diff_emit_submodule_header(o, sb.buf);
  
        strbuf_release(&sb);
  }
  
 -void show_submodule_summary(FILE *f, const char *path,
 -              const char *line_prefix,
 +void show_submodule_summary(struct diff_options *o, const char *path,
                struct object_id *one, struct object_id *two,
 -              unsigned dirty_submodule, const char *meta,
 -              const char *del, const char *add, const char *reset)
 +              unsigned dirty_submodule)
  {
        struct rev_info rev;
        struct commit *left = NULL, *right = NULL;
        struct commit_list *merge_bases = NULL;
  
 -      show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
 -                            meta, reset, &left, &right, &merge_bases);
 +      show_submodule_header(o, path, one, two, dirty_submodule,
 +                            &left, &right, &merge_bases);
  
        /*
         * If we don't have both a left and a right pointer, there is no
  
        /* Treat revision walker failure the same as missing commits */
        if (prepare_submodule_summary(&rev, path, left, right, merge_bases)) {
 -              fprintf(f, "%s(revision walker failed)\n", line_prefix);
 +              diff_emit_submodule_error(o, "(revision walker failed)\n");
                goto out;
        }
  
 -      print_submodule_summary(&rev, f, line_prefix, del, add, reset);
 +      print_submodule_summary(&rev, o);
  
  out:
        if (merge_bases)
        clear_commit_marks(right, ~0);
  }
  
 -void show_submodule_inline_diff(FILE *f, const char *path,
 -              const char *line_prefix,
 +void show_submodule_inline_diff(struct diff_options *o, const char *path,
                struct object_id *one, struct object_id *two,
 -              unsigned dirty_submodule, const char *meta,
 -              const char *del, const char *add, const char *reset,
 -              const struct diff_options *o)
 +              unsigned dirty_submodule)
  {
        const struct object_id *old = &empty_tree_oid, *new = &empty_tree_oid;
        struct commit *left = NULL, *right = NULL;
        struct commit_list *merge_bases = NULL;
 -      struct strbuf submodule_dir = STRBUF_INIT;
        struct child_process cp = CHILD_PROCESS_INIT;
 +      struct strbuf sb = STRBUF_INIT;
  
 -      show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
 -                            meta, reset, &left, &right, &merge_bases);
 +      show_submodule_header(o, path, one, two, dirty_submodule,
 +                            &left, &right, &merge_bases);
  
        /* We need a valid left and right commit to display a difference */
        if (!(left || is_null_oid(one)) ||
        if (right)
                new = two;
  
 -      fflush(f);
        cp.git_cmd = 1;
        cp.dir = path;
 -      cp.out = dup(fileno(f));
 +      cp.out = -1;
        cp.no_stdin = 1;
  
        /* TODO: other options may need to be passed here. */
        argv_array_pushl(&cp.args, "diff", "--submodule=diff", NULL);
 +      argv_array_pushf(&cp.args, "--color=%s", want_color(o->use_color) ?
 +                       "always" : "never");
  
 -      argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix);
        if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
                argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
                                 o->b_prefix, path);
                argv_array_push(&cp.args, oid_to_hex(new));
  
        prepare_submodule_repo_env(&cp.env_array);
 -      if (run_command(&cp))
 -              fprintf(f, "(diff failed)\n");
 +      if (start_command(&cp))
 +              diff_emit_submodule_error(o, "(diff failed)\n");
 +
 +      while (strbuf_getwholeline_fd(&sb, cp.out, '\n') != EOF)
 +              diff_emit_submodule_pipethrough(o, sb.buf, sb.len);
 +
 +      if (finish_command(&cp))
 +              diff_emit_submodule_error(o, "(diff failed)\n");
  
  done:
 -      strbuf_release(&submodule_dir);
 +      strbuf_release(&sb);
        if (merge_bases)
                free_commit_list(merge_bases);
        if (left)
@@@ -1082,6 -1147,7 +1089,6 @@@ int submodule_touches_in_range(struct o
        struct argv_array args = ARGV_ARRAY_INIT;
        int ret;
  
 -      gitmodules_config();
        /* No need to check if there are no submodules configured */
        if (!submodule_from_path(NULL, NULL))
                return 0;
@@@ -1130,27 -1196,19 +1137,27 @@@ static int get_next_submodule(struct ch
                        continue;
  
                submodule = submodule_from_path(&null_oid, ce->name);
 -              if (!submodule)
 -                      submodule = submodule_from_name(&null_oid, ce->name);
  
                default_argv = "yes";
                if (spf->command_line_option == RECURSE_SUBMODULES_DEFAULT) {
 -                      if (submodule &&
 -                          submodule->fetch_recurse !=
 -                                              RECURSE_SUBMODULES_NONE) {
 -                              if (submodule->fetch_recurse ==
 -                                              RECURSE_SUBMODULES_OFF)
 +                      int fetch_recurse = RECURSE_SUBMODULES_NONE;
 +
 +                      if (submodule) {
 +                              char *key;
 +                              const char *value;
 +
 +                              fetch_recurse = submodule->fetch_recurse;
 +                              key = xstrfmt("submodule.%s.fetchRecurseSubmodules", submodule->name);
 +                              if (!repo_config_get_string_const(the_repository, key, &value)) {
 +                                      fetch_recurse = parse_fetch_recurse_submodules_arg(key, value);
 +                              }
 +                              free(key);
 +                      }
 +
 +                      if (fetch_recurse != RECURSE_SUBMODULES_NONE) {
 +                              if (fetch_recurse == RECURSE_SUBMODULES_OFF)
                                        continue;
 -                              if (submodule->fetch_recurse ==
 -                                              RECURSE_SUBMODULES_ON_DEMAND) {
 +                              if (fetch_recurse == RECURSE_SUBMODULES_ON_DEMAND) {
                                        if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
                                                continue;
                                        default_argv = "on-demand";
@@@ -1627,6 -1685,8 +1634,8 @@@ static int find_first_merges(struct obj
                        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 */
@@@ -1988,6 -2048,7 +1997,6 @@@ int submodule_to_gitdir(struct strbuf *
                strbuf_addstr(buf, git_dir);
        }
        if (!is_git_directory(buf->buf)) {
 -              gitmodules_config();
                sub = submodule_from_path(&null_oid, submodule);
                if (!sub) {
                        ret = -1;
diff --combined worktree.c
index c0c5a2b3735c89fc98d7aa29863eb4a28651d808,389e2e952cb92fbf1f42b717310ed186fc704eb5..8aaeea03771cfefa9edd019d0dc5146fef17a9c7
@@@ -30,7 -30,7 +30,7 @@@ static void add_head_info(struct worktr
  
        target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
                                         "HEAD",
 -                                       RESOLVE_REF_READING,
 +                                       0,
                                         wt->head_sha1, &flags);
        if (!target)
                return;
@@@ -386,3 -386,25 +386,25 @@@ int submodule_uses_worktrees(const cha
        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;
+ }