Merge branch 'es/worktree-add'
authorJunio C Hamano <gitster@pobox.com>
Mon, 13 Jul 2015 21:02:18 +0000 (14:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 13 Jul 2015 21:02:19 +0000 (14:02 -0700)
Update to the "linked checkout" in 2.5.0-rc1.

Instead of "checkout --to" that does not do what "checkout"
normally does, move the functionality to "git worktree add".

* es/worktree-add: (24 commits)
Revert "checkout: retire --ignore-other-worktrees in favor of --force"
checkout: retire --ignore-other-worktrees in favor of --force
worktree: add: auto-vivify new branch when <branch> is omitted
worktree: add: make -b/-B default to HEAD when <branch> is omitted
worktree: extract basename computation to new function
checkout: require worktree unconditionally
checkout: retire --to option
tests: worktree: retrofit "checkout --to" tests for "worktree add"
worktree: add -b/-B options
worktree: add --detach option
worktree: add --force option
worktree: introduce "add" command
checkout: drop 'checkout_opts' dependency from prepare_linked_checkout
checkout: make --to unconditionally verbose
checkout: prepare_linked_checkout: drop now-unused 'new' argument
checkout: relocate --to's "no branch specified" check
checkout: fix bug with --to and relative HEAD
Documentation/git-worktree: add EXAMPLES section
Documentation/git-worktree: add high-level 'lock' overview
Documentation/git-worktree: split technical info from general description
...

1  2 
Documentation/git-checkout.txt
builtin/checkout.c
builtin/worktree.c
git.c
index 2df6d0d4b457dfc88e5dbac3d45ba36ebf986636,efe6a026f132b040cc47d847bbdb4f284c2a0133..63b739c550dda6fddb73fad776d48e0b71f71bfa
@@@ -3,7 -3,7 +3,7 @@@ git-checkout(1
  
  NAME
  ----
 -git-checkout - Checkout a branch or paths to the working tree
 +git-checkout - Switch branches or restore working tree files
  
  SYNOPSIS
  --------
@@@ -89,10 -89,6 +89,10 @@@ Omitting <branch> detaches HEAD at the 
        (i.e.  commit, tag or tree) to update the index for the given
        paths before updating the working tree.
  +
 +'git checkout' with <paths> or `--patch` is used to restore modified or
 +deleted paths to their original contents from the index or replace paths
 +with the contents from a named <tree-ish> (most often a commit-ish).
 ++
  The index may contain unmerged entries because of a previous failed merge.
  By default, if you try to check out such an entry from the index, the
  checkout operation will fail and nothing will be checked out.
@@@ -148,7 -144,7 +148,7 @@@ explicitly give a name with '-b' in suc
  
  --no-track::
        Do not set up "upstream" configuration, even if the
 -      branch.autosetupmerge configuration variable is true.
 +      branch.autoSetupMerge configuration variable is true.
  
  -l::
        Create the new branch's reflog; see linkgit:git-branch[1] for
@@@ -214,7 -210,7 +214,7 @@@ the conflicted merge in the specified p
  --conflict=<style>::
        The same as --merge option above, but changes the way the
        conflicting hunks are presented, overriding the
 -      merge.conflictstyle configuration variable.  Possible values are
 +      merge.conflictStyle configuration variable.  Possible values are
        "merge" (default) and "diff3" (in addition to what is shown by
        "merge" style, shows the original contents).
  
@@@ -229,13 -225,6 +229,6 @@@ This means that you can use `git checko
  edits from your current working tree. See the ``Interactive Mode''
  section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
  
- --to=<path>::
-       Check out a branch in a separate working directory at
-       `<path>`. A new working directory is linked to the current
-       repository, sharing everything except working directory
-       specific files such as HEAD, index... See "MULTIPLE WORKING
-       TREES" section for more information.
  --ignore-other-worktrees::
        `git checkout` refuses when the wanted ref is already checked
        out by another worktree. This option makes it check the ref
@@@ -405,71 -394,6 +398,6 @@@ $ git reflog -2 HEAD # o
  $ git log -g -2 HEAD
  ------------
  
- MULTIPLE WORKING TREES
- ----------------------
- A git repository can support multiple working trees, allowing you to check
- out more than one branch at a time.  With `git checkout --to` a new working
- tree is associated with the repository.  This new working tree is called a
- "linked working tree" as opposed to the "main working tree" prepared by "git
- init" or "git clone".  A repository has one main working tree (if it's not a
- bare repository) and zero or more linked working trees.
- Each linked working tree has a private sub-directory in the repository's
- $GIT_DIR/worktrees directory.  The private sub-directory's name is usually
- the base name of the linked working tree's path, possibly appended with a
- number to make it unique.  For example, when `$GIT_DIR=/path/main/.git` the
- command `git checkout --to /path/other/test-next next` creates the linked
- working tree in `/path/other/test-next` and also creates a
- `$GIT_DIR/worktrees/test-next` directory (or `$GIT_DIR/worktrees/test-next1`
- if `test-next` is already taken).
- Within a linked working tree, $GIT_DIR is set to point to this private
- directory (e.g. `/path/main/.git/worktrees/test-next` in the example) and
- $GIT_COMMON_DIR is set to point back to the main working tree's $GIT_DIR
- (e.g. `/path/main/.git`). These settings are made in a `.git` file located at
- the top directory of the linked working tree.
- Path resolution via `git rev-parse --git-path` uses either
- $GIT_DIR or $GIT_COMMON_DIR depending on the path. For example, in the
- linked working tree `git rev-parse --git-path HEAD` returns
- `/path/main/.git/worktrees/test-next/HEAD` (not
- `/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.
- See linkgit:gitrepository-layout[5] for more information. The rule of
- thumb is do not make any assumption about whether a path belongs to
- $GIT_DIR or $GIT_COMMON_DIR when you need to directly access something
- inside $GIT_DIR. Use `git rev-parse --git-path` to get the final path.
- When you are done with a linked working tree you can simply delete it.
- The working tree's entry in the repository's $GIT_DIR/worktrees
- directory will eventually be removed automatically (see
- `gc.pruneworktreesexpire` in linkgit::git-config[1]), or you can run
- `git prune --worktrees` in the main or any linked working tree to
- clean up any stale entries in $GIT_DIR/worktrees.
- If you move a linked working directory to another file system, or
- within a file system that does not support hard links, you need to run
- at least one git command inside the linked working directory
- (e.g. `git status`) in order to update its entry in $GIT_DIR/worktrees
- so that it does not get automatically removed.
- To prevent a $GIT_DIR/worktrees entry from from being pruned (which
- can be useful in some situations, such as when the
- entry's working tree is stored on a portable device), add a file named
- 'locked' to the entry's directory. The file contains the reason in
- plain text. For example, if a linked working tree's `.git` file points
- to `/path/main/.git/worktrees/test-next` then a file named
- `/path/main/.git/worktrees/test-next/locked` will prevent the
- `test-next` entry from being pruned.  See
- linkgit:gitrepository-layout[5] for details.
- Multiple checkout support for submodules is incomplete. It is NOT
- recommended to make multiple checkouts of a superproject.
  EXAMPLES
  --------
  
diff --combined builtin/checkout.c
index e227f649955a21647381ee322af65322b075b80d,57545543ebcc46ef28ca4a22d399b5476de7af0b..f71844a23a9d4bda0664bec709648911bc0f2609
  #include "ll-merge.h"
  #include "resolve-undo.h"
  #include "submodule.h"
- #include "argv-array.h"
- #include "sigchain.h"
  
  static const char * const checkout_usage[] = {
 -      N_("git checkout [options] <branch>"),
 -      N_("git checkout [options] [<branch>] -- <file>..."),
 +      N_("git checkout [<options>] <branch>"),
 +      N_("git checkout [<options>] [<branch>] -- <file>..."),
        NULL,
  };
  
@@@ -51,8 -49,6 +49,6 @@@ struct checkout_opts 
        struct pathspec pathspec;
        struct tree *source_tree;
  
-       const char *new_worktree;
-       const char **saved_argv;
        int new_worktree_mode;
  };
  
@@@ -68,41 -64,23 +64,41 @@@ static int post_checkout_hook(struct co
  
  }
  
 -static int update_some(const unsigned char *sha1, const char *base, int baselen,
 +static int update_some(const unsigned char *sha1, struct strbuf *base,
                const char *pathname, unsigned mode, int stage, void *context)
  {
        int len;
        struct cache_entry *ce;
 +      int pos;
  
        if (S_ISDIR(mode))
                return READ_TREE_RECURSIVE;
  
 -      len = baselen + strlen(pathname);
 +      len = base->len + strlen(pathname);
        ce = xcalloc(1, cache_entry_size(len));
        hashcpy(ce->sha1, sha1);
 -      memcpy(ce->name, base, baselen);
 -      memcpy(ce->name + baselen, pathname, len - baselen);
 +      memcpy(ce->name, base->buf, base->len);
 +      memcpy(ce->name + base->len, pathname, len - base->len);
        ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
        ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
 +
 +      /*
 +       * If the entry is the same as the current index, we can leave the old
 +       * entry in place. Whether it is UPTODATE or not, checkout_entry will
 +       * do the right thing.
 +       */
 +      pos = cache_name_pos(ce->name, ce->ce_namelen);
 +      if (pos >= 0) {
 +              struct cache_entry *old = active_cache[pos];
 +              if (ce->ce_mode == old->ce_mode &&
 +                  !hashcmp(ce->sha1, old->sha1)) {
 +                      old->ce_flags |= CE_UPDATE;
 +                      free(ce);
 +                      return 0;
 +              }
 +      }
 +
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        return 0;
  }
@@@ -273,9 -251,6 +269,6 @@@ static int checkout_paths(const struct 
                die(_("Cannot update paths and switch to branch '%s' at the same time."),
                    opts->new_branch);
  
-       if (opts->new_worktree)
-               die(_("'%s' cannot be used with updating paths"), "--to");
        if (opts->patch_mode)
                return run_add_interactive(revision, "--patch=checkout",
                                           &opts->pathspec);
@@@ -702,10 -677,10 +695,10 @@@ static void update_refs_for_switch(cons
  }
  
  static int add_pending_uninteresting_ref(const char *refname,
 -                                       const unsigned char *sha1,
 +                                       const struct object_id *oid,
                                         int flags, void *cb_data)
  {
 -      add_pending_sha1(cb_data, refname, sha1, UNINTERESTING);
 +      add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
        return 0;
  }
  
@@@ -760,17 -735,10 +753,17 @@@ static void suggest_reattach(struct com
  
        if (advice_detached_head)
                fprintf(stderr,
 -                      _(
 +                      Q_(
 +                      /* The singular version */
 +                      "If you want to keep it by creating a new branch, "
 +                      "this may be a good time\nto do so with:\n\n"
 +                      " git branch <new-branch-name> %s\n\n",
 +                      /* The plural version */
                        "If you want to keep them by creating a new branch, "
                        "this may be a good time\nto do so with:\n\n"
 -                      " git branch new_branch_name %s\n\n"),
 +                      " git branch <new-branch-name> %s\n\n",
 +                      /* Give ngettext() the count */
 +                      lost),
                        find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
  }
  
@@@ -850,138 -818,6 +843,6 @@@ static int switch_branches(const struc
        return ret || writeout_error;
  }
  
- static char *junk_work_tree;
- static char *junk_git_dir;
- static int is_junk;
- static pid_t junk_pid;
- static void remove_junk(void)
- {
-       struct strbuf sb = STRBUF_INIT;
-       if (!is_junk || getpid() != junk_pid)
-               return;
-       if (junk_git_dir) {
-               strbuf_addstr(&sb, junk_git_dir);
-               remove_dir_recursively(&sb, 0);
-               strbuf_reset(&sb);
-       }
-       if (junk_work_tree) {
-               strbuf_addstr(&sb, junk_work_tree);
-               remove_dir_recursively(&sb, 0);
-       }
-       strbuf_release(&sb);
- }
- static void remove_junk_on_signal(int signo)
- {
-       remove_junk();
-       sigchain_pop(signo);
-       raise(signo);
- }
- static int prepare_linked_checkout(const struct checkout_opts *opts,
-                                  struct branch_info *new)
- {
-       struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
-       struct strbuf sb = STRBUF_INIT;
-       const char *path = opts->new_worktree, *name;
-       struct stat st;
-       struct child_process cp;
-       int counter = 0, len, ret;
-       if (!new->commit)
-               die(_("no branch specified"));
-       if (file_exists(path) && !is_empty_dir(path))
-               die(_("'%s' already exists"), path);
-       len = strlen(path);
-       while (len && is_dir_sep(path[len - 1]))
-               len--;
-       for (name = path + len - 1; name > path; name--)
-               if (is_dir_sep(*name)) {
-                       name++;
-                       break;
-               }
-       strbuf_addstr(&sb_repo,
-                     git_path("worktrees/%.*s", (int)(path + len - name), name));
-       len = sb_repo.len;
-       if (safe_create_leading_directories_const(sb_repo.buf))
-               die_errno(_("could not create leading directories of '%s'"),
-                         sb_repo.buf);
-       while (!stat(sb_repo.buf, &st)) {
-               counter++;
-               strbuf_setlen(&sb_repo, len);
-               strbuf_addf(&sb_repo, "%d", counter);
-       }
-       name = strrchr(sb_repo.buf, '/') + 1;
-       junk_pid = getpid();
-       atexit(remove_junk);
-       sigchain_push_common(remove_junk_on_signal);
-       if (mkdir(sb_repo.buf, 0777))
-               die_errno(_("could not create directory of '%s'"), sb_repo.buf);
-       junk_git_dir = xstrdup(sb_repo.buf);
-       is_junk = 1;
-       /*
-        * lock the incomplete repo so prune won't delete it, unlock
-        * after the preparation is over.
-        */
-       strbuf_addf(&sb, "%s/locked", sb_repo.buf);
-       write_file(sb.buf, 1, "initializing\n");
-       strbuf_addf(&sb_git, "%s/.git", path);
-       if (safe_create_leading_directories_const(sb_git.buf))
-               die_errno(_("could not create leading directories of '%s'"),
-                         sb_git.buf);
-       junk_work_tree = xstrdup(path);
-       strbuf_reset(&sb);
-       strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
-       write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
-       write_file(sb_git.buf, 1, "gitdir: %s/worktrees/%s\n",
-                  real_path(get_git_common_dir()), name);
-       /*
-        * This is to keep resolve_ref() happy. We need a valid HEAD
-        * or is_git_directory() will reject the directory. Any valid
-        * value would do because this value will be ignored and
-        * replaced at the next (real) checkout.
-        */
-       strbuf_reset(&sb);
-       strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-       write_file(sb.buf, 1, "%s\n", sha1_to_hex(new->commit->object.sha1));
-       strbuf_reset(&sb);
-       strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
-       write_file(sb.buf, 1, "../..\n");
-       if (!opts->quiet)
-               fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
-       setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1);
-       setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);
-       setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
-       memset(&cp, 0, sizeof(cp));
-       cp.git_cmd = 1;
-       cp.argv = opts->saved_argv;
-       ret = run_command(&cp);
-       if (!ret) {
-               is_junk = 0;
-               free(junk_work_tree);
-               free(junk_git_dir);
-               junk_work_tree = NULL;
-               junk_git_dir = NULL;
-       }
-       strbuf_reset(&sb);
-       strbuf_addf(&sb, "%s/locked", sb_repo.buf);
-       unlink_or_warn(sb.buf);
-       strbuf_release(&sb);
-       strbuf_release(&sb_repo);
-       strbuf_release(&sb_git);
-       return ret;
- }
  static int git_checkout_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "diff.ignoresubmodules")) {
@@@ -1320,9 -1156,6 +1181,6 @@@ static int checkout_branch(struct check
                free(head_ref);
        }
  
-       if (opts->new_worktree)
-               return prepare_linked_checkout(opts, new);
        if (!new->commit && opts->new_branch) {
                unsigned char rev[20];
                int flag;
@@@ -1364,9 -1197,7 +1222,7 @@@ int cmd_checkout(int argc, const char *
                OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
                         N_("do not limit pathspecs to sparse entries only")),
                OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
 -                              N_("second guess 'git checkout no-such-branch'")),
 +                              N_("second guess 'git checkout <no-such-branch>'")),
-               OPT_FILENAME(0, "to", &opts.new_worktree,
-                          N_("check a branch out in a separate working directory")),
                OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
                         N_("do not check if another worktree is holding the given ref")),
                OPT_END(),
        opts.overwrite_ignore = 1;
        opts.prefix = prefix;
  
