Merge branch 'nd/multiple-work-trees'
authorJunio C Hamano <gitster@pobox.com>
Mon, 13 Jul 2015 21:02:02 +0000 (14:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 13 Jul 2015 21:02:02 +0000 (14:02 -0700)
"git checkout [<tree-ish>] <paths>" spent unnecessary cycles
checking if the current branch was checked out elsewhere, when we
know we are not switching the branches ourselves.

* nd/multiple-work-trees:
worktree: new place for "git prune --worktrees"
checkout: don't check worktrees when not necessary

1  2 
.gitignore
Makefile
builtin/checkout.c
builtin/gc.c
builtin/prune.c
command-list.txt
git.c
diff --combined .gitignore
index 422c5382c1acfde24223fda6236dd3586456a263,b527248ff0e7390750bd4a4e864baf6928ee84ae..a685ec1fb0ca49607431a65f1ccf035bb9b95a3a
  /git-verify-tag
  /git-web--browse
  /git-whatchanged
+ /git-worktree
  /git-write-tree
  /git-core-*/?*
  /gitweb/GITWEB-BUILD-OPTIONS
  /test-delta
  /test-dump-cache-tree
  /test-dump-split-index
 +/test-dump-untracked-cache
  /test-scrap-cache-tree
  /test-genrandom
  /test-hashmap
diff --combined Makefile
index 895f002869045288dfc6298e596c020aa4bc8efd,bfdc90877169fbfc95d38159496636b6e4aec302..8c3c724a7f5e0594ad836065757371f7a144c472
+++ b/Makefile
@@@ -191,10 -191,6 +191,10 @@@ all:
  # Define NO_TRUSTABLE_FILEMODE if your filesystem may claim to support
  # the executable mode bit, but doesn't really do so.
  #
 +# Define NEEDS_MODE_TRANSLATION if your OS strays from the typical file type
 +# bits in mode values (e.g. z/OS defines I_SFMT to 0xFF000000 as opposed to the
 +# usual 0xF000).
 +#
  # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
  #
  # Define NO_UNIX_SOCKETS if your system does not offer unix sockets.
  # as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
  #
  # Define USE_NSEC below if you want git to care about sub-second file mtimes
 -# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
 -# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
 -# randomly break unless your underlying filesystem supports those sub-second
 -# times (my ext3 doesn't).
 +# and ctimes. Note that you need recent glibc (at least 2.2.4) for this. On
 +# Linux, kernel 2.6.11 or newer is required for reliable sub-second file times
 +# on file systems with exactly 1 ns or 1 s resolution. If you intend to use Git
 +# on other file systems (e.g. CEPH, CIFS, NTFS, UDF), don't enable USE_NSEC. See
 +# Documentation/technical/racy-git.txt for details.
  #
  # Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
  # "st_ctim"
  # return NULL when it receives a bogus time_t.
  #
  # Define HAVE_CLOCK_GETTIME if your platform has clock_gettime in librt.
 +#
 +# Define HAVE_CLOCK_MONOTONIC if your platform has CLOCK_MONOTONIC in librt.
 +#
 +# Define NO_HMAC_CTX_CLEANUP if your OpenSSL is version 0.9.6b or earlier to
 +# cleanup the HMAC context with the older HMAC_cleanup function.
 +#
 +# Define USE_PARENS_AROUND_GETTEXT_N to "yes" if your compiler happily
 +# compiles the following initialization:
 +#
 +#   static const char s[] = ("FOO");
 +#
 +# and define it to "no" if you need to remove the parentheses () around the
 +# constant.  The default is "auto", which means to use parentheses if your
 +# compiler is detected to support it.
 +#
 +# Define HAVE_BSD_SYSCTL if your platform has a BSD-compatible sysctl function.
 +#
 +# Define HAVE_GETDELIM if your system has the getdelim() function.
  
  GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -575,7 -552,6 +575,7 @@@ TEST_PROGRAMS_NEED_X += test-dat
  TEST_PROGRAMS_NEED_X += test-delta
  TEST_PROGRAMS_NEED_X += test-dump-cache-tree
  TEST_PROGRAMS_NEED_X += test-dump-split-index
 +TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
  TEST_PROGRAMS_NEED_X += test-genrandom
  TEST_PROGRAMS_NEED_X += test-hashmap
  TEST_PROGRAMS_NEED_X += test-index-version
@@@ -910,6 -886,7 +910,7 @@@ BUILTIN_OBJS += builtin/var.
  BUILTIN_OBJS += builtin/verify-commit.o
  BUILTIN_OBJS += builtin/verify-pack.o
  BUILTIN_OBJS += builtin/verify-tag.o
+ BUILTIN_OBJS += builtin/worktree.o
  BUILTIN_OBJS += builtin/write-tree.o
  
  GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
@@@ -970,14 -947,6 +971,14 @@@ ifneq (,$(SOCKLEN_T)
        BASIC_CFLAGS += -Dsocklen_t=$(SOCKLEN_T)
  endif
  
 +ifeq (yes,$(USE_PARENS_AROUND_GETTEXT_N))
 +      BASIC_CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=1
 +else
 +ifeq (no,$(USE_PARENS_AROUND_GETTEXT_N))
 +      BASIC_CFLAGS += -DUSE_PARENS_AROUND_GETTEXT_N=0
 +endif
 +endif
 +
  ifeq ($(uname_S),Darwin)
        ifndef NO_FINK
                ifeq ($(shell test -d /sw/lib && echo y),y)
@@@ -1027,9 -996,6 +1028,9 @@@ ifdef HAVE_ALLOCA_
        BASIC_CFLAGS += -DHAVE_ALLOCA_H
  endif
  
 +IMAP_SEND_BUILDDEPS =
 +IMAP_SEND_LDFLAGS = $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
 +
  ifdef NO_CURL
        BASIC_CFLAGS += -DNO_CURL
        REMOTE_CURL_PRIMARY =
        REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
        PROGRAM_OBJS += http-fetch.o
        PROGRAMS += $(REMOTE_CURL_NAMES)
 -      curl_check := $(shell (echo 070908; curl-config --vernum) 2>/dev/null | sort -r | sed -ne 2p)
 +      curl_check := $(shell (echo 070908; curl-config --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
                        PROGRAM_OBJS += http-push.o
                endif
        endif
 +      curl_check := $(shell (echo 072200; curl-config --vernum | sed -e '/^70[BC]/s/^/0/') 2>/dev/null | sort -r | sed -ne 2p)
 +      ifeq "$(curl_check)" "072200"
 +              USE_CURL_FOR_IMAP_SEND = YesPlease
 +      endif
 +      ifdef USE_CURL_FOR_IMAP_SEND
 +              BASIC_CFLAGS += -DUSE_CURL_FOR_IMAP_SEND
 +              IMAP_SEND_BUILDDEPS = http.o
 +              IMAP_SEND_LDFLAGS += $(CURL_LIBCURL)
 +      endif
        ifndef NO_EXPAT
                ifdef EXPATDIR
                        BASIC_CFLAGS += -I$(EXPATDIR)/include
@@@ -1103,9 -1060,6 +1104,9 @@@ ifndef NO_OPENSS
        ifdef NEEDS_CRYPTO_WITH_SSL
                OPENSSL_LIBSSL += -lcrypto
        endif
 +      ifdef NO_HMAC_CTX_CLEANUP
 +              BASIC_CFLAGS += -DNO_HMAC_CTX_CLEANUP
 +      endif
  else
        BASIC_CFLAGS += -DNO_OPENSSL
        BLK_SHA1 = 1
@@@ -1277,10 -1231,6 +1278,10 @@@ endi
  ifdef NO_TRUSTABLE_FILEMODE
        BASIC_CFLAGS += -DNO_TRUSTABLE_FILEMODE
  endif
 +ifdef NEEDS_MODE_TRANSLATION
 +      COMPAT_CFLAGS += -DNEEDS_MODE_TRANSLATION
 +      COMPAT_OBJS += compat/stat.o
 +endif
  ifdef NO_IPV6
        BASIC_CFLAGS += -DNO_IPV6
  endif
@@@ -1433,18 -1383,6 +1434,18 @@@ ifdef HAVE_CLOCK_GETTIM
        EXTLIBS += -lrt
  endif
  
 +ifdef HAVE_CLOCK_MONOTONIC
 +      BASIC_CFLAGS += -DHAVE_CLOCK_MONOTONIC
 +endif
 +
 +ifdef HAVE_BSD_SYSCTL
 +      BASIC_CFLAGS += -DHAVE_BSD_SYSCTL
 +endif
 +
 +ifdef HAVE_GETDELIM
 +      BASIC_CFLAGS += -DHAVE_GETDELIM
 +endif
 +
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK = NoThanks
  endif
@@@ -1695,10 -1633,10 +1696,10 @@@ $(BUILT_INS): git$
        ln -s $< $@ 2>/dev/null || \
        cp $< $@
  
 -common-cmds.h: ./generate-cmdlist.sh command-list.txt
 +common-cmds.h: generate-cmdlist.perl command-list.txt
  
  common-cmds.h: $(wildcard Documentation/git-*.txt)
 -      $(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
 +      $(QUIET_GEN)$(PERL_PATH) generate-cmdlist.perl command-list.txt > $@+ && mv $@+ $@
  
  SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
        $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
@@@ -1725,7 -1663,7 +1726,7 @@@ GIT-SCRIPT-DEFINES: FORC
              fi
  
  
 -$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh GIT-SCRIPT-DEFINES
 +$(SCRIPT_SH_GEN) : % : %.sh GIT-SCRIPT-DEFINES
        $(QUIET_GEN)$(cmd_munge_script) && \
        chmod +x $@+ && \
        mv $@+ $@
@@@ -1739,16 -1677,13 +1740,16 @@@ git.res: git.rc GIT-VERSION-FIL
          $(join -DMAJOR= -DMINOR=, $(wordlist 1,2,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
          -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" $< -o $@
  
 +# This makes sure we depend on the NO_PERL setting itself.
 +$(SCRIPT_PERL_GEN): GIT-BUILD-OPTIONS
 +
  ifndef NO_PERL
 -$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
 +$(SCRIPT_PERL_GEN): perl/perl.mak
  
  perl/perl.mak: perl/PM.stamp
  
  perl/PM.stamp: FORCE
 -      $(QUIET_GEN)$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
 +      @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
        { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \
        $(RM) $@+
  
@@@ -1756,7 -1691,7 +1757,7 @@@ perl/perl.mak: GIT-CFLAGS GIT-PREFIX pe
        $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
  
  PERL_DEFINES = $(PERL_PATH_SQ):$(PERLLIB_EXTRA_SQ)
 -$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
 +$(SCRIPT_PERL_GEN): % : %.perl perl/perl.mak GIT-PERL-DEFINES GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
        INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
        INSTLIBDIR_EXTRA='$(PERLLIB_EXTRA_SQ)' && \
@@@ -1785,12 -1720,12 +1786,12 @@@ GIT-PERL-DEFINES: FORC
  gitweb:
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
  
 -git-instaweb: git-instaweb.sh gitweb GIT-SCRIPT-DEFINES
 +git-instaweb: git-instaweb.sh GIT-SCRIPT-DEFINES
        $(QUIET_GEN)$(cmd_munge_script) && \
        chmod +x $@+ && \
        mv $@+ $@
  else # NO_PERL
 -$(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
 +$(SCRIPT_PERL_GEN) git-instaweb: % : unimplemented.sh
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's|@@REASON@@|NO_PERL=$(NO_PERL)|g' \
        mv $@+ $@
  endif # NO_PERL
  
 +# This makes sure we depend on the NO_PYTHON setting itself.
 +$(SCRIPT_PYTHON_GEN): GIT-BUILD-OPTIONS
 +
  ifndef NO_PYTHON
  $(SCRIPT_PYTHON_GEN): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
  $(SCRIPT_PYTHON_GEN): % : %.py
@@@ -1943,7 -1875,7 +1944,7 @@@ gettext.sp gettext.s gettext.o: GIT-PRE
  gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
        -DGIT_LOCALE_PATH='"$(localedir_SQ)"'
  
 -http-push.sp http.sp http-walker.sp remote-curl.sp: SPARSE_FLAGS += \
 +http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \
        -DCURL_DISABLE_TYPECHECK
  
  ifdef NO_EXPAT
@@@ -1964,9 -1896,9 +1965,9 @@@ endi
  git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
  
 -git-imap-send$X: imap-send.o GIT-LDFLAGS $(GITLIBS)
 +git-imap-send$X: imap-send.o $(IMAP_SEND_BUILDDEPS) GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 -              $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL) $(LIB_4_CRYPTO)
 +              $(LIBS) $(IMAP_SEND_LDFLAGS)
  
  git-http-fetch$X: http.o http-walker.o http-fetch.o GIT-LDFLAGS $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
@@@ -2104,47 -2036,45 +2105,47 @@@ GIT-LDFLAGS: FORC
  # that runs GIT-BUILD-OPTIONS, and then again to protect it
  # and the first level quoting from the shell that runs "echo".
  GIT-BUILD-OPTIONS: FORCE
 -      @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
 -      @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
 -      @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
 -      @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
 -      @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
 -      @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
 -      @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@
 -      @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
 -      @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
 -      @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
 +      @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@+
 +      @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@+
 +      @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@+
 +      @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@+
 +      @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+
 +      @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+
 +      @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+
 +      @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+
 +      @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
 +      @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
 +      @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
  ifdef TEST_OUTPUT_DIRECTORY
 -      @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@
 +      @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
  endif
  ifdef GIT_TEST_OPTS
 -      @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@
 +      @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@+
  endif
  ifdef GIT_TEST_CMP
 -      @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
 +      @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@+
  endif
  ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
 -      @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@
 +      @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@+
  endif
 -      @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@
 -      @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@
 +      @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@+
 +      @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@+
  ifdef GIT_PERF_REPEAT_COUNT
 -      @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@
 +      @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@+
  endif
  ifdef GIT_PERF_REPO
 -      @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@
 +      @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@+
  endif
  ifdef GIT_PERF_LARGE_REPO
 -      @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@
 +      @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@+
  endif
  ifdef GIT_PERF_MAKE_OPTS
 -      @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@
 +      @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
  endif
  ifdef TEST_GIT_INDEX_VERSION
 -      @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@
 +      @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
  endif
 +      @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
  
  ### Detect Python interpreter path changes
  ifndef NO_PYTHON
@@@ -2457,7 -2387,7 +2458,7 @@@ check-docs:
                esac ; \
                test -f "Documentation/$$v.txt" || \
                echo "no doc: $$v"; \
 -              sed -e '/^#/d' command-list.txt | \
 +              sed -e '1,/^### command list/d' -e '/^#/d' command-list.txt | \
                grep -q "^$$v[  ]" || \
                case "$$v" in \
                git) ;; \
                esac ; \
        done; \
        ( \
 -              sed -e '/^#/d' \
 +              sed -e '1,/^### command list/d' \
 +                  -e '/^#/d' \
                    -e 's/[     ].*//' \
                    -e 's/^/listed /' command-list.txt; \
                $(MAKE) -C Documentation print-man1 | \
diff --combined builtin/checkout.c
index 9b49f0e413ba03ebfee924d514a175d8f74fe1d3,2079aa417076201dd6bafe832393eb27aaa83ec9..e227f649955a21647381ee322af65322b075b80d
@@@ -23,8 -23,8 +23,8 @@@
  #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,
  };
  
@@@ -68,41 -68,23 +68,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;
  }
@@@ -702,10 -684,10 +702,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 -742,10 +760,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));
  }
  
@@@ -1110,7 -1085,6 +1110,6 @@@ static int parse_branchname_arg(int arg
  {
        struct tree **source_tree = &opts->source_tree;
        const char **new_branch = &opts->new_branch;
-       int force_detach = opts->force_detach;
        int argcount = 0;
        unsigned char branch_rev[20];
        const char *arg;
        else
                new->path = NULL; /* not an existing branch */
  
-       if (new->path && !force_detach && !*new_branch) {
-               unsigned char sha1[20];
-               int flag;
-               char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
-               if (head_ref &&
-                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&
-                   !opts->ignore_other_worktrees)
-                       check_linked_checkouts(new);
-               free(head_ref);
-       }
        new->commit = lookup_commit_reference_gently(rev, 1);
        if (!new->commit) {
                /* not a commit */
@@@ -1321,6 -1284,17 +1309,17 @@@ static int checkout_branch(struct check
                die(_("Cannot switch branch to a non-commit '%s'"),
                    new->name);
  
+       if (new->path && !opts->force_detach && !opts->new_branch) {
+               unsigned char sha1[20];
+               int flag;
+               char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
+               if (head_ref &&
+                   (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&
+                   !opts->ignore_other_worktrees)
+                       check_linked_checkouts(new);
+               free(head_ref);
+       }
        if (opts->new_worktree)
                return prepare_linked_checkout(opts, new);
  
@@@ -1365,7 -1339,7 +1364,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,
diff --combined builtin/gc.c
index 36fe33300f644fc9c7e5139b452a9703548ba2a2,44b206acad36393a4158c5e440b1ec5ca4924bc1..4957c3903293c2d4262f49c01188973bd3e53aa2
@@@ -21,7 -21,7 +21,7 @@@
  #define FAILED_RUN "failed to run %s"
  
  static const char * const builtin_gc_usage[] = {
 -      N_("git gc [options]"),
 +      N_("git gc [<options>]"),
        NULL
  };
  
@@@ -293,7 -293,7 +293,7 @@@ int cmd_gc(int argc, const char **argv
        argv_array_pushl(&reflog, "reflog", "expire", "--all", NULL);
        argv_array_pushl(&repack, "repack", "-d", "-l", NULL);
        argv_array_pushl(&prune, "prune", "--expire", NULL);
-       argv_array_pushl(&prune_worktrees, "prune", "--worktrees", "--expire", NULL);
+       argv_array_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
        argv_array_pushl(&rerere, "rerere", "gc", NULL);
  
        gc_config();
diff --combined builtin/prune.c
index 0c73246c721b3307f0d68b4e58f7c684aebce2e9,75f3b83aa1dc45d75cebc3e81824dad9607bf62a..10b03d3e4cb5ced78118251ac390dd6a33f9a71b
@@@ -6,7 -6,6 +6,6 @@@
  #include "reachable.h"
  #include "parse-options.h"
  #include "progress.h"
- #include "dir.h"
  
  static const char * const prune_usage[] = {
        N_("git prune [-n] [-v] [--expire <time>] [--] [<head>...]"),
@@@ -76,95 -75,6 +75,6 @@@ static int prune_subdir(int nr, const c
        return 0;
  }
  
- static int prune_worktree(const char *id, struct strbuf *reason)
- {
-       struct stat st;
-       char *path;
-       int fd, len;
-       if (!is_directory(git_path("worktrees/%s", id))) {
-               strbuf_addf(reason, _("Removing worktrees/%s: not a valid directory"), id);
-               return 1;
-       }
-       if (file_exists(git_path("worktrees/%s/locked", id)))
-               return 0;
-       if (stat(git_path("worktrees/%s/gitdir", id), &st)) {
-               strbuf_addf(reason, _("Removing worktrees/%s: gitdir file does not exist"), id);
-               return 1;
-       }
-       fd = open(git_path("worktrees/%s/gitdir", id), O_RDONLY);
-       if (fd < 0) {
-               strbuf_addf(reason, _("Removing worktrees/%s: unable to read gitdir file (%s)"),
-                           id, strerror(errno));
-               return 1;
-       }
-       len = st.st_size;
-       path = xmalloc(len + 1);
-       read_in_full(fd, path, len);
-       close(fd);
-       while (len && (path[len - 1] == '\n' || path[len - 1] == '\r'))
-               len--;
-       if (!len) {
-               strbuf_addf(reason, _("Removing worktrees/%s: invalid gitdir file"), id);
-               free(path);
-               return 1;
-       }
-       path[len] = '\0';
-       if (!file_exists(path)) {
-               struct stat st_link;
-               free(path);
-               /*
-                * the repo is moved manually and has not been
-                * accessed since?
-                */
-               if (!stat(git_path("worktrees/%s/link", id), &st_link) &&
-                   st_link.st_nlink > 1)
-                       return 0;
-               if (st.st_mtime <= expire) {
-                       strbuf_addf(reason, _("Removing worktrees/%s: gitdir file points to non-existent location"), id);
-                       return 1;
-               } else {
-                       return 0;
-               }
-       }
-       free(path);
-       return 0;
- }
- static void prune_worktrees(void)
- {
-       struct strbuf reason = STRBUF_INIT;
-       struct strbuf path = STRBUF_INIT;
-       DIR *dir = opendir(git_path("worktrees"));
-       struct dirent *d;
-       int ret;
-       if (!dir)
-               return;
-       while ((d = readdir(dir)) != NULL) {
-               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
-                       continue;
-               strbuf_reset(&reason);
-               if (!prune_worktree(d->d_name, &reason))
-                       continue;
-               if (show_only || verbose)
-                       printf("%s\n", reason.buf);
-               if (show_only)
-                       continue;
-               strbuf_reset(&path);
-               strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
-               ret = remove_dir_recursively(&path, 0);
-               if (ret < 0 && errno == ENOTDIR)
-                       ret = unlink(path.buf);
-               if (ret)
-                       error(_("failed to remove: %s"), strerror(errno));
-       }
-       closedir(dir);
-       if (!show_only)
-               rmdir(git_path("worktrees"));
-       strbuf_release(&reason);
-       strbuf_release(&path);
- }
  /*
   * Write errors (particularly out of space) can result in
   * failed temporary packs (and more rarely indexes and other
@@@ -191,12 -101,10 +101,10 @@@ int cmd_prune(int argc, const char **ar
  {
        struct rev_info revs;
        struct progress *progress = NULL;
-       int do_prune_worktrees = 0;
        const struct option options[] = {
                OPT__DRY_RUN(&show_only, N_("do not remove, show only")),
                OPT__VERBOSE(&verbose, N_("report pruned objects")),
                OPT_BOOL(0, "progress", &show_progress, N_("show progress")),
-               OPT_BOOL(0, "worktrees", &do_prune_worktrees, N_("prune .git/worktrees")),
                OPT_EXPIRY_DATE(0, "expire", &expire,
                                N_("expire objects older than <time>")),
                OPT_END()
        expire = ULONG_MAX;
        save_commit_buffer = 0;
        check_replace_refs = 0;
 +      ref_paranoia = 1;
        init_revisions(&revs, prefix);
  
        argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
  
-       if (do_prune_worktrees) {
-               if (argc)
-                       die(_("--worktrees does not take extra arguments"));
-               prune_worktrees();
-               return 0;
-       }
        while (argc--) {
                unsigned char sha1[20];
                const char *name = *argv++;
diff --combined command-list.txt
index b17c011bfd3dc7693a00ee6faf20a7a0608c7b7e,5fc74b94b8addbf9703543b96eb67e5a8c01bd49..2a94137bbb383fda655b481446d0891062d73cb9
@@@ -1,39 -1,29 +1,39 @@@
 -# List of known git commands.
 -# command name                                category [deprecated] [common]
 -git-add                                 mainporcelain common
 +# common commands are grouped by themes
 +# these groups are output by 'git help' in the order declared here.
 +# map each common command in the command list to one of these groups.
 +### common groups (do not change this line)
 +init         start a working area (see also: git help tutorial)
 +worktree     work on the current change (see also: git help everyday)
 +info         examine the history and state (see also: git help revisions)
 +history      grow, mark and tweak your common history
 +remote       collaborate (see also: git help workflows)
 +
 +### command list (do not change this line)
 +# command name                          category [deprecated] [common]
 +git-add                                 mainporcelain           worktree
  git-am                                  mainporcelain
  git-annotate                            ancillaryinterrogators
  git-apply                               plumbingmanipulators
  git-archimport                          foreignscminterface
  git-archive                             mainporcelain
 -git-bisect                              mainporcelain common
 +git-bisect                              mainporcelain           info
  git-blame                               ancillaryinterrogators
 -git-branch                              mainporcelain common
 +git-branch                              mainporcelain           history
  git-bundle                              mainporcelain
  git-cat-file                            plumbinginterrogators
  git-check-attr                          purehelpers
  git-check-ignore                        purehelpers
  git-check-mailmap                       purehelpers
 -git-checkout                            mainporcelain common
 +git-checkout                            mainporcelain           history
  git-checkout-index                      plumbingmanipulators
  git-check-ref-format                    purehelpers
  git-cherry                              ancillaryinterrogators
  git-cherry-pick                         mainporcelain
  git-citool                              mainporcelain
  git-clean                               mainporcelain
 -git-clone                               mainporcelain common
 +git-clone                               mainporcelain           init
  git-column                              purehelpers
 -git-commit                              mainporcelain common
 +git-commit                              mainporcelain           history
  git-commit-tree                         plumbingmanipulators
  git-config                              ancillarymanipulators
  git-count-objects                       ancillaryinterrogators
@@@ -45,42 -35,42 +45,42 @@@ git-cvsimpor
  git-cvsserver                           foreignscminterface
  git-daemon                              synchingrepositories
  git-describe                            mainporcelain
 -git-diff                                mainporcelain common
 +git-diff                                mainporcelain           history
  git-diff-files                          plumbinginterrogators
  git-diff-index                          plumbinginterrogators
  git-diff-tree                           plumbinginterrogators
  git-difftool                            ancillaryinterrogators
 -git-fast-export                               ancillarymanipulators
 -git-fast-import                               ancillarymanipulators
 -git-fetch                               mainporcelain common
 +git-fast-export                         ancillarymanipulators
 +git-fast-import                         ancillarymanipulators
 +git-fetch                               mainporcelain           remote
  git-fetch-pack                          synchingrepositories
  git-filter-branch                       ancillarymanipulators
  git-fmt-merge-msg                       purehelpers
  git-for-each-ref                        plumbinginterrogators
  git-format-patch                        mainporcelain
 -git-fsck                              ancillaryinterrogators
 +git-fsck                                ancillaryinterrogators
  git-gc                                  mainporcelain
  git-get-tar-commit-id                   ancillaryinterrogators
 -git-grep                                mainporcelain common
 +git-grep                                mainporcelain           info
  git-gui                                 mainporcelain
  git-hash-object                         plumbingmanipulators
 -git-help                              ancillaryinterrogators
 +git-help                                ancillaryinterrogators
  git-http-backend                        synchingrepositories
  git-http-fetch                          synchelpers
  git-http-push                           synchelpers
  git-imap-send                           foreignscminterface
  git-index-pack                          plumbingmanipulators
 -git-init                                mainporcelain common
 +git-init                                mainporcelain           init
  git-instaweb                            ancillaryinterrogators
  git-interpret-trailers                  purehelpers
  gitk                                    mainporcelain
 -git-log                                 mainporcelain common
 +git-log                                 mainporcelain           info
  git-ls-files                            plumbinginterrogators
  git-ls-remote                           plumbinginterrogators
  git-ls-tree                             plumbinginterrogators
  git-mailinfo                            purehelpers
  git-mailsplit                           purehelpers
 -git-merge                               mainporcelain common
 +git-merge                               mainporcelain           history
  git-merge-base                          plumbinginterrogators
  git-merge-file                          plumbingmanipulators
  git-merge-index                         plumbingmanipulators
@@@ -89,7 -79,7 +89,7 @@@ git-mergetoo
  git-merge-tree                          ancillaryinterrogators
  git-mktag                               plumbingmanipulators
  git-mktree                              plumbingmanipulators
 -git-mv                                  mainporcelain common
 +git-mv                                  mainporcelain           worktree
  git-name-rev                            plumbinginterrogators
  git-notes                               mainporcelain
  git-p4                                  foreignscminterface
@@@ -100,11 -90,11 +100,11 @@@ git-parse-remot
  git-patch-id                            purehelpers
  git-prune                               ancillarymanipulators
  git-prune-packed                        plumbingmanipulators
 -git-pull                                mainporcelain common
 -git-push                                mainporcelain common
 +git-pull                                mainporcelain           remote
 +git-push                                mainporcelain           remote
  git-quiltimport                         foreignscminterface
  git-read-tree                           plumbingmanipulators
 -git-rebase                              mainporcelain common
 +git-rebase                              mainporcelain           history
  git-receive-pack                        synchelpers
  git-reflog                              ancillarymanipulators
  git-relink                              ancillarymanipulators
@@@ -113,28 -103,28 +113,28 @@@ git-repac
  git-replace                             ancillarymanipulators
  git-request-pull                        foreignscminterface
  git-rerere                              ancillaryinterrogators
 -git-reset                               mainporcelain common
 +git-reset                               mainporcelain           worktree
  git-revert                              mainporcelain
  git-rev-list                            plumbinginterrogators
  git-rev-parse                           ancillaryinterrogators
 -git-rm                                  mainporcelain common
 +git-rm                                  mainporcelain           worktree
  git-send-email                          foreignscminterface
  git-send-pack                           synchingrepositories
  git-shell                               synchelpers
  git-shortlog                            mainporcelain
 -git-show                                mainporcelain common
 +git-show                                mainporcelain           info
  git-show-branch                         ancillaryinterrogators
  git-show-index                          plumbinginterrogators
  git-show-ref                            plumbinginterrogators
  git-sh-i18n                             purehelpers
  git-sh-setup                            purehelpers
  git-stash                               mainporcelain
 -git-status                              mainporcelain common
 +git-status                              mainporcelain           info
  git-stripspace                          purehelpers
  git-submodule                           mainporcelain
  git-svn                                 foreignscminterface
  git-symbolic-ref                        plumbingmanipulators
 -git-tag                                 mainporcelain common
 +git-tag                                 mainporcelain           history
  git-unpack-file                         plumbinginterrogators
  git-unpack-objects                      plumbingmanipulators
  git-update-index                        plumbingmanipulators
@@@ -148,4 -138,5 +148,5 @@@ git-verify-pac
  git-verify-tag                          ancillaryinterrogators
  gitweb                                  ancillaryinterrogators
  git-whatchanged                         ancillaryinterrogators
+ git-worktree                            mainporcelain
  git-write-tree                          plumbingmanipulators
diff --combined git.c
index 44374b1d9b6b73b3669f0f851bee058f45ee4f32,f22783894c161850b8e36fa5426bf61f4fc98a51..fa77bc92bf75e5edda1a1f68be63e8591145b11b
--- 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 {
@@@ -483,23 -484,19 +483,24 @@@ static struct cmd_struct commands[] = 
        { "verify-tag", cmd_verify_tag, RUN_SETUP },
        { "version", cmd_version },
        { "whatchanged", cmd_whatchanged, RUN_SETUP },
+       { "worktree", cmd_worktree, 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));
        }
  }
  
@@@ -620,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) {