Merge branch 'nd/per-worktree-ref-iteration'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Nov 2018 13:37:26 +0000 (22:37 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Nov 2018 13:37:26 +0000 (22:37 +0900)
The code to traverse objects for reachability, used to decide what
objects are unreferenced and expendable, have been taught to also
consider per-worktree refs of other worktrees as starting points to
prevent data loss.

* nd/per-worktree-ref-iteration:
git-worktree.txt: correct linkgit command name
reflog expire: cover reflog from all worktrees
fsck: check HEAD and reflog from other worktrees
fsck: move fsck_head_link() to get_default_heads() to avoid some globals
revision.c: better error reporting on ref from different worktrees
revision.c: correct a parameter name
refs: new ref types to make per-worktree refs visible to all worktrees
Add a place for (not) sharing stuff between worktrees
refs.c: indent with tabs, not spaces

1  2 
Documentation/git-worktree.txt
Documentation/gitrepository-layout.txt
builtin/fsck.c
builtin/reflog.c
path.c
refs.c
refs.h
revision.c
t/t1450-fsck.sh
worktree.c
worktree.h
index 5e986ce8aaf4a06e6ac21ca6266c8b7066d1a9d2,69d55f13502223c77c407c849f6089c8b5193112..cb86318f3e1b34e0eda5b9886fc934456434e7a8
@@@ -204,36 -204,35 +204,65 @@@ working trees, it can be used to identi
  you only have two working trees, at "/abc/def/ghi" and "/abc/def/ggg",
  then "ghi" or "def/ghi" is enough to point to the former working tree.
  
+ REFS
+ ----
+ In multiple working trees, some refs may be shared between all working
+ trees, some refs are local. One example is HEAD is different for all
+ working trees. This section is about the sharing rules and how to access
+ refs of one working tree from another.
+ In general, all pseudo refs are per working tree and all refs starting
+ with "refs/" are shared. Pseudo refs are ones like HEAD which are
+ directly under GIT_DIR instead of inside GIT_DIR/refs. There are one
+ exception to this: refs inside refs/bisect and refs/worktree is not
+ shared.
+ Refs that are per working tree can still be accessed from another
+ working tree via two special paths, main-worktree and worktrees. The
+ former gives access to per-worktree refs of the main working tree,
+ while the latter to all linked working trees.
+ For example, main-worktree/HEAD or main-worktree/refs/bisect/good
+ resolve to the same value as the main working tree's HEAD and
+ refs/bisect/good respectively. Similarly, worktrees/foo/HEAD or
+ worktrees/bar/refs/bisect/bad are the same as
+ GIT_COMMON_DIR/worktrees/foo/HEAD and
+ GIT_COMMON_DIR/worktrees/bar/refs/bisect/bad.
+ To access refs, it's best not to look inside GIT_DIR directly. Instead
+ use commands such as linkgit:git-rev-parse[1] or linkgit:git-update-ref[1]
+ which will handle refs correctly.
 +CONFIGURATION FILE
 +------------------
 +By default, the repository "config" file is shared across all working
 +trees. If the config variables `core.bare` or `core.worktree` are
 +already present in the config file, they will be applied to the main
 +working trees only.
 +
 +In order to have configuration specific to working trees, you can turn
 +on "worktreeConfig" extension, e.g.:
 +
 +------------
 +$ git config extensions.worktreeConfig true
 +------------
 +
 +In this mode, specific configuration stays in the path pointed by `git
 +rev-parse --git-path config.worktree`. You can add or update
 +configuration in this file with `git config --worktree`. Older Git
 +versions will refuse to access repositories with this extension.
 +
 +Note that in this file, the exception for `core.bare` and `core.worktree`
 +is gone. If you have them in $GIT_DIR/config before, you must move
 +them to the `config.worktree` of the main working tree. You may also
 +take this opportunity to review and move other configuration that you
 +do not want to share to all working trees:
 +
 + - `core.worktree` and `core.bare` should never be shared
 +
 + - `core.sparseCheckout` is recommended per working tree, unless you
 +   are sure you always use sparse checkout for all working trees.
 +
  DETAILS
  -------
  Each linked working tree has a private sub-directory in the repository's
@@@ -258,7 -257,8 +287,8 @@@ linked working tree `git rev-parse --gi
  `/path/other/test-next/.git/HEAD` or `/path/main/.git/HEAD`) while `git
  rev-parse --git-path refs/heads/master` uses
  $GIT_COMMON_DIR and returns `/path/main/.git/refs/heads/master`,
- since refs are shared across all working trees.
+ since refs are shared across all working trees, except refs/bisect and
+ refs/worktree.
  
  See linkgit:gitrepository-layout[5] for more information. The rule of
  thumb is do not make any assumption about whether a path belongs to
@@@ -283,9 -283,6 +313,9 @@@ to `/path/main/.git/worktrees/test-next
  `test-next` entry from being pruned.  See
  linkgit:gitrepository-layout[5] for details.
  
 +When extensions.worktreeConfig is enabled, the config file
 +`.git/worktrees/<id>/config.worktree` is read after `.git/config` is.
 +
  LIST OUTPUT FORMAT
  ------------------
  The worktree list command has two output formats.  The default format shows the
@@@ -303,8 -300,8 +333,8 @@@ Porcelain Forma
  The porcelain format has a line per attribute.  Attributes are listed with a
  label and value separated by a single space.  Boolean attributes (like 'bare'
  and 'detached') are listed as a label only, and are only present if and only
 -if the value is true.  An empty line indicates the end of a worktree.  For
 -example:
 +if the value is true.  The first attribute of a worktree is always `worktree`,
 +an empty line indicates the end of the record.  For example:
  
  ------------
  $ git worktree list --porcelain
index 36fcca80875b524480ca97be5ef668886c29eabb,89b616e0491ed7ee760ae934311777f9eb833be7..d501af9d776edc6890aeea51e3581a3f9bb22049
@@@ -95,8 -95,10 +95,10 @@@ refs:
        References are stored in subdirectories of this
        directory.  The 'git prune' command knows to preserve
        objects reachable from refs found in this directory and
-       its subdirectories. This directory is ignored if $GIT_COMMON_DIR
-       is set and "$GIT_COMMON_DIR/refs" will be used instead.
+       its subdirectories.
+       This directory is ignored (except refs/bisect and
+       refs/worktree) if $GIT_COMMON_DIR is set and
+       "$GIT_COMMON_DIR/refs" will be used instead.
  
  refs/heads/`name`::
        records tip-of-the-tree commit objects of branch `name`
@@@ -143,11 -145,6 +145,11 @@@ config:
        if $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/config" will be
        used instead.
  
 +config.worktree::
 +      Working directory specific configuration file for the main
 +      working directory in multiple working directory setup (see
 +      linkgit:git-worktree[1]).
 +
  branches::
        A slightly deprecated way to store shorthands to be used
        to specify a URL to 'git fetch', 'git pull' and 'git push'.
@@@ -170,6 -167,11 +172,11 @@@ hooks:
        each hook. This directory is ignored if $GIT_COMMON_DIR is set
        and "$GIT_COMMON_DIR/hooks" will be used instead.
  
+ common::
+       When multiple working trees are used, most of files in
+       $GIT_DIR are per-worktree with a few known exceptions. All
+       files under 'common' however will be shared between all
+       working trees.
  
  index::
        The current index file for the repository.  It is
@@@ -280,9 -282,6 +287,9 @@@ worktrees/<id>/locked:
        or manually by `git worktree prune`. The file may contain a string
        explaining why the repository is locked.
  
 +worktrees/<id>/config.worktree::
 +      Working directory specific configuration file.
 +
  SEE ALSO
  --------
  linkgit:git-init[1],
diff --combined builtin/fsck.c
index 06eb42172099a39e6f181d0dd7eab581595d9756,71492c158da8e213b1f2aa29f3e62ad3d86b31dd..3c3e0f06e78009ee3ee2d54333dfc4740b8792a9
@@@ -19,6 -19,7 +19,7 @@@
  #include "packfile.h"
  #include "object-store.h"
  #include "run-command.h"
+ #include "worktree.h"
  
  #define REACHABLE 0x0001
  #define SEEN      0x0002
@@@ -36,8 -37,6 +37,6 @@@ static int check_strict
  static int keep_cache_objects;
  static struct fsck_options fsck_walk_options = FSCK_OPTIONS_DEFAULT;
  static struct fsck_options fsck_obj_options = FSCK_OPTIONS_DEFAULT;
- static struct object_id head_oid;
- static const char *head_points_at;
  static int errors_found;
  static int write_lost_and_found;
  static int verbose;
@@@ -446,7 -445,11 +445,11 @@@ static int fsck_handle_reflog_ent(struc
  static int fsck_handle_reflog(const char *logname, const struct object_id *oid,
                              int flag, void *cb_data)
  {
-       for_each_reflog_ent(logname, fsck_handle_reflog_ent, (void *)logname);
+       struct strbuf refname = STRBUF_INIT;
+       strbuf_worktree_ref(cb_data, &refname, logname);
+       for_each_reflog_ent(refname.buf, fsck_handle_reflog_ent, refname.buf);
+       strbuf_release(&refname);
        return 0;
  }
  
@@@ -484,13 -487,34 +487,34 @@@ static int fsck_handle_ref(const char *
        return 0;
  }
  
+ static int fsck_head_link(const char *head_ref_name,
+                         const char **head_points_at,
+                         struct object_id *head_oid);
  static void get_default_heads(void)
  {
-       if (head_points_at && !is_null_oid(&head_oid))
-               fsck_handle_ref("HEAD", &head_oid, 0, NULL);
+       struct worktree **worktrees, **p;
+       const char *head_points_at;
+       struct object_id head_oid;
        for_each_rawref(fsck_handle_ref, NULL);
-       if (include_reflogs)
-               for_each_reflog(fsck_handle_reflog, NULL);
+       worktrees = get_worktrees(0);
+       for (p = worktrees; *p; p++) {
+               struct worktree *wt = *p;
+               struct strbuf ref = STRBUF_INIT;
+               strbuf_worktree_ref(wt, &ref, "HEAD");
+               fsck_head_link(ref.buf, &head_points_at, &head_oid);
+               if (head_points_at && !is_null_oid(&head_oid))
+                       fsck_handle_ref(ref.buf, &head_oid, 0, NULL);
+               strbuf_release(&ref);
+               if (include_reflogs)
+                       refs_for_each_reflog(get_worktree_ref_store(wt),
+                                            fsck_handle_reflog, wt);
+       }
+       free_worktrees(worktrees);
  
        /*
         * Not having any default heads isn't really fatal, but
@@@ -579,33 -603,36 +603,36 @@@ static void fsck_object_dir(const char 
        stop_progress(&progress);
  }
  
- static int fsck_head_link(void)
+ static int fsck_head_link(const char *head_ref_name,
+                         const char **head_points_at,
+                         struct object_id *head_oid)
  {
        int null_is_error = 0;
  
        if (verbose)
-               fprintf(stderr, "Checking HEAD link\n");
+               fprintf(stderr, "Checking %s link\n", head_ref_name);
  
-       head_points_at = resolve_ref_unsafe("HEAD", 0, &head_oid, NULL);
-       if (!head_points_at) {
+       *head_points_at = resolve_ref_unsafe(head_ref_name, 0, head_oid, NULL);
+       if (!*head_points_at) {
                errors_found |= ERROR_REFS;
-               return error("Invalid HEAD");
+               return error("Invalid %s", head_ref_name);
        }
-       if (!strcmp(head_points_at, "HEAD"))
+       if (!strcmp(*head_points_at, head_ref_name))
                /* detached HEAD */
                null_is_error = 1;
-       else if (!starts_with(head_points_at, "refs/heads/")) {
+       else if (!starts_with(*head_points_at, "refs/heads/")) {
                errors_found |= ERROR_REFS;
-               return error("HEAD points to something strange (%s)",
-                            head_points_at);
+               return error("%s points to something strange (%s)",
+                            head_ref_name, *head_points_at);
        }
-       if (is_null_oid(&head_oid)) {
+       if (is_null_oid(head_oid)) {
                if (null_is_error) {
                        errors_found |= ERROR_REFS;
-                       return error("HEAD: detached HEAD points at nothing");
+                       return error("%s: detached HEAD points at nothing",
+                                    head_ref_name);
                }
-               fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
-                       head_points_at + 11);
+               fprintf(stderr, "notice: %s points to an unborn branch (%s)\n",
+                       head_ref_name, *head_points_at + 11);
        }
        return 0;
  }
@@@ -720,7 -747,6 +747,6 @@@ int cmd_fsck(int argc, const char **arg
  
        git_config(fsck_config, NULL);
  
-       fsck_head_link();
        if (connectivity_only) {
                for_each_loose_object(mark_loose_for_connectivity, NULL, 0);
                for_each_packed_object(mark_packed_for_connectivity, NULL, 0);
                }
        }
  
 +      if (!git_config_get_bool("core.multipackindex", &i) && i) {
 +              struct child_process midx_verify = CHILD_PROCESS_INIT;
 +              const char *midx_argv[] = { "multi-pack-index", "verify", NULL, NULL, NULL };
 +
 +              midx_verify.argv = midx_argv;
 +              midx_verify.git_cmd = 1;
 +              if (run_command(&midx_verify))
 +                      errors_found |= ERROR_COMMIT_GRAPH;
 +
 +              prepare_alt_odb(the_repository);
 +              for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
 +                      midx_argv[2] = "--object-dir";
 +                      midx_argv[3] = alt->path;
 +                      if (run_command(&midx_verify))
 +                              errors_found |= ERROR_COMMIT_GRAPH;
 +              }
 +      }
 +
        return errors_found;
  }
diff --combined builtin/reflog.c
index b5941c1ff325b981593f0e54fd7f32f956696d8d,ba36d086e96f65a8cdc458bd64dcffcc01e7ecca..7a85e4b1640b12d4c8f7958cb59b66e037fd2ce2
@@@ -10,6 -10,7 +10,7 @@@
  #include "diff.h"
  #include "revision.h"
  #include "reachable.h"
+ #include "worktree.h"
  
  /* NEEDSWORK: switch to using parse_options */
  static const char reflog_expire_usage[] =
@@@ -52,6 -53,7 +53,7 @@@ struct collect_reflog_cb 
        struct collected_reflog **e;
        int alloc;
        int nr;
+       struct worktree *wt;
  };
  
  /* Remember to update object flag allocation in object.h */