-       opts.saved_argv = xmalloc(sizeof(const char *) * (argc + 2));
-       memcpy(opts.saved_argv, argv, sizeof(const char *) * (argc + 1));
        gitmodules_config();
        git_config(git_checkout_config, &opts);
  
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
  
-       /* recursive execution from checkout_new_worktree() */
        opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL;
-       if (opts.new_worktree_mode)
-               opts.new_worktree = NULL;
-       if (!opts.new_worktree)
-               setup_work_tree();
  
        if (conflict_style) {
                opts.merge = 1; /* implied */
diff --combined builtin/worktree.c
index 2a729c661cd3747ee345c8491dfc8ff540ee9c0f,69248ba0a352de82f59a117b9ba4ed4e6af74a4d..6a264ee749221cd6c6bf98ce68790bdec3dd82c5
@@@ -2,8 -2,12 +2,13 @@@
  #include "builtin.h"
  #include "dir.h"
  #include "parse-options.h"
+ #include "argv-array.h"
+ #include "run-command.h"
+ #include "sigchain.h"
++#include "refs.h"
  
  static const char * const worktree_usage[] = {
+       N_("git worktree add [<options>] <path> <branch>"),
        N_("git worktree prune [<options>]"),
        NULL
  };
@@@ -119,6 -123,198 +124,198 @@@ static int prune(int ac, const char **a
        return 0;
  }
  
+ static char *junk_work_tree;
+ static char *junk_git_dir;
+ static int is_junk;
+ static pid_t junk_pid;
+ static void remove_junk(void)
+ {
+       struct strbuf sb = STRBUF_INIT;
+       if (!is_junk || getpid() != junk_pid)
+               return;
+       if (junk_git_dir) {
+               strbuf_addstr(&sb, junk_git_dir);
+               remove_dir_recursively(&sb, 0);
+               strbuf_reset(&sb);
+       }
+       if (junk_work_tree) {
+               strbuf_addstr(&sb, junk_work_tree);
+               remove_dir_recursively(&sb, 0);
+       }
+       strbuf_release(&sb);
+ }
+ static void remove_junk_on_signal(int signo)
+ {
+       remove_junk();
+       sigchain_pop(signo);
+       raise(signo);
+ }
+ static const char *worktree_basename(const char *path, int *olen)
+ {
+       const char *name;
+       int len;
+       len = strlen(path);
+       while (len && is_dir_sep(path[len - 1]))
+               len--;
+       for (name = path + len - 1; name > path; name--)
+               if (is_dir_sep(*name)) {
+                       name++;
+                       break;
+               }
+       *olen = len;
+       return name;
+ }
+ static int add_worktree(const char *path, const char **child_argv)
+ {
+       struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
+       struct strbuf sb = STRBUF_INIT;
+       const char *name;
+       struct stat st;
+       struct child_process cp;
+       int counter = 0, len, ret;
+       unsigned char rev[20];
+       if (file_exists(path) && !is_empty_dir(path))
+               die(_("'%s' already exists"), path);
+       name = worktree_basename(path, &len);
+       strbuf_addstr(&sb_repo,
+                     git_path("worktrees/%.*s", (int)(path + len - name), name));
+       len = sb_repo.len;
+       if (safe_create_leading_directories_const(sb_repo.buf))
+               die_errno(_("could not create leading directories of '%s'"),
+                         sb_repo.buf);
+       while (!stat(sb_repo.buf, &st)) {
+               counter++;
+               strbuf_setlen(&sb_repo, len);
+               strbuf_addf(&sb_repo, "%d", counter);
+       }
+       name = strrchr(sb_repo.buf, '/') + 1;
+       junk_pid = getpid();
+       atexit(remove_junk);
+       sigchain_push_common(remove_junk_on_signal);
+       if (mkdir(sb_repo.buf, 0777))
+               die_errno(_("could not create directory of '%s'"), sb_repo.buf);
+       junk_git_dir = xstrdup(sb_repo.buf);
+       is_junk = 1;
+       /*
+        * lock the incomplete repo so prune won't delete it, unlock
+        * after the preparation is over.
+        */
+       strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+       write_file(sb.buf, 1, "initializing\n");
+       strbuf_addf(&sb_git, "%s/.git", path);
+       if (safe_create_leading_directories_const(sb_git.buf))
+               die_errno(_("could not create leading directories of '%s'"),
+                         sb_git.buf);
+       junk_work_tree = xstrdup(path);
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
+       write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
+       write_file(sb_git.buf, 1, "gitdir: %s/worktrees/%s\n",
+                  real_path(get_git_common_dir()), name);
+       /*
+        * This is to keep resolve_ref() happy. We need a valid HEAD
+        * or is_git_directory() will reject the directory. Moreover, HEAD
+        * in the new worktree must resolve to the same value as HEAD in
+        * the current tree since the command invoked to populate the new
+        * worktree will be handed the branch/ref specified by the user.
+        * For instance, if the user asks for the new worktree to be based
+        * at HEAD~5, then the resolved HEAD~5 in the new worktree must
+        * match the resolved HEAD~5 in the current tree in order to match
+        * the user's expectation.
+        */
+       if (!resolve_ref_unsafe("HEAD", 0, rev, NULL))
+               die(_("unable to resolve HEAD"));
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
+       write_file(sb.buf, 1, "%s\n", sha1_to_hex(rev));
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
+       write_file(sb.buf, 1, "../..\n");
+       fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
+       setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1);
+       setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);
+       setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
+       memset(&cp, 0, sizeof(cp));
+       cp.git_cmd = 1;
+       cp.argv = child_argv;
+       ret = run_command(&cp);
+       if (!ret) {
+               is_junk = 0;
+               free(junk_work_tree);
+               free(junk_git_dir);
+               junk_work_tree = NULL;
+               junk_git_dir = NULL;
+       }
+       strbuf_reset(&sb);
+       strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+       unlink_or_warn(sb.buf);
+       strbuf_release(&sb);
+       strbuf_release(&sb_repo);
+       strbuf_release(&sb_git);
+       return ret;
+ }
+ static int add(int ac, const char **av, const char *prefix)
+ {
+       int force = 0, detach = 0;
+       const char *new_branch = NULL, *new_branch_force = NULL;
+       const char *path, *branch;
+       struct argv_array cmd = ARGV_ARRAY_INIT;
+       struct option options[] = {
+               OPT__FORCE(&force, N_("checkout <branch> even if already checked out in other worktree")),
+               OPT_STRING('b', NULL, &new_branch, N_("branch"),
+                          N_("create a new branch")),
+               OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
+                          N_("create or reset a branch")),
+               OPT_BOOL(0, "detach", &detach, N_("detach HEAD at named commit")),
+               OPT_END()
+       };
+       ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
+       if (new_branch && new_branch_force)
+               die(_("-b and -B are mutually exclusive"));
+       if (ac < 1 || ac > 2)
+               usage_with_options(worktree_usage, options);
+       path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
+       branch = ac < 2 ? "HEAD" : av[1];
+       if (ac < 2 && !new_branch && !new_branch_force) {
+               int n;
+               const char *s = worktree_basename(path, &n);
+               new_branch = xstrndup(s, n);
+       }
+       argv_array_push(&cmd, "checkout");
+       if (force)
+               argv_array_push(&cmd, "--ignore-other-worktrees");
+       if (new_branch)
+               argv_array_pushl(&cmd, "-b", new_branch, NULL);
+       if (new_branch_force)
+               argv_array_pushl(&cmd, "-B", new_branch_force, NULL);
+       if (detach)
+               argv_array_push(&cmd, "--detach");
+       argv_array_push(&cmd, branch);
+       return add_worktree(path, cmd.argv);
+ }
  int cmd_worktree(int ac, const char **av, const char *prefix)
  {
        struct option options[] = {
  
        if (ac < 2)
                usage_with_options(worktree_usage, options);
+       if (!strcmp(av[1], "add"))
+               return add(ac - 1, av + 1, prefix);
        if (!strcmp(av[1], "prune"))
                return prune(ac - 1, av + 1, prefix);
        usage_with_options(worktree_usage, options);
diff --combined git.c
index fa77bc92bf75e5edda1a1f68be63e8591145b11b,21a639844fc10d0a93988ccb5215898106612239..fe94066aeebb0d71ea140c3d67ac4ad232db0799
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -1,12 -1,15 +1,12 @@@
  #include "builtin.h"
 -#include "cache.h"
  #include "exec_cmd.h"
  #include "help.h"
 -#include "quote.h"
  #include "run-command.h"
 -#include "commit.h"
  
  const char git_usage_string[] =
        "git [--version] [--help] [-C <path>] [-c name=value]\n"
        "           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n"
 -      "           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]\n"
 +      "           [-p | --paginate | --no-pager] [--no-replace-objects] [--bare]\n"
        "           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n"
        "           <command> [<args>]";
  
@@@ -204,12 -207,10 +204,12 @@@ static int handle_options(const char **
                                fprintf(stderr, "No directory given for -C.\n" );
                                usage(git_usage_string);
                        }
 -                      if (chdir((*argv)[1]))
 -                              die_errno("Cannot change to '%s'", (*argv)[1]);
 -                      if (envchanged)
 -                              *envchanged = 1;
 +                      if ((*argv)[1][0]) {
 +                              if (chdir((*argv)[1]))
 +                                      die_errno("Cannot change to '%s'", (*argv)[1]);
 +                              if (envchanged)
 +                                      *envchanged = 1;
 +                      }
                        (*argv)++;
                        (*argc)--;
                } else {
@@@ -382,7 -383,7 +382,7 @@@ static struct cmd_struct commands[] = 
        { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
        { "check-mailmap", cmd_check_mailmap, RUN_SETUP },
        { "check-ref-format", cmd_check_ref_format },
-       { "checkout", cmd_checkout, RUN_SETUP },
+       { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
        { "checkout-index", cmd_checkout_index,
                RUN_SETUP | NEED_WORK_TREE},
        { "cherry", cmd_cherry, RUN_SETUP },
        { "write-tree", cmd_write_tree, RUN_SETUP },
  };
  
 -int is_builtin(const char *s)
 +static struct cmd_struct *get_builtin(const char *s)
  {
        int i;
        for (i = 0; i < ARRAY_SIZE(commands); i++) {
 -              struct cmd_struct *p = commands+i;
 +              struct cmd_struct *p = commands + i;
                if (!strcmp(s, p->cmd))
 -                      return 1;
 +                      return p;
        }
 -      return 0;
 +      return NULL;
 +}
 +
 +int is_builtin(const char *s)
 +{
 +      return !!get_builtin(s);
  }
  
  static void handle_builtin(int argc, const char **argv)
        const char *cmd = argv[0];
        int i;
        static const char ext[] = STRIP_EXTENSION;
 +      struct cmd_struct *builtin;
  
        if (sizeof(ext) > 1) {
                i = strlen(argv[0]) - strlen(ext);
                argv[0] = cmd = "help";
        }
  
 -      for (i = 0; i < ARRAY_SIZE(commands); i++) {
 -              struct cmd_struct *p = commands+i;
 -              if (strcmp(p->cmd, cmd))
 -                      continue;
 -              if (saved_environment && (p->option & NO_SETUP)) {
 +      builtin = get_builtin(cmd);
 +      if (builtin) {
 +              if (saved_environment && (builtin->option & NO_SETUP))
                        restore_env();
 -                      break;
 -              }
 -              exit(run_builtin(p, argc, argv));
 +              else
 +                      exit(run_builtin(builtin, argc, argv));
        }
  }
  
@@@ -621,7 -619,6 +621,7 @@@ int main(int argc, char **av
  {
        const char **argv = (const char **) av;
        const char *cmd;
 +      int done_help = 0;
  
        startup_info = &git_startup_info;
  
        setup_path();
  
        while (1) {
 -              static int done_help = 0;
 -              static int was_alias = 0;
 -              was_alias = run_argv(&argc, &argv);
 +              int was_alias = run_argv(&argc, &argv);
                if (errno != ENOENT)
                        break;
                if (was_alias) {