Merge branch 'nd/worktree-various-heads'
authorJunio C Hamano <gitster@pobox.com>
Mon, 23 May 2016 21:54:29 +0000 (14:54 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 23 May 2016 21:54:29 +0000 (14:54 -0700)
The experimental "multiple worktree" feature gains more safety to
forbid operations on a branch that is checked out or being actively
worked on elsewhere, by noticing that e.g. it is being rebased.

* nd/worktree-various-heads:
branch: do not rename a branch under bisect or rebase
worktree.c: check whether branch is bisected in another worktree
wt-status.c: split bisect detection out of wt_status_get_state()
worktree.c: check whether branch is rebased in another worktree
worktree.c: avoid referencing to worktrees[i] multiple times
wt-status.c: make wt_status_check_rebase() work on any worktree
wt-status.c: split rebase detection out of wt_status_get_state()
path.c: refactor and add worktree_git_path()
worktree.c: mark current worktree
worktree.c: make find_shared_symref() return struct worktree *
worktree.c: store "id" instead of "git_dir"
path.c: add git_common_path() and strbuf_git_common_path()
dir.c: rename str(n)cmp_icase to fspath(n)cmp

1  2 
builtin/branch.c
builtin/checkout.c
builtin/worktree.c
cache.h
dir.c
dir.h
fast-import.c
path.c
sha1_file.c
worktree.c
wt-status.c
diff --combined builtin/branch.c
index eacec57593fda5dc15c611f0e92b4a6614c5aa00,b488c3fb3c7b1d51588057a44df784676c297537..2ecde53bf838777e191926557c0c8190cdc7d09a
@@@ -220,12 -220,12 +220,12 @@@ static int delete_branches(int argc, co
                name = mkpathdup(fmt, bname.buf);
  
                if (kinds == FILTER_REFS_BRANCHES) {
-                       char *worktree = find_shared_symref("HEAD", name);
-                       if (worktree) {
+                       const struct worktree *wt =
+                               find_shared_symref("HEAD", name);
+                       if (wt) {
                                error(_("Cannot delete branch '%s' "
                                        "checked out at '%s'"),
-                                     bname.buf, worktree);
-                               free(worktree);
+                                     bname.buf, wt->path);
                                ret = 1;
                                continue;
                        }
@@@ -375,14 -375,12 +375,14 @@@ static char *get_head_description(void
                strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
                            state.branch);
        else if (state.detached_from) {
 -              /* TRANSLATORS: make sure these match _("HEAD detached at ")
 -                 and _("HEAD detached from ") in wt-status.c */
                if (state.detached_at)
 +                      /* TRANSLATORS: make sure this matches
 +                         "HEAD detached at " in wt-status.c */
                        strbuf_addf(&desc, _("(HEAD detached at %s)"),
                                state.detached_from);
                else
 +                      /* TRANSLATORS: make sure this matches
 +                         "HEAD detached from " in wt-status.c */
                        strbuf_addf(&desc, _("(HEAD detached from %s)"),
                                state.detached_from);
        }
@@@ -526,6 -524,29 +526,29 @@@ static void print_ref_list(struct ref_f
        ref_array_clear(&array);
  }
  
+ static void reject_rebase_or_bisect_branch(const char *target)
+ {
+       struct worktree **worktrees = get_worktrees();
+       int i;
+       for (i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+               if (!wt->is_detached)
+                       continue;
+               if (is_worktree_being_rebased(wt, target))
+                       die(_("Branch %s is being rebased at %s"),
+                           target, wt->path);
+               if (is_worktree_being_bisected(wt, target))
+                       die(_("Branch %s is being bisected at %s"),
+                           target, wt->path);
+       }
+       free_worktrees(worktrees);
+ }
  static void rename_branch(const char *oldname, const char *newname, int force)
  {
        struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
  
        validate_new_branchname(newname, &newref, force, clobber_head_ok);
  
+       reject_rebase_or_bisect_branch(oldref.buf);
        strbuf_addf(&logmsg, "Branch: renamed %s to %s",
                 oldref.buf, newref.buf);
  
@@@ -595,7 -618,8 +620,7 @@@ static int edit_branch_description(cons
                    branch_name, comment_line_char);
        if (write_file_gently(git_path(edit_description), "%s", buf.buf)) {
                strbuf_release(&buf);
 -              return error(_("could not write branch description template: %s"),
 -                           strerror(errno));
 +              return error_errno(_("could not write branch description template"));
        }
        strbuf_reset(&buf);
        if (launch_editor(git_path(edit_description), &buf, NULL)) {
@@@ -631,7 -655,7 +656,7 @@@ int cmd_branch(int argc, const char **a
                        BRANCH_TRACK_EXPLICIT),
                OPT_SET_INT( 0, "set-upstream",  &track, N_("change upstream info"),
                        BRANCH_TRACK_OVERRIDE),
 -              OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"),
 +              OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),
                OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"),
                OPT__COLOR(&branch_use_color, N_("use colored output")),
                OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
                if (argc == 1 && track == BRANCH_TRACK_OVERRIDE &&
                    !branch_existed && remote_tracking) {
                        fprintf(stderr, _("\nIf you wanted to make '%s' track '%s', do this:\n\n"), head, branch->name);
 -                      fprintf(stderr, _("    git branch -d %s\n"), branch->name);
 -                      fprintf(stderr, _("    git branch --set-upstream-to %s\n"), branch->name);
 +                      fprintf(stderr, "    git branch -d %s\n", branch->name);
 +                      fprintf(stderr, "    git branch --set-upstream-to %s\n", branch->name);
                }
  
        } else
diff --combined builtin/checkout.c
index ea2fe1cf3fc251dcab2f2d70e737a0f8f5000a21,60417187838ef2e93fcf997dd4b311bcf54c0a15..3398c61e9a64ab686bf59fb027277f3de578b5fb
@@@ -242,6 -242,7 +242,6 @@@ static int checkout_paths(const struct 
        struct checkout state;
        static char *ps_matched;
        unsigned char rev[20];
 -      int flag;
        struct commit *head;
        int errs = 0;
        struct lock_file *lock_file;
        if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
 -      read_ref_full("HEAD", 0, rev, &flag);
 +      read_ref_full("HEAD", 0, rev, NULL);
        head = lookup_commit_reference_gently(rev, 1);
  
        errs |= post_checkout_hook(head, head, 0);
@@@ -1110,7 -1111,7 +1110,7 @@@ static int checkout_branch(struct check
                char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
                if (head_ref &&
                    (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
-                       die_if_checked_out(new->path);
+                       die_if_checked_out(new->path, 1);
                free(head_ref);
        }
  
diff --combined builtin/worktree.c
index 331ecf6761d22d256398adeedccdd85edfacd2de,12c0af723e69b87c07cc152824fb2cff6930ee66..96a2834a18be8ee03427cfdd408914ea71d59243
@@@ -110,7 -110,7 +110,7 @@@ static void prune_worktrees(void
                if (ret < 0 && errno == ENOTDIR)
                        ret = unlink(path.buf);
                if (ret)
 -                      error(_("failed to remove: %s"), strerror(errno));
 +                      error_errno(_("failed to remove '%s'"), path.buf);
        }
        closedir(dir);
        if (!show_only)
@@@ -205,7 -205,7 +205,7 @@@ static int add_worktree(const char *pat
        if (!opts->detach && !strbuf_check_branch_ref(&symref, refname) &&
                 ref_exists(symref.buf)) { /* it's a branch */
                if (!opts->force)
-                       die_if_checked_out(symref.buf);
+                       die_if_checked_out(symref.buf, 0);
        } else { /* must be a commit */
                commit = lookup_commit_reference_by_name(refname);
                if (!commit)
@@@ -349,7 -349,7 +349,7 @@@ static int add(int ac, const char **av
                if (!opts.force &&
                    !strbuf_check_branch_ref(&symref, opts.new_branch) &&
                    ref_exists(symref.buf))
-                       die_if_checked_out(symref.buf);
+                       die_if_checked_out(symref.buf, 0);
                strbuf_release(&symref);
        }
  
diff --combined cache.h
index 6b80a17edc281bfa0b078d5502d8ffd5b19b461c,c04a17f8d028658e73e010c6928c2536f6fd647d..6049f867113896def34306f22ac6927b8f0e8e1e
+++ b/cache.h
@@@ -654,7 -654,6 +654,7 @@@ extern int warn_on_object_refname_ambig
  extern const char *apply_default_whitespace;
  extern const char *apply_default_ignorewhitespace;
  extern const char *git_attributes_file;
 +extern const char *git_hooks_path;
  extern int zlib_compression_level;
  extern int core_compression_level;
  extern int core_compression_seen;
@@@ -701,14 -700,6 +701,14 @@@ extern int ref_paranoia
  extern char comment_line_char;
  extern int auto_comment_line_char;
  
 +/* Windows only */
 +enum hide_dotfiles_type {
 +      HIDE_DOTFILES_FALSE = 0,
 +      HIDE_DOTFILES_TRUE,
 +      HIDE_DOTFILES_DOTGITONLY
 +};
 +extern enum hide_dotfiles_type hide_dotfiles;
 +
  enum branch_track {
        BRANCH_TRACK_UNSPECIFIED = -1,
        BRANCH_TRACK_NEVER = 0,
@@@ -808,11 -799,14 +808,14 @@@ extern void check_repository_format(voi
   */
  extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+ extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
  
  extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
  extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
+ extern void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
  extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
  extern void strbuf_git_path_submodule(struct strbuf *sb, const char *path,
@@@ -967,6 -961,8 +970,6 @@@ static inline int is_empty_blob_sha1(co
  
  int git_mkstemp(char *path, size_t n, const char *template);
  
 -int git_mkstemps(char *path, size_t n, const char *template, int suffix_len);
 -
  /* set default permissions by passing mode arguments to open(2) */
  int git_mkstemps_mode(char *pattern, int suffix_len, int mode);
  int git_mkstemp_mode(char *pattern, int mode);
@@@ -1163,8 -1159,6 +1166,8 @@@ extern int get_sha1_blob(const char *st
  extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
  extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
  
 +extern int get_oid(const char *str, struct object_id *oid);
 +
  typedef int each_abbrev_fn(const unsigned char *sha1, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
  
@@@ -1775,8 -1769,8 +1778,8 @@@ int add_files_to_cache(const char *pref
  extern int diff_auto_refresh_index;
  
  /* match-trees.c */
 -void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
 -void shift_tree_by(const unsigned char *, const unsigned char *, unsigned char *, const char *);
 +void shift_tree(const struct object_id *, const struct object_id *, struct object_id *, int);
 +void shift_tree_by(const struct object_id *, const struct object_id *, struct object_id *, const char *);
  
  /*
   * whitespace rules.
diff --combined dir.c
index 656f272adc69b633ddd06277bf9cc77205a7e8ee,f04bd3bd3333800965632f53e838b3c9bce33ac0..6172b3438d356d0359c2158708c718c6694e183a
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -53,17 -53,23 +53,16 @@@ static enum path_treatment read_directo
        int check_only, const struct path_simplify *simplify);
  static int get_dtype(struct dirent *de, const char *path, int len);
  
- /* helper string functions with support for the ignore_case flag */
- int strcmp_icase(const char *a, const char *b)
+ int fspathcmp(const char *a, const char *b)
  {
        return ignore_case ? strcasecmp(a, b) : strcmp(a, b);
  }
  
- int strncmp_icase(const char *a, const char *b, size_t count)
+ int fspathncmp(const char *a, const char *b, size_t count)
  {
        return ignore_case ? strncasecmp(a, b, count) : strncmp(a, b, count);
  }
  
 -int fnmatch_icase(const char *pattern, const char *string, int flags)
 -{
 -      return wildmatch(pattern, string,
 -                       flags | (ignore_case ? WM_CASEFOLD : 0),
 -                       NULL);
 -}
 -
  int git_fnmatch(const struct pathspec_item *item,
                const char *pattern, const char *string,
                int prefix)
@@@ -795,12 -801,12 +794,12 @@@ int match_basename(const char *basename
  {
        if (prefix == patternlen) {
                if (patternlen == basenamelen &&
-                   !strncmp_icase(pattern, basename, basenamelen))
+                   !fspathncmp(pattern, basename, basenamelen))
                        return 1;
        } else if (flags & EXC_FLAG_ENDSWITH) {
                /* "*literal" matching against "fooliteral" */
                if (patternlen - 1 <= basenamelen &&
-                   !strncmp_icase(pattern + 1,
+                   !fspathncmp(pattern + 1,
                                   basename + basenamelen - (patternlen - 1),
                                   patternlen - 1))
                        return 1;
@@@ -837,7 -843,7 +836,7 @@@ int match_pathname(const char *pathname
         */
        if (pathlen < baselen + 1 ||
            (baselen && pathname[baselen] != '/') ||
-           strncmp_icase(pathname, base, baselen))
+           fspathncmp(pathname, base, baselen))
                return 0;
  
        namelen = baselen ? pathlen - baselen - 1 : pathlen;
                if (prefix > namelen)
                        return 0;
  
-               if (strncmp_icase(pattern, name, prefix))
+               if (fspathncmp(pattern, name, prefix))
                        return 0;
                pattern += prefix;
                patternlen -= prefix;
diff --combined dir.h
index d56d2fb48f68191d54fdfb8c5c104cb87cba1c41,e34d555d537b82392e0372fd0149d9b62ef3ccd0..bfde698c488adcc75b7a294476c622afb6f92b36
--- 1/dir.h
--- 2/dir.h
+++ b/dir.h
@@@ -270,8 -270,9 +270,8 @@@ extern int remove_dir_recursively(struc
  /* tries to remove the path with empty directories along it, ignores ENOENT */
  extern int remove_path(const char *path);
  
- extern int strcmp_icase(const char *a, const char *b);
- extern int strncmp_icase(const char *a, const char *b, size_t count);
+ extern int fspathcmp(const char *a, const char *b);
+ extern int fspathncmp(const char *a, const char *b, size_t count);
 -extern int fnmatch_icase(const char *pattern, const char *string, int flags);
  
  /*
   * The prefix part of pattern must not contains wildcards.
diff --combined fast-import.c
index 21881d1fddf9b2ddb3a14a67baf03fccf73e0a3f,339cd38076e65ae9b2f3aa948758b8cfce11d1c4..83558dcfe3ab442a415accd82ec7503f95f4746e
@@@ -414,7 -414,7 +414,7 @@@ static void write_crash_report(const ch
        struct recent_command *rc;
  
        if (!rpt) {
 -              error("can't write crash report %s: %s", loc, strerror(errno));
 +              error_errno("can't write crash report %s", loc);
                free(loc);
                return;
        }
@@@ -1512,7 -1512,7 +1512,7 @@@ static int tree_content_set
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
                        if (!*slash1) {
                                if (!S_ISDIR(mode)
                                                && e->versions[1].mode == mode
@@@ -1602,7 -1602,7 +1602,7 @@@ static int tree_content_remove
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
                        if (*slash1 && !S_ISDIR(e->versions[1].mode))
                                /*
                                 * If p names a file in some subdirectory, and a
@@@ -1669,7 -1669,7 +1669,7 @@@ static int tree_content_get
        t = root->tree;
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
-               if (e->name->str_len == n && !strncmp_icase(p, e->name->str_dat, n)) {
+               if (e->name->str_len == n && !fspathncmp(p, e->name->str_dat, n)) {
                        if (!*slash1)
                                goto found_entry;
                        if (!S_ISDIR(e->versions[1].mode))
@@@ -1806,8 -1806,8 +1806,8 @@@ static void dump_marks(void
                return;
  
        if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
 -              failure |= error("Unable to write marks file %s: %s",
 -                      export_marks_file, strerror(errno));
 +              failure |= error_errno("Unable to write marks file %s",
 +                                     export_marks_file);
                return;
        }
  
  
        dump_marks_helper(f, 0, marks);
        if (commit_lock_file(&mark_lock)) {
 -              failure |= error("Unable to write file %s: %s",
 -                      export_marks_file, strerror(errno));
 +              failure |= error_errno("Unable to write file %s",
 +                                     export_marks_file);
                return;
        }
  }
diff --combined path.c
index 503766784c4c0c1be71edacbaae6ec4bd698ee8d,8fdd187f730063d194bd14daa8663f1dcb030956..259aeed846bd3dac8e10cde30a22ec1ad8697a63
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -5,6 -5,7 +5,7 @@@
  #include "strbuf.h"
  #include "string-list.h"
  #include "dir.h"
+ #include "worktree.h"
  
  static int get_st_mode_bits(const char *path, int *mode)
  {
@@@ -134,7 -135,7 +135,7 @@@ static struct common_dir common_list[] 
   * definite
   * definition
   *
 - * The trie would look look like:
 + * The trie would look like:
   * root: len = 0, children a and d non-NULL, value = NULL.
   *    a: len = 2, contents = bc, value = (data for "abc")
   *    d: len = 2, contents = ef, children i non-NULL, value = (data for "def")
@@@ -383,10 -384,11 +384,11 @@@ static void adjust_git_path(struct strb
                update_common_dir(buf, git_dir_len, NULL);
  }
  
- static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
+ static void do_git_path(const struct worktree *wt, struct strbuf *buf,
+                       const char *fmt, va_list args)
  {
        int gitdir_len;
-       strbuf_addstr(buf, get_git_dir());
+       strbuf_addstr(buf, get_worktree_git_dir(wt));
        if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
                strbuf_addch(buf, '/');
        gitdir_len = buf->len;
@@@ -400,7 -402,7 +402,7 @@@ char *git_path_buf(struct strbuf *buf, 
        va_list args;
        strbuf_reset(buf);
        va_start(args, fmt);
-       do_git_path(buf, fmt, args);
+       do_git_path(NULL, buf, fmt, args);
        va_end(args);
        return buf->buf;
  }
@@@ -409,7 -411,7 +411,7 @@@ void strbuf_git_path(struct strbuf *sb
  {
        va_list args;
        va_start(args, fmt);
-       do_git_path(sb, fmt, args);
+       do_git_path(NULL, sb, fmt, args);
        va_end(args);
  }
  
@@@ -418,7 -420,7 +420,7 @@@ const char *git_path(const char *fmt, .
        struct strbuf *pathname = get_pathname();
        va_list args;
        va_start(args, fmt);
-       do_git_path(pathname, fmt, args);
+       do_git_path(NULL, pathname, fmt, args);
        va_end(args);
        return pathname->buf;
  }
@@@ -428,7 -430,7 +430,7 @@@ char *git_pathdup(const char *fmt, ...
        struct strbuf path = STRBUF_INIT;
        va_list args;
        va_start(args, fmt);
-       do_git_path(&path, fmt, args);
+       do_git_path(NULL, &path, fmt, args);
        va_end(args);
        return strbuf_detach(&path, NULL);
  }
@@@ -454,6 -456,16 +456,16 @@@ const char *mkpath(const char *fmt, ...
        return cleanup_path(pathname->buf);
  }
  
+ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
+ {
+       struct strbuf *pathname = get_pathname();
+       va_list args;
+       va_start(args, fmt);
+       do_git_path(wt, pathname, fmt, args);
+       va_end(args);
+       return pathname->buf;
+ }
  static void do_submodule_path(struct strbuf *buf, const char *path,
                              const char *fmt, va_list args)
  {
@@@ -503,6 -515,35 +515,35 @@@ void strbuf_git_path_submodule(struct s
        va_end(args);
  }
  
+ static void do_git_common_path(struct strbuf *buf,
+                              const char *fmt,
+                              va_list args)
+ {
+       strbuf_addstr(buf, get_git_common_dir());
+       if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+               strbuf_addch(buf, '/');
+       strbuf_vaddf(buf, fmt, args);
+       strbuf_cleanup_path(buf);
+ }
+ const char *git_common_path(const char *fmt, ...)
+ {
+       struct strbuf *pathname = get_pathname();
+       va_list args;
+       va_start(args, fmt);
+       do_git_common_path(pathname, fmt, args);
+       va_end(args);
+       return pathname->buf;
+ }
+ void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+ {
+       va_list args;
+       va_start(args, fmt);
+       do_git_common_path(sb, fmt, args);
+       va_end(args);
+ }
  int validate_headref(const char *path)
  {
        struct stat st;
diff --combined sha1_file.c
index a7f45b3490abe2d71c91e7d3bf1704101fb359d9,ea6381b3fd0469eb9dc283d90f880917b5dbb1c0..d5e11217f523018008b3c4861d5d70068de14c72
@@@ -301,7 -301,7 +301,7 @@@ static int link_alt_odb_entry(const cha
                        return -1;
                }
        }
-       if (!strcmp_icase(ent->base, normalized_objdir)) {
+       if (!fspathcmp(ent->base, normalized_objdir)) {
                free(ent);
                return -1;
        }
@@@ -1107,8 -1107,9 +1107,8 @@@ unsigned char *use_pack(struct packed_g
                                PROT_READ, MAP_PRIVATE,
                                p->pack_fd, win->offset);
                        if (win->base == MAP_FAILED)
 -                              die("packfile %s cannot be mapped: %s",
 -                                      p->pack_name,
 -                                      strerror(errno));
 +                              die_errno("packfile %s cannot be mapped",
 +                                        p->pack_name);
                        if (!win->offset && win->len == p->pack_size
                                && !p->do_not_close)
                                close_pack_fd(p);
@@@ -1278,8 -1279,8 +1278,8 @@@ static void prepare_packed_git_one(cha
        dir = opendir(path.buf);
        if (!dir) {
                if (errno != ENOENT)
 -                      error("unable to open object pack directory: %s: %s",
 -                            path.buf, strerror(errno));
 +                      error_errno("unable to open object pack directory: %s",
 +                                  path.buf);
                strbuf_release(&path);
                return;
        }
@@@ -2983,7 -2984,7 +2983,7 @@@ int finalize_object_file(const char *tm
        unlink_or_warn(tmpfile);
        if (ret) {
                if (ret != EEXIST) {
 -                      return error("unable to write sha1 filename %s: %s", filename, strerror(ret));
 +                      return error_errno("unable to write sha1 filename %s", filename);
                }
                /* FIXME!!! Collision check here ? */
        }
@@@ -2997,7 -2998,7 +2997,7 @@@ out
  static int write_buffer(int fd, const void *buf, size_t len)
  {
        if (write_in_full(fd, buf, len) < 0)
 -              return error("file write error (%s)", strerror(errno));
 +              return error_errno("file write error");
        return 0;
  }
  
@@@ -3080,7 -3081,7 +3080,7 @@@ static int write_loose_object(const uns
                if (errno == EACCES)
                        return error("insufficient permission for adding an object to repository database %s", get_object_directory());
                else
 -                      return error("unable to create temporary file: %s", strerror(errno));
 +                      return error_errno("unable to create temporary file");
        }
  
        /* Set it up */
                utb.actime = mtime;
                utb.modtime = mtime;
                if (utime(tmp_file.buf, &utb) < 0)
 -                      warning("failed utime() on %s: %s",
 -                              tmp_file.buf, strerror(errno));
 +                      warning_errno("failed utime() on %s", tmp_file.buf);
        }
  
        return finalize_object_file(tmp_file.buf, filename);
@@@ -3358,7 -3360,7 +3358,7 @@@ static int index_core(unsigned char *sh
                if (size == read_in_full(fd, buf, size))
                        ret = index_mem(sha1, buf, size, type, path, flags);
                else
 -                      ret = error("short read %s", strerror(errno));
 +                      ret = error_errno("short read");
                free(buf);
        } else {
                void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
@@@ -3423,14 -3425,18 +3423,14 @@@ int index_path(unsigned char *sha1, con
        case S_IFREG:
                fd = open(path, O_RDONLY);
                if (fd < 0)
 -                      return error("open(\"%s\"): %s", path,
 -                                   strerror(errno));
 +                      return error_errno("open(\"%s\")", path);
                if (index_fd(sha1, fd, st, OBJ_BLOB, path, flags) < 0)
                        return error("%s: failed to insert into database",
                                     path);
                break;
        case S_IFLNK:
 -              if (strbuf_readlink(&sb, path, st->st_size)) {
 -                      char *errstr = strerror(errno);
 -                      return error("readlink(\"%s\"): %s", path,
 -                                   errstr);
 -              }
 +              if (strbuf_readlink(&sb, path, st->st_size))
 +                      return error_errno("readlink(\"%s\")", path);
                if (!(flags & HASH_WRITE_OBJECT))
                        hash_sha1_file(sb.buf, sb.len, blob_type, sha1);
                else if (write_sha1_file(sb.buf, sb.len, blob_type, sha1))
@@@ -3486,7 -3492,7 +3486,7 @@@ static int for_each_file_in_obj_subdir(
        if (!dir) {
                if (errno == ENOENT)
                        return 0;
 -              return error("unable to open %s: %s", path->buf, strerror(errno));
 +              return error_errno("unable to open %s", path->buf);
        }
  
        while ((de = readdir(dir))) {
diff --combined worktree.c
index 89ebe67a505f6e7772f74bd92363230955dc2b74,4817d600556e444f9b22551a4f59a8dadcf258f2..199b1ef94ba84aad094a5a0fe95360c2df631003
@@@ -2,6 -2,8 +2,8 @@@
  #include "refs.h"
  #include "strbuf.h"
  #include "worktree.h"
+ #include "dir.h"
+ #include "wt-status.h"
  
  void free_worktrees(struct worktree **worktrees)
  {
@@@ -9,7 -11,7 +11,7 @@@
  
        for (i = 0; worktrees[i]; i++) {
                free(worktrees[i]->path);
-               free(worktrees[i]->git_dir);
+               free(worktrees[i]->id);
                free(worktrees[i]->head_ref);
                free(worktrees[i]);
        }
@@@ -18,7 -20,7 +20,7 @@@
  
  /*
   * read 'path_to_ref' into 'ref'.  Also if is_detached is not NULL,
 - * set is_detached to 1 (0) if the ref is detatched (is not detached).
 + * set is_detached to 1 (0) if the ref is detached (is not detached).
   *
   * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
   * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
@@@ -74,13 -76,11 +76,11 @@@ static struct worktree *get_main_worktr
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
-       struct strbuf gitdir = STRBUF_INIT;
        struct strbuf head_ref = STRBUF_INIT;
        int is_bare = 0;
        int is_detached = 0;
  
-       strbuf_addf(&gitdir, "%s", absolute_path(get_git_common_dir()));
-       strbuf_addbuf(&worktree_path, &gitdir);
+       strbuf_addstr(&worktree_path, absolute_path(get_git_common_dir()));
        is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
        if (is_bare)
                strbuf_strip_suffix(&worktree_path, "/.");
  
        worktree = xmalloc(sizeof(struct worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
-       worktree->git_dir = strbuf_detach(&gitdir, NULL);
+       worktree->id = NULL;
        worktree->is_bare = is_bare;
        worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
+       worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
  
  done:
        strbuf_release(&path);
-       strbuf_release(&gitdir);
        strbuf_release(&worktree_path);
        strbuf_release(&head_ref);
        return worktree;
@@@ -111,16 -111,13 +111,13 @@@ static struct worktree *get_linked_work
        struct worktree *worktree = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
-       struct strbuf gitdir = STRBUF_INIT;
        struct strbuf head_ref = STRBUF_INIT;
        int is_detached = 0;
  
        if (!id)
                die("Missing linked worktree name");
  
-       strbuf_addf(&gitdir, "%s/worktrees/%s",
-                       absolute_path(get_git_common_dir()), id);
-       strbuf_addf(&path, "%s/gitdir", gitdir.buf);
+       strbuf_git_common_path(&path, "worktrees/%s/gitdir", id);
        if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
                /* invalid gitdir file */
                goto done;
  
        worktree = xmalloc(sizeof(struct worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
-       worktree->git_dir = strbuf_detach(&gitdir, NULL);
+       worktree->id = xstrdup(id);
        worktree->is_bare = 0;
        worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
+       worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
  
  done:
        strbuf_release(&path);
-       strbuf_release(&gitdir);
        strbuf_release(&worktree_path);
        strbuf_release(&head_ref);
        return worktree;
  }
  
+ static void mark_current_worktree(struct worktree **worktrees)
+ {
+       struct strbuf git_dir = STRBUF_INIT;
+       struct strbuf path = STRBUF_INIT;
+       int i;
+       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
+       for (i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
+               wt->is_current = !fspathcmp(git_dir.buf, path.buf);
+               strbuf_reset(&path);
+               if (wt->is_current)
+                       break;
+       }
+       strbuf_release(&git_dir);
+       strbuf_release(&path);
+ }
  struct worktree **get_worktrees(void)
  {
        struct worktree **list = NULL;
        }
        ALLOC_GROW(list, counter + 1, alloc);
        list[counter] = NULL;
+       mark_current_worktree(list);
        return list;
  }
  
- char *find_shared_symref(const char *symref, const char *target)
+ const char *get_worktree_git_dir(const struct worktree *wt)
+ {
+       if (!wt)
+               return get_git_dir();
+       else if (!wt->id)
+               return get_git_common_dir();
+       else
+               return git_common_path("worktrees/%s", wt->id);
+ }
+ int is_worktree_being_rebased(const struct worktree *wt,
+                             const char *target)
+ {
+       struct wt_status_state state;
+       int found_rebase;
+       memset(&state, 0, sizeof(state));
+       found_rebase = wt_status_check_rebase(wt, &state) &&
+               ((state.rebase_in_progress ||
+                 state.rebase_interactive_in_progress) &&
+                state.branch &&
+                starts_with(target, "refs/heads/") &&
+                !strcmp(state.branch, target + strlen("refs/heads/")));
+       free(state.branch);
+       free(state.onto);
+       return found_rebase;
+ }
+ int is_worktree_being_bisected(const struct worktree *wt,
+                              const char *target)
  {
-       char *existing = NULL;
+       struct wt_status_state state;
+       int found_rebase;
+       memset(&state, 0, sizeof(state));
+       found_rebase = wt_status_check_bisect(wt, &state) &&
+               state.branch &&
+               starts_with(target, "refs/heads/") &&
+               !strcmp(state.branch, target + strlen("refs/heads/"));
+       free(state.branch);
+       return found_rebase;
+ }
+ /*
+  * note: this function should be able to detect shared symref even if
+  * HEAD is temporarily detached (e.g. in the middle of rebase or
+  * bisect). New commands that do similar things should update this
+  * function as well.
+  */
+ const struct worktree *find_shared_symref(const char *symref,
+                                         const char *target)
+ {
+       const struct worktree *existing = NULL;
        struct strbuf path = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
-       struct worktree **worktrees = get_worktrees();
+       static struct worktree **worktrees;
        int i = 0;
  
+       if (worktrees)
+               free_worktrees(worktrees);
+       worktrees = get_worktrees();
        for (i = 0; worktrees[i]; i++) {
+               struct worktree *wt = worktrees[i];
+               if (wt->is_detached && !strcmp(symref, "HEAD")) {
+                       if (is_worktree_being_rebased(wt, target)) {
+                               existing = wt;
+                               break;
+                       }
+                       if (is_worktree_being_bisected(wt, target)) {
+                               existing = wt;
+                               break;
+                       }
+               }
                strbuf_reset(&path);
                strbuf_reset(&sb);
-               strbuf_addf(&path, "%s/%s", worktrees[i]->git_dir, symref);
+               strbuf_addf(&path, "%s/%s",
+                           get_worktree_git_dir(wt),
+                           symref);
  
                if (parse_ref(path.buf, &sb, NULL)) {
                        continue;
                }
  
                if (!strcmp(sb.buf, target)) {
-                       existing = xstrdup(worktrees[i]->path);
+                       existing = wt;
                        break;
                }
        }
  
        strbuf_release(&path);
        strbuf_release(&sb);
-       free_worktrees(worktrees);
  
        return existing;
  }
diff --combined wt-status.c
index 16856305ca818f7d83e17ba6f653afdcc3901e9d,0032ef5f6fad7ef5283615b8f52718c185e27428..4f27bd62af992122d83df911c42b585342ac1066
@@@ -15,6 -15,7 +15,7 @@@
  #include "column.h"
  #include "strbuf.h"
  #include "utf8.h"
+ #include "worktree.h"
  
  static const char cut_line[] =
  "------------------------ >8 ------------------------\n";
@@@ -950,7 -951,6 +951,7 @@@ static void show_merge_in_progress(stru
                        status_printf_ln(s, color,
                                _("  (fix conflicts and run \"git commit\")"));
        } else {
 +              s-> commitable = 1;
                status_printf_ln(s, color,
                        _("All conflicts fixed but you are still merging."));
                if (s->hints)
@@@ -1263,13 -1263,13 +1264,13 @@@ static void show_bisect_in_progress(str
  /*
   * Extract branch information from rebase/bisect
   */
- static char *read_and_strip_branch(const char *path)
+ static char *get_branch(const struct worktree *wt, const char *path)
  {
        struct strbuf sb = STRBUF_INIT;
        unsigned char sha1[20];
        const char *branch_name;
  
-       if (strbuf_read_file(&sb, git_path("%s", path), 0) <= 0)
+       if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
                goto got_nothing;
  
        while (sb.len && sb.buf[sb.len - 1] == '\n')
@@@ -1361,40 -1361,62 +1362,62 @@@ static void wt_status_get_detached_from
        strbuf_release(&cb.buf);
  }
  
void wt_status_get_state(struct wt_status_state *state,
-                        int get_detached_from)
int wt_status_check_rebase(const struct worktree *wt,
+                          struct wt_status_state *state)
  {
        struct stat st;
-       unsigned char sha1[20];
  
-       if (!stat(git_path_merge_head(), &st)) {
-               state->merge_in_progress = 1;
-       } else if (!stat(git_path("rebase-apply"), &st)) {
-               if (!stat(git_path("rebase-apply/applying"), &st)) {
+       if (!stat(worktree_git_path(wt, "rebase-apply"), &st)) {
+               if (!stat(worktree_git_path(wt, "rebase-apply/applying"), &st)) {
                        state->am_in_progress = 1;
-                       if (!stat(git_path("rebase-apply/patch"), &st) && !st.st_size)
+                       if (!stat(worktree_git_path(wt, "rebase-apply/patch"), &st) && !st.st_size)
                                state->am_empty_patch = 1;
                } else {
                        state->rebase_in_progress = 1;
-                       state->branch = read_and_strip_branch("rebase-apply/head-name");
-                       state->onto = read_and_strip_branch("rebase-apply/onto");
+                       state->branch = get_branch(wt, "rebase-apply/head-name");
+                       state->onto = get_branch(wt, "rebase-apply/onto");
                }
-       } else if (!stat(git_path("rebase-merge"), &st)) {
-               if (!stat(git_path("rebase-merge/interactive"), &st))
+       } else if (!stat(worktree_git_path(wt, "rebase-merge"), &st)) {
+               if (!stat(worktree_git_path(wt, "rebase-merge/interactive"), &st))
                        state->rebase_interactive_in_progress = 1;
                else
                        state->rebase_in_progress = 1;
-               state->branch = read_and_strip_branch("rebase-merge/head-name");
-               state->onto = read_and_strip_branch("rebase-merge/onto");
+               state->branch = get_branch(wt, "rebase-merge/head-name");
+               state->onto = get_branch(wt, "rebase-merge/onto");
+       } else
+               return 0;
+       return 1;
+ }
+ int wt_status_check_bisect(const struct worktree *wt,
+                          struct wt_status_state *state)
+ {
+       struct stat st;
+       if (!stat(worktree_git_path(wt, "BISECT_LOG"), &st)) {
+               state->bisect_in_progress = 1;
+               state->branch = get_branch(wt, "BISECT_START");
+               return 1;
+       }
+       return 0;
+ }
+ void wt_status_get_state(struct wt_status_state *state,
+                        int get_detached_from)
+ {
+       struct stat st;
+       unsigned char sha1[20];
+       if (!stat(git_path_merge_head(), &st)) {
+               state->merge_in_progress = 1;
+       } else if (wt_status_check_rebase(NULL, state)) {
+               ;               /* all set */
        } else if (!stat(git_path_cherry_pick_head(), &st) &&
                        !get_sha1("CHERRY_PICK_HEAD", sha1)) {
                state->cherry_pick_in_progress = 1;
                hashcpy(state->cherry_pick_head_sha1, sha1);
        }
-       if (!stat(git_path("BISECT_LOG"), &st)) {
-               state->bisect_in_progress = 1;
-               state->branch = read_and_strip_branch("BISECT_START");
-       }
+       wt_status_check_bisect(NULL, state);
        if (!stat(git_path_revert_head(), &st) &&
            !get_sha1("REVERT_HEAD", sha1)) {
                state->revert_in_progress = 1;