@@@ -330,13 -332,27 +332,27 @@@ static int push_tip_to_list(const char 
        return 0;
  }
  
+ static int is_head(const char *refname)
+ {
+       switch (ref_type(refname)) {
+       case REF_TYPE_OTHER_PSEUDOREF:
+       case REF_TYPE_MAIN_PSEUDOREF:
+               if (parse_worktree_ref(refname, NULL, NULL, &refname))
+                       BUG("not a worktree ref: %s", refname);
+               break;
+       default:
+               break;
+       }
+       return !strcmp(refname, "HEAD");
+ }
  static void reflog_expiry_prepare(const char *refname,
                                  const struct object_id *oid,
                                  void *cb_data)
  {
        struct expire_reflog_policy_cb *cb = cb_data;
  
-       if (!cb->cmd.expire_unreachable || !strcmp(refname, "HEAD")) {
+       if (!cb->cmd.expire_unreachable || is_head(refname)) {
                cb->tip_commit = NULL;
                cb->unreachable_expire_kind = UE_HEAD;
        } else {
@@@ -388,8 -404,19 +404,19 @@@ static int collect_reflog(const char *r
  {
        struct collected_reflog *e;
        struct collect_reflog_cb *cb = cb_data;
+       struct strbuf newref = STRBUF_INIT;
+       /*
+        * Avoid collecting the same shared ref multiple times because
+        * they are available via all worktrees.
+        */
+       if (!cb->wt->is_current && ref_type(ref) == REF_TYPE_NORMAL)
+               return 0;
+       strbuf_worktree_ref(cb->wt, &newref, ref);
+       FLEX_ALLOC_STR(e, reflog, newref.buf);
+       strbuf_release(&newref);
  
-       FLEX_ALLOC_STR(e, reflog, ref);
        oidcpy(&e->oid, oid);
        ALLOC_GROW(cb->e, cb->nr + 1, cb->alloc);
        cb->e[cb->nr++] = e;
@@@ -512,7 -539,7 +539,7 @@@ static int cmd_reflog_expire(int argc, 
  {
        struct expire_reflog_policy_cb cb;
        timestamp_t now = time(NULL);
-       int i, status, do_all;
+       int i, status, do_all, all_worktrees = 1;
        int explicit_expiry = 0;
        unsigned int flags = 0;
  
                        flags |= EXPIRE_REFLOGS_UPDATE_REF;
                else if (!strcmp(arg, "--all"))
                        do_all = 1;
+               else if (!strcmp(arg, "--single-worktree"))
+                       all_worktrees = 0;
                else if (!strcmp(arg, "--verbose"))
                        flags |= EXPIRE_REFLOGS_VERBOSE;
                else if (!strcmp(arg, "--")) {
         * from reflog if the repository was pruned with older git.
         */
        if (cb.cmd.stalefix) {
 -              init_revisions(&cb.cmd.revs, prefix);
 +              repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
                if (flags & EXPIRE_REFLOGS_VERBOSE)
                        printf("Marking reachable objects...");
                mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
  
        if (do_all) {
                struct collect_reflog_cb collected;
+               struct worktree **worktrees, **p;
                int i;
  
                memset(&collected, 0, sizeof(collected));
-               for_each_reflog(collect_reflog, &collected);
+               worktrees = get_worktrees(0);
+               for (p = worktrees; *p; p++) {
+                       if (!all_worktrees && !(*p)->is_current)
+                               continue;
+                       collected.wt = *p;
+                       refs_for_each_reflog(get_worktree_ref_store(*p),
+                                            collect_reflog, &collected);
+               }
+               free_worktrees(worktrees);
                for (i = 0; i < collected.nr; i++) {
                        struct collected_reflog *e = collected.e[i];
                        set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
diff --combined path.c
index ba06ec5b2d6cd307c7d30384b056cf8a6c61baa8,bf4bb02a27be020efbd3afb560c40cba0ad84043..dc3294c71e1e72ae2aae9164c790e27d1237e956
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -108,6 -108,7 +108,7 @@@ struct common_dir 
  
  static struct common_dir common_list[] = {
        { 0, 1, 0, "branches" },
+       { 0, 1, 0, "common" },
        { 0, 1, 0, "hooks" },
        { 0, 1, 0, "info" },
        { 0, 0, 1, "info/sparse-checkout" },
        { 0, 1, 0, "objects" },
        { 0, 1, 0, "refs" },
        { 0, 1, 1, "refs/bisect" },
+       { 0, 1, 1, "refs/worktree" },
        { 0, 1, 0, "remotes" },
        { 0, 1, 0, "worktrees" },
        { 0, 1, 0, "rr-cache" },
@@@ -1369,7 -1371,7 +1371,7 @@@ only_spaces_and_periods
                        saw_tilde = 1;
                } else if (i >= 6)
                        return 0;
 -              else if (name[i] 0) {
 +              else if (name[i] & 0x80) {
                        /*
                         * We know our needles contain only ASCII, so we clamp
                         * here to make the results of tolower() sane.
diff --combined refs.c
index bbcac921b6d78fd598380608e7195675aae1d4f6,2378b2e7fc39385902d93ef6c8f5c3924c2a7ba8..17e4307f3171697ddd8c1e49710c16218e6774a8
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -624,6 -624,7 +624,7 @@@ int dwim_log(const char *str, int len, 
  static int is_per_worktree_ref(const char *refname)
  {
        return !strcmp(refname, "HEAD") ||
+               starts_with(refname, "refs/worktree/") ||
                starts_with(refname, "refs/bisect/") ||
                starts_with(refname, "refs/rewritten/");
  }
@@@ -640,13 -641,34 +641,34 @@@ static int is_pseudoref_syntax(const ch
        return 1;
  }
  
+ static int is_main_pseudoref_syntax(const char *refname)
+ {
+       return skip_prefix(refname, "main-worktree/", &refname) &&
+               *refname &&
+               is_pseudoref_syntax(refname);
+ }
+ static int is_other_pseudoref_syntax(const char *refname)
+ {
+       if (!skip_prefix(refname, "worktrees/", &refname))
+               return 0;
+       refname = strchr(refname, '/');
+       if (!refname || !refname[1])
+               return 0;
+       return is_pseudoref_syntax(refname + 1);
+ }
  enum ref_type ref_type(const char *refname)
  {
        if (is_per_worktree_ref(refname))
                return REF_TYPE_PER_WORKTREE;
        if (is_pseudoref_syntax(refname))
                return REF_TYPE_PSEUDOREF;
-        return REF_TYPE_NORMAL;
+       if (is_main_pseudoref_syntax(refname))
+               return REF_TYPE_MAIN_PSEUDOREF;
+       if (is_other_pseudoref_syntax(refname))
+               return REF_TYPE_OTHER_PSEUDOREF;
+       return REF_TYPE_NORMAL;
  }
  
  long get_files_ref_lock_timeout_ms(void)
@@@ -1394,50 -1416,17 +1416,50 @@@ struct ref_iterator *refs_ref_iterator_
   * non-zero value, stop the iteration and return that value;
   * otherwise, return 0.
   */
 +static int do_for_each_repo_ref(struct repository *r, const char *prefix,
 +                              each_repo_ref_fn fn, int trim, int flags,
 +                              void *cb_data)
 +{
 +      struct ref_iterator *iter;
 +      struct ref_store *refs = get_main_ref_store(r);
 +
 +      if (!refs)
 +              return 0;
 +
 +      iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
 +
 +      return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
 +}
 +
 +struct do_for_each_ref_help {
 +      each_ref_fn *fn;
 +      void *cb_data;
 +};
 +
 +static int do_for_each_ref_helper(struct repository *r,
 +                                const char *refname,
 +                                const struct object_id *oid,
 +                                int flags,
 +                                void *cb_data)
 +{
 +      struct do_for_each_ref_help *hp = cb_data;
 +
 +      return hp->fn(refname, oid, flags, hp->cb_data);
 +}
 +
  static int do_for_each_ref(struct ref_store *refs, const char *prefix,
                           each_ref_fn fn, int trim, int flags, void *cb_data)
  {
        struct ref_iterator *iter;
 +      struct do_for_each_ref_help hp = { fn, cb_data };
  
        if (!refs)
                return 0;
  
        iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
  
 -      return do_for_each_ref_iterator(iter, fn, cb_data);
 +      return do_for_each_repo_ref_iterator(the_repository, iter,
 +                                      do_for_each_ref_helper, &hp);
  }
  
  int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@@ -1482,11 -1471,12 +1504,11 @@@ int refs_for_each_fullref_in(struct ref
        return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
  }
  
 -int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data)
 +int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
  {
 -      return do_for_each_ref(get_main_ref_store(r),
 -                             git_replace_ref_base, fn,
 -                             strlen(git_replace_ref_base),
 -                             DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 +      return do_for_each_repo_ref(r, git_replace_ref_base, fn,
 +                                  strlen(git_replace_ref_base),
 +                                  DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
  }
  
  int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
@@@ -2065,12 -2055,10 +2087,12 @@@ cleanup
  int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
  {
        struct ref_iterator *iter;
 +      struct do_for_each_ref_help hp = { fn, cb_data };
  
        iter = refs->be->reflog_iterator_begin(refs);
  
 -      return do_for_each_ref_iterator(iter, fn, cb_data);
 +      return do_for_each_repo_ref_iterator(the_repository, iter,
 +                                           do_for_each_ref_helper, &hp);
  }
  
  int for_each_reflog(each_ref_fn fn, void *cb_data)
diff --combined refs.h
index 6cc0397679fd55bfdfc72b7a7f4cbf3ee5d028a0,9b53dbeae8f5656c6dd008ecfacd0e491a5de94e..308fa1f03b26c151f2df76a0c4f2f675e7bbee5a
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -276,16 -276,6 +276,16 @@@ struct ref_transaction
  typedef int each_ref_fn(const char *refname,
                        const struct object_id *oid, int flags, void *cb_data);
  
 +/*
 + * The same as each_ref_fn, but also with a repository argument that
 + * contains the repository associated with the callback.
 + */
 +typedef int each_repo_ref_fn(struct repository *r,
 +                           const char *refname,
 +                           const struct object_id *oid,
 +                           int flags,
 +                           void *cb_data);
 +
  /*
   * The following functions invoke the specified callback function for
   * each reference indicated.  If the function ever returns a nonzero
@@@ -319,7 -309,7 +319,7 @@@ int for_each_fullref_in(const char *pre
  int for_each_tag_ref(each_ref_fn fn, void *cb_data);
  int for_each_branch_ref(each_ref_fn fn, void *cb_data);
  int for_each_remote_ref(each_ref_fn fn, void *cb_data);
 -int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data);
 +int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);
  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);
@@@ -714,9 -704,11 +714,11 @@@ int parse_hide_refs_config(const char *
  int ref_is_hidden(const char *, const char *);
  
  enum ref_type {
-       REF_TYPE_PER_WORKTREE,
-       REF_TYPE_PSEUDOREF,
-       REF_TYPE_NORMAL,
+       REF_TYPE_PER_WORKTREE,    /* refs inside refs/ but not shared       */
+       REF_TYPE_PSEUDOREF,       /* refs outside refs/ in current worktree */
+       REF_TYPE_MAIN_PSEUDOREF,  /* pseudo refs from the main worktree     */
+       REF_TYPE_OTHER_PSEUDOREF, /* pseudo refs from other worktrees       */
+       REF_TYPE_NORMAL,          /* normal/shared refs inside refs/        */
  };
  
  enum ref_type ref_type(const char *refname);
diff --combined revision.c
index 28fb2a70cdaaab93e8665a37c2ce02f89a90ed4c,8ce660e3b1c4165e97def27265e2e8560499b50e..59a3c22b8a6d9827721121727f5f903128d3812a
@@@ -52,8 -52,7 +52,8 @@@ static void mark_blob_uninteresting(str
        blob->object.flags |= UNINTERESTING;
  }
  
 -static void mark_tree_contents_uninteresting(struct tree *tree)
 +static void mark_tree_contents_uninteresting(struct repository *r,
 +                                           struct tree *tree)
  {
        struct tree_desc desc;
        struct name_entry entry;
        while (tree_entry(&desc, &entry)) {
                switch (object_type(entry.mode)) {
                case OBJ_TREE:
 -                      mark_tree_uninteresting(lookup_tree(the_repository, entry.oid));
 +                      mark_tree_uninteresting(r, lookup_tree(r, entry.oid));
                        break;
                case OBJ_BLOB:
 -                      mark_blob_uninteresting(lookup_blob(the_repository, entry.oid));
 +                      mark_blob_uninteresting(lookup_blob(r, entry.oid));
                        break;
                default:
                        /* Subproject commit - not in this repository */
@@@ -83,7 -82,7 +83,7 @@@
        free_tree_buffer(tree);
  }
  
 -void mark_tree_uninteresting(struct tree *tree)
 +void mark_tree_uninteresting(struct repository *r, struct tree *tree)
  {
        struct object *obj;
  
@@@ -94,7 -93,7 +94,7 @@@
        if (obj->flags & UNINTERESTING)
                return;
        obj->flags |= UNINTERESTING;
 -      mark_tree_contents_uninteresting(tree);
 +      mark_tree_contents_uninteresting(r, tree);
  }
  
  struct commit_stack {
@@@ -177,6 -176,7 +177,6 @@@ static void add_pending_object_with_pat
                strbuf_release(&buf);
                return; /* do not add the commit itself */
        }
 -      obj->flags |= USER_GIVEN;
        add_object_array_with_path(obj, name, &revs->pending, mode, path);
  }
  
@@@ -199,7 -199,7 +199,7 @@@ void add_head_to_pending(struct rev_inf
        struct object *obj;
        if (get_oid("HEAD", &oid))
                return;
 -      obj = parse_object(the_repository, &oid);
 +      obj = parse_object(revs->repo, &oid);
        if (!obj)
                return;
        add_pending_object(revs, obj, "HEAD");
@@@ -211,7 -211,7 +211,7 @@@ static struct object *get_reference(str
  {
        struct object *object;
  
 -      object = parse_object(the_repository, oid);
 +      object = parse_object(revs->repo, oid);
        if (!object) {
                if (revs->ignore_missing)
                        return object;
@@@ -248,7 -248,7 +248,7 @@@ static struct commit *handle_commit(str
                        add_pending_object(revs, object, tag->tag);
                if (!tag->tagged)
                        die("bad tag");
 -              object = parse_object(the_repository, &tag->tagged->oid);
 +              object = parse_object(revs->repo, &tag->tagged->oid);
                if (!object) {
                        if (revs->ignore_missing_links || (flags & UNINTERESTING))
                                return NULL;
                if (!revs->tree_objects)
                        return NULL;
                if (flags & UNINTERESTING) {
 -                      mark_tree_contents_uninteresting(tree);
 +                      mark_tree_contents_uninteresting(revs->repo, tree);
                        return NULL;
                }
                add_pending_object_with_path(revs, object, name, mode, path);
@@@ -878,7 -878,7 +878,7 @@@ static void cherry_pick_list(struct com
                return;
  
        left_first = left_count < right_count;
 -      init_patch_ids(&ids);
 +      init_patch_ids(revs->repo, &ids);
        ids.diffopts.pathspec = revs->diffopt.pathspec;
  
        /* Compute patch-ids for one side */
@@@ -1177,7 -1177,7 +1177,7 @@@ struct all_refs_cb 
        int warned_bad_reflog;
        struct rev_info *all_revs;
        const char *name_for_errormsg;
-       struct ref_store *refs;
+       struct worktree *wt;
  };
  
  int ref_excluded(struct string_list *ref_excludes, const char *path)
@@@ -1214,7 -1214,7 +1214,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;
+       cb->wt = NULL;
  }
  
  void clear_ref_exclusion(struct string_list **ref_excludes_p)
@@@ -1254,7 -1254,7 +1254,7 @@@ static void handle_one_reflog_commit(st
  {
        struct all_refs_cb *cb = cb_data;
        if (!is_null_oid(oid)) {
 -              struct object *o = parse_object(the_repository, oid);
 +              struct object *o = parse_object(cb->all_revs->repo, oid);
                if (o) {
                        o->flags |= cb->all_flags;
                        /* ??? CMDLINEFLAGS ??? */
@@@ -1277,14 -1277,20 +1277,20 @@@ static int handle_one_reflog_ent(struc
        return 0;
  }
  
- static int handle_one_reflog(const char *path, const struct object_id *oid,
+ static int handle_one_reflog(const char *refname_in_wt,
+                            const struct object_id *oid,
                             int flag, void *cb_data)
  {
        struct all_refs_cb *cb = cb_data;
+       struct strbuf refname = STRBUF_INIT;
        cb->warned_bad_reflog = 0;
-       cb->name_for_errormsg = path;
-       refs_for_each_reflog_ent(cb->refs, path,
+       strbuf_worktree_ref(cb->wt, &refname, refname_in_wt);
+       cb->name_for_errormsg = refname.buf;
+       refs_for_each_reflog_ent(get_main_ref_store(the_repository),
+                                refname.buf,
                                 handle_one_reflog_ent, cb_data);
+       strbuf_release(&refname);
        return 0;
  }
  
@@@ -1299,8 -1305,8 +1305,8 @@@ static void add_other_reflogs_to_pendin
                if (wt->is_current)
                        continue;
  
-               cb->refs = get_worktree_ref_store(wt);
-               refs_for_each_reflog(cb->refs,
+               cb->wt = wt;
+               refs_for_each_reflog(get_worktree_ref_store(wt),
                                     handle_one_reflog,
                                     cb);
        }
@@@ -1313,7 -1319,7 +1319,7 @@@ void add_reflogs_to_pending(struct rev_
  
        cb.all_revs = revs;
        cb.all_flags = flags;
-       cb.refs = get_main_ref_store(revs->repo);
+       cb.wt = NULL;
        for_each_reflog(handle_one_reflog, &cb);
  
        if (!revs->single_worktree)
@@@ -1327,7 -1333,7 +1333,7 @@@ static void add_cache_tree(struct cache
        int i;
  
        if (it->entry_count >= 0) {
 -              struct tree *tree = lookup_tree(the_repository, &it->oid);
 +              struct tree *tree = lookup_tree(revs->repo, &it->oid);
                add_pending_object_with_path(revs, &tree->object, "",
                                             040000, path->buf);
        }
@@@ -1353,7 -1359,7 +1359,7 @@@ static void do_add_index_objects_to_pen
                if (S_ISGITLINK(ce->ce_mode))
                        continue;
  
 -              blob = lookup_blob(the_repository, &ce->oid);
 +              blob = lookup_blob(revs->repo, &ce->oid);
                if (!blob)
                        die("unable to add index blob to traversal");
                add_pending_object_with_path(revs, &blob->object, "",
@@@ -1371,8 -1377,8 +1377,8 @@@ void add_index_objects_to_pending(struc
  {
        struct worktree **worktrees, **p;
  
 -      read_cache();
 -      do_add_index_objects_to_pending(revs, &the_index);
 +      read_index(revs->repo->index);
 +      do_add_index_objects_to_pending(revs, revs->repo->index);
  
        if (revs->single_worktree)
                return;
@@@ -1440,13 -1446,10 +1446,13 @@@ static int add_parents_only(struct rev_
        return 1;
  }
  
 -void init_revisions(struct rev_info *revs, const char *prefix)
 +void repo_init_revisions(struct repository *r,
 +                       struct rev_info *revs,
 +                       const char *prefix)
  {
        memset(revs, 0, sizeof(*revs));
  
 +      revs->repo = r;
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
        revs->commit_format = CMIT_FMT_DEFAULT;
        revs->expand_tabs_in_log_default = 8;
  
 -      init_grep_defaults();
 -      grep_init(&revs->grep_filter, prefix);
 +      init_grep_defaults(revs->repo);
 +      grep_init(&revs->grep_filter, revs->repo, prefix);
        revs->grep_filter.status_only = 1;
  
 -      diff_setup(&revs->diffopt);
 +      repo_diff_setup(revs->repo, &revs->diffopt);
        if (prefix && !revs->diffopt.prefix) {
                revs->diffopt.prefix = prefix;
                revs->diffopt.prefix_length = strlen(prefix);
@@@ -1500,7 -1503,6 +1506,7 @@@ static void prepare_show_merge(struct r
        struct object_id oid;
        const char **prune = NULL;
        int i, prune_num = 1; /* counting terminating NULL */
 +      struct index_state *istate = revs->repo->index;
  
        if (get_oid("HEAD", &oid))
                die("--merge without HEAD?");
        free_commit_list(bases);
        head->object.flags |= SYMMETRIC_LEFT;
  
 -      if (!active_nr)
 -              read_cache();
 -      for (i = 0; i < active_nr; i++) {
 -              const struct cache_entry *ce = active_cache[i];
 +      if (!istate->cache_nr)
 +              read_index(istate);
 +      for (i = 0; i < istate->cache_nr; i++) {
 +              const struct cache_entry *ce = istate->cache[i];
                if (!ce_stage(ce))
                        continue;
 -              if (ce_path_match(&the_index, ce, &revs->prune_data, NULL)) {
 +              if (ce_path_match(istate, ce, &revs->prune_data, NULL)) {
                        prune_num++;
                        REALLOC_ARRAY(prune, prune_num);
                        prune[prune_num-2] = ce->name;
                        prune[prune_num-1] = NULL;
                }
 -              while ((i+1 < active_nr) &&
 -                     ce_same_name(ce, active_cache[i+1]))
 +              while ((i+1 < istate->cache_nr) &&
 +                     ce_same_name(ce, istate->cache[i+1]))
                        i++;
        }
        clear_pathspec(&revs->prune_data);
@@@ -1586,8 -1588,8 +1592,8 @@@ static int handle_dotdot_1(const char *
                *dotdot = '\0';
        }
  
 -      a_obj = parse_object(the_repository, &a_oid);
 -      b_obj = parse_object(the_repository, &b_oid);
 +      a_obj = parse_object(revs->repo, &a_oid);
 +      b_obj = parse_object(revs->repo, &b_oid);
        if (!a_obj || !b_obj)
                return dotdot_missing(arg, dotdot, revs, symmetric);
  
                struct commit *a, *b;
                struct commit_list *exclude;
  
 -              a = lookup_commit_reference(the_repository, &a_obj->oid);
 -              b = lookup_commit_reference(the_repository, &b_obj->oid);
 +              a = lookup_commit_reference(revs->repo, &a_obj->oid);
 +              b = lookup_commit_reference(revs->repo, &b_obj->oid);
                if (!a || !b)
                        return dotdot_missing(arg, dotdot, revs, symmetric);
  
@@@ -2138,8 -2140,7 +2144,8 @@@ static int handle_revision_opt(struct r
                revs->limited = 1;
        } else if (!strcmp(arg, "--ignore-missing")) {
                revs->ignore_missing = 1;
 -      } else if (!strcmp(arg, "--exclude-promisor-objects")) {
 +      } else if (revs->allow_exclude_promisor_objects_opt &&
 +                 !strcmp(arg, "--exclude-promisor-objects")) {
                if (fetch_if_missing)
                        BUG("exclude_promisor_objects can only be used when fetch_if_missing is 0");
                revs->exclude_promisor_objects = 1;
@@@ -2210,7 -2211,7 +2216,7 @@@ static int handle_revision_pseudo_opt(c
                        BUG("--single-worktree cannot be used together with submodule");
                refs = get_submodule_ref_store(submodule);
        } else
 -              refs = get_main_ref_store(the_repository);
 +              refs = get_main_ref_store(revs->repo);
  
        /*
         * NOTE!
@@@ -2890,10 -2891,9 +2896,10 @@@ void reset_revision_walk(void
  static int mark_uninteresting(const struct object_id *oid,
                              struct packed_git *pack,
                              uint32_t pos,
 -                            void *unused)
 +                            void *cb)
  {
 -      struct object *o = parse_object(the_repository, oid);
 +      struct rev_info *revs = cb;
 +      struct object *o = parse_object(revs->repo, oid);
        o->flags |= UNINTERESTING | SEEN;
        return 0;
  }
@@@ -2926,7 -2926,7 +2932,7 @@@ int prepare_revision_walk(struct rev_in
                revs->treesame.name = "treesame";
  
        if (revs->exclude_promisor_objects) {
 -              for_each_packed_object(mark_uninteresting, NULL,
 +              for_each_packed_object(mark_uninteresting, revs,
                                       FOR_EACH_OBJECT_PROMISOR_ONLY);
        }
  
diff --combined t/t1450-fsck.sh
index b5677d26a4268505139ec6cb7d65fe35db7debbe,28201677d592284871940c18145d6e7a9f95d74f..e20e8fa8301607b94cff7c42e83545f072d5191f
@@@ -101,6 -101,41 +101,41 @@@ test_expect_success 'HEAD link pointin
        grep "HEAD points to something strange" out
  '
  
+ test_expect_success 'HEAD link pointing at a funny object (from different wt)' '
+       test_when_finished "mv .git/SAVED_HEAD .git/HEAD" &&
+       test_when_finished "rm -rf .git/worktrees wt" &&
+       git worktree add wt &&
+       mv .git/HEAD .git/SAVED_HEAD &&
+       echo $ZERO_OID >.git/HEAD &&
+       # avoid corrupt/broken HEAD from interfering with repo discovery
+       test_must_fail git -C wt fsck 2>out &&
+       grep "main-worktree/HEAD: detached HEAD points" out
+ '
+ test_expect_success 'other worktree HEAD link pointing at a funny object' '
+       test_when_finished "rm -rf .git/worktrees other" &&
+       git worktree add other &&
+       echo $ZERO_OID >.git/worktrees/other/HEAD &&
+       test_must_fail git fsck 2>out &&
+       grep "worktrees/other/HEAD: detached HEAD points" out
+ '
+ test_expect_success 'other worktree HEAD link pointing at missing object' '
+       test_when_finished "rm -rf .git/worktrees other" &&
+       git worktree add other &&
+       echo "Contents missing from repo" | git hash-object --stdin >.git/worktrees/other/HEAD &&
+       test_must_fail git fsck 2>out &&
+       grep "worktrees/other/HEAD: invalid sha1 pointer" out
+ '
+ test_expect_success 'other worktree HEAD link pointing at a funny place' '
+       test_when_finished "rm -rf .git/worktrees other" &&
+       git worktree add other &&
+       echo "ref: refs/funny/place" >.git/worktrees/other/HEAD &&
+       test_must_fail git fsck 2>out &&
+       grep "worktrees/other/HEAD points to something strange" out
+ '
  test_expect_success 'email without @ is okay' '
        git cat-file commit HEAD >basis &&
        sed "s/@/AT/" basis >okay &&
@@@ -673,35 -708,16 +708,35 @@@ test_expect_success 'fsck detects trail
        test_i18ngrep "garbage.*$commit" out
  '
  
 -test_expect_success 'fsck detects trailing loose garbage (blob)' '
 +test_expect_success 'fsck detects trailing loose garbage (large blob)' '
        blob=$(echo trailing | git hash-object -w --stdin) &&
        file=$(sha1_file $blob) &&
        test_when_finished "remove_object $blob" &&
        chmod +w "$file" &&
        echo garbage >>"$file" &&
 -      test_must_fail git fsck 2>out &&
 +      test_must_fail git -c core.bigfilethreshold=5 fsck 2>out &&
        test_i18ngrep "garbage.*$blob" out
  '
  
 +test_expect_success 'fsck detects truncated loose object' '
 +      # make it big enough that we know we will truncate in the data
 +      # portion, not the header
 +      test-tool genrandom truncate 4096 >file &&
 +      blob=$(git hash-object -w file) &&
 +      file=$(sha1_file $blob) &&
 +      test_when_finished "remove_object $blob" &&
 +      test_copy_bytes 1024 <"$file" >tmp &&
 +      rm "$file" &&
 +      mv -f tmp "$file" &&
 +
 +      # check both regular and streaming code paths
 +      test_must_fail git fsck 2>out &&
 +      test_i18ngrep corrupt.*$blob out &&
 +
 +      test_must_fail git -c core.bigfilethreshold=128 fsck 2>out &&
 +      test_i18ngrep corrupt.*$blob out
 +'
 +
  # for each of type, we have one version which is referenced by another object
  # (and so while unreachable, not dangling), and another variant which really is
  # dangling.
diff --combined worktree.c
index befdbe7faef37d8ae1d4a3de896e37fade2f4862,e6a65ec6842d1a18a0463d774be0ab920cbee5a9..d6a0ee7f730d96a7e8cc04095ddafb43c937105a
@@@ -235,7 -235,7 +235,7 @@@ int is_main_worktree(const struct workt
        return !wt->id;
  }
  
 -const char *is_worktree_locked(struct worktree *wt)
 +const char *worktree_lock_reason(struct worktree *wt)
  {
        assert(!is_main_worktree(wt));
  
@@@ -487,6 -487,75 +487,75 @@@ int submodule_uses_worktrees(const cha
        return ret;
  }
  
+ int parse_worktree_ref(const char *worktree_ref, const char **name,
+                      int *name_length, const char **ref)
+ {
+       if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) {
+               if (!*worktree_ref)
+                       return -1;
+               if (name)
+                       *name = NULL;
+               if (name_length)
+                       *name_length = 0;
+               if (ref)
+                       *ref = worktree_ref;
+               return 0;
+       }
+       if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) {
+               const char *slash = strchr(worktree_ref, '/');
+               if (!slash || slash == worktree_ref || !slash[1])
+                       return -1;
+               if (name)
+                       *name = worktree_ref;
+               if (name_length)
+                       *name_length = slash - worktree_ref;
+               if (ref)
+                       *ref = slash + 1;
+               return 0;
+       }
+       return -1;
+ }
+ void strbuf_worktree_ref(const struct worktree *wt,
+                        struct strbuf *sb,
+                        const char *refname)
+ {
+       switch (ref_type(refname)) {
+       case REF_TYPE_PSEUDOREF:
+       case REF_TYPE_PER_WORKTREE:
+               if (wt && !wt->is_current) {
+                       if (is_main_worktree(wt))
+                               strbuf_addstr(sb, "main-worktree/");
+                       else
+                               strbuf_addf(sb, "worktrees/%s/", wt->id);
+               }
+               break;
+       case REF_TYPE_MAIN_PSEUDOREF:
+       case REF_TYPE_OTHER_PSEUDOREF:
+               break;
+       case REF_TYPE_NORMAL:
+               /*
+                * For shared refs, don't prefix worktrees/ or
+                * main-worktree/. It's not necessary and
+                * files-backend.c can't handle it anyway.
+                */
+               break;
+       }
+       strbuf_addstr(sb, refname);
+ }
+ const char *worktree_ref(const struct worktree *wt, const char *refname)
+ {
+       static struct strbuf sb = STRBUF_INIT;
+       strbuf_reset(&sb);
+       strbuf_worktree_ref(wt, &sb, refname);
+       return sb.buf;
+ }
  int other_head_refs(each_ref_fn fn, void *cb_data)
  {
        struct worktree **worktrees, **p;
        worktrees = get_worktrees(0);
        for (p = worktrees; *p; p++) {
                struct worktree *wt = *p;
-               struct ref_store *refs;
+               struct object_id oid;
+               int flag;
  
                if (wt->is_current)
                        continue;
  
-               refs = get_worktree_ref_store(wt);
-               ret = refs_head_ref(refs, fn, cb_data);
+               if (!refs_read_ref_full(get_main_ref_store(the_repository),
+                                       worktree_ref(wt, "HEAD"),
+                                       RESOLVE_REF_READING,
+                                       &oid, &flag))
+                       ret = fn(worktree_ref(wt, "HEAD"), &oid, flag, cb_data);
                if (ret)
                        break;
        }
diff --combined worktree.h
index 55d449b6a9777e5db603dadd6e36fddf1d96b01a,1164ca396ff628412793d62ff8a1b3d7e84ea058..9e3b0b7b6f9bd2aad3de9094febb8ad76aa45caf
@@@ -10,12 -10,12 +10,12 @@@ struct worktree 
        char *path;
        char *id;
        char *head_ref;         /* NULL if HEAD is broken or detached */
 -      char *lock_reason;      /* internal use */
 +      char *lock_reason;      /* private - use worktree_lock_reason */
        struct object_id head_oid;
        int is_detached;
        int is_bare;
        int is_current;
 -      int lock_reason_valid;
 +      int lock_reason_valid; /* private */
  };
  
  /* Functions for acting on the information about worktrees. */
@@@ -60,7 -60,7 +60,7 @@@ extern int is_main_worktree(const struc
   * Return the reason string if the given worktree is locked or NULL
   * otherwise.
   */
 -extern const char *is_worktree_locked(struct worktree *wt);
 +extern const char *worktree_lock_reason(struct worktree *wt);
  
  #define WT_VALIDATE_WORKTREE_MISSING_OK (1 << 0)
  
@@@ -108,4 -108,28 +108,28 @@@ extern const char *worktree_git_path(co
                                     const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
  
+ /*
+  * Parse a worktree ref (i.e. with prefix main-worktree/ or
+  * worktrees/) and return the position of the worktree's name and
+  * length (or NULL and zero if it's main worktree), and ref.
+  *
+  * All name, name_length and ref arguments could be NULL.
+  */
+ int parse_worktree_ref(const char *worktree_ref, const char **name,
+                      int *name_length, const char **ref);
+ /*
+  * Return a refname suitable for access from the current ref store.
+  */
+ void strbuf_worktree_ref(const struct worktree *wt,
+                        struct strbuf *sb,
+                        const char *refname);
+ /*
+  * Return a refname suitable for access from the current ref
+  * store. The result will be destroyed at the next call.
+  */
+ const char *worktree_ref(const struct worktree *wt,
+                        const char *refname);
  #endif