Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Wed, 14 Nov 2007 11:37:18 +0000 (03:37 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Nov 2007 11:37:18 +0000 (03:37 -0800)
* maint:
git-clean: honor core.excludesfile
Documentation: Fix man page breakage with DocBook XSL v1.72
git-remote.txt: fix typo
core-tutorial.txt: Fix argument mistake in an example.
replace reference to git-rm with git-reset in git-commit doc
Grammar fixes for gitattributes documentation
Don't allow fast-import tree delta chains to exceed maximum depth
revert/cherry-pick: allow starting from dirty work tree.
t/t3404: fix test for a bogus todo file.

Conflicts:

fast-import.c

1  2 
Documentation/core-tutorial.txt
Documentation/git-add.txt
Documentation/git-remote.txt
Documentation/gitattributes.txt
Makefile
builtin-revert.c
fast-import.c
git-clean.sh
t/t3404-rebase-interactive.sh
t/t7300-clean.sh
index 401d1deca04f29d453f5902b9c2a4a8ca3bf6d4b,c126038c1f289c44e5e65bd03f797e11f3353333..bd6cd4124546a867c029a0c14b16f1944f9eca88
@@@ -553,8 -553,13 +553,8 @@@ can explore on your own
  
  [NOTE]
  Most likely, you are not directly using the core
 -git Plumbing commands, but using Porcelain like Cogito on top
 -of it. Cogito works a bit differently and you usually do not
 -have to run `git-update-index` yourself for changed files (you
 -do tell underlying git about additions and removals via
 -`cg-add` and `cg-rm` commands). Just before you make a commit
 -with `cg-commit`, Cogito figures out which files you modified,
 -and runs `git-update-index` on them for you.
 +git Plumbing commands, but using Porcelain such as `git-add`, `git-rm'
 +and `git-commit'.
  
  
  Tagging a version
@@@ -681,8 -686,8 +681,8 @@@ $ git rese
  
  and in fact a lot of the common git command combinations can be scripted
  with the `git xyz` interfaces.  You can learn things by just looking
 -at what the various git scripts do.  For example, `git reset` is the
 -above two lines implemented in `git-reset`, but some things like
 +at what the various git scripts do.  For example, `git reset` used to be
 +the above two lines implemented in `git-reset`, but some things like
  `git status` and `git commit` are slightly more complex scripts around
  the basic git commands.
  
@@@ -800,8 -805,8 +800,8 @@@ you have, you can sa
  $ git branch
  ------------
  
 -which is nothing more than a simple script around `ls .git/refs/heads`.
 -There will be asterisk in front of the branch you are currently on.
 +which used to be nothing more than a simple script around `ls .git/refs/heads`.
 +There will be an asterisk in front of the branch you are currently on.
  
  Sometimes you may wish to create a new branch _without_ actually
  checking it out and switching to it. If so, just use the command
@@@ -931,13 -936,12 +931,13 @@@ Another useful tool, especially if you 
  environment, is `git show-branch`.
  
  ------------------------------------------------
 -$ git show-branch --topo-order master mybranch
 +$ git-show-branch --topo-order --more=1 master mybranch
  * [master] Merge work in mybranch
   ! [mybranch] Some work.
  --
  -  [master] Merge work in mybranch
  *+ [mybranch] Some work.
 +*  [master^] Some fun.
  ------------------------------------------------
  
  The first two lines indicate that it is showing the two branches
@@@ -948,29 -952,17 +948,29 @@@ the later output lines is used to show 
  `master` branch, and the second column for the `mybranch`
  branch. Three commits are shown along with their log messages.
  All of them have non blank characters in the first column (`*`
 -shows an ordinary commit on the current branch, `.` is a merge commit), which
 +shows an ordinary commit on the current branch, `-` is a merge commit), which
  means they are now part of the `master` branch. Only the "Some
  work" commit has the plus `+` character in the second column,
  because `mybranch` has not been merged to incorporate these
  commits from the master branch.  The string inside brackets
  before the commit log message is a short name you can use to
  name the commit.  In the above example, 'master' and 'mybranch'
 -are branch heads.  'master~1' is the first parent of 'master'
 +are branch heads.  'master^' is the first parent of 'master'
  branch head.  Please see 'git-rev-parse' documentation if you
  see more complex cases.
  
 +[NOTE]
 +Without the '--more=1' option, 'git-show-branch' would not output the
 +'[master^]' commit, as '[mybranch]' commit is a common ancestor of
 +both 'master' and 'mybranch' tips.  Please see 'git-show-branch'
 +documentation for details.
 +
 +[NOTE]
 +If there were more commits on the 'master' branch after the merge, the
 +merge commit itself would not be shown by 'git-show-branch' by
 +default.  You would need to provide '--sparse' option to make the
 +merge commit visible in this case.
 +
  Now, let's pretend you are the one who did all the work in
  `mybranch`, and the fruit of your hard work has finally been merged
  to the `master` branch. Let's go back to `mybranch`, and run
@@@ -1090,6 -1082,11 +1090,6 @@@ server like git Native transport does
  that does not even support directory index would suffice.  But
  you must prepare your repository with `git-update-server-info`
  to help dumb transport downloaders.
 -+
 -There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
 -programs, which are 'commit walkers'; they outlived their
 -usefulness when git Native and SSH transports were introduced,
 -and not used by `git pull` or `git push` scripts.
  
  Once you fetch from the remote repository, you `merge` that
  with your current branch.
@@@ -1152,7 -1149,7 +1152,7 @@@ back to the earlier repository with "he
  and bring ourselves back to the pre-merge state:
  
  ------------
- $ git show-branch --more=3 master mybranch
+ $ git show-branch --more=2 master mybranch
  ! [master] Merge work in mybranch
   * [mybranch] Merge work in mybranch
  --
@@@ -1196,7 -1193,7 +1196,7 @@@ $ mb=$(git-merge-base HEAD mybranch
  
  The command writes the commit object name of the common ancestor
  to the standard output, so we captured its output to a variable,
 -because we will be using it in the next step.  BTW, the common
 +because we will be using it in the next step.  By the way, the common
  ancestor commit is the "New day." commit in this case.  You can
  tell it by:
  
@@@ -1215,7 -1212,7 +1215,7 @@@ $ git-read-tree -m -u $mb HEAD mybranc
  This is the same `git-read-tree` command we have already seen,
  but it takes three trees, unlike previous examples.  This reads
  the contents of each tree into different 'stage' in the index
- file (the first tree goes to stage 1, the second stage 2,
+ file (the first tree goes to stage 1, the second to stage 2,
  etc.).  After reading three trees into three stages, the paths
  that are the same in all three stages are 'collapsed' into stage
  0.  Also paths that are the same in two of three stages are
@@@ -1462,7 -1459,8 +1462,7 @@@ Although git is a truly distributed sys
  convenient to organize your project with an informal hierarchy
  of developers. Linux kernel development is run this way. There
  is a nice illustration (page 17, "Merges to Mainline") in
 -link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf
 -[Randy Dunlap's presentation].
 +link:http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf[Randy Dunlap's presentation].
  
  It should be stressed that this hierarchy is purely *informal*.
  There is nothing fundamental in git that enforces the "chain of
index 963e1ab1e2b7437a32f0aff97dda8dae3c4f47a0,fd82fc19b5decb04a3aa4dfb7e15edf270f61d72..63829d93cc827255355aa07a7db061b1a3a9e4d9
@@@ -50,10 -50,10 +50,10 @@@ OPTION
        and `dir/file2`) can be given to add all files in the
        directory, recursively.
  
 --n::
 +-n, \--dry-run::
          Don't actually add the file(s), just show if they exist.
  
 --v::
 +-v, \--verbose::
          Be verbose.
  
  -f::
@@@ -224,6 -224,7 +224,7 @@@ See Als
  --------
  gitlink:git-status[1]
  gitlink:git-rm[1]
+ gitlink:git-reset[1]
  gitlink:git-mv[1]
  gitlink:git-commit[1]
  gitlink:git-update-index[1]
index 027ba11bdb67ba540b63ec12e5a6b920cd2e5f7e,c386dd0168d893363cd00c65d90c78254d5a421f..0da8704a25f7010d84008396ab1bb0be5b4a4da1
@@@ -10,8 -10,7 +10,8 @@@ SYNOPSI
  --------
  [verse]
  'git-remote'
 -'git-remote' add [-t <branch>] [-m <branch>] [-f] <name> <url>
 +'git-remote' add [-t <branch>] [-m <branch>] [-f] [--mirror] <name> <url>
 +'git-remote' rm <name>
  'git-remote' show <name>
  'git-remote' prune <name>
  'git-remote' update [group]
@@@ -46,15 -45,6 +46,15 @@@ multiple branches without grabbing all 
  With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
  up to point at remote's `<master>` branch instead of whatever
  branch the `HEAD` at the remote repository actually points at.
 ++
 +In mirror mode, enabled with `--mirror`, the refs will not be stored
 +in the 'refs/remotes/' namespace, but in 'refs/heads/'.  This option
 +only makes sense in bare repositories.
 +
 +'rm'::
 +
 +Remove the remote named <name>. All remote tracking branches and
 +configuration settings for the remote are removed.
  
  'show'::
  
@@@ -79,7 -69,7 +79,7 @@@ caution
  Fetch updates for a named set of remotes in the repository as defined by
  remotes.<group>.  If a named group is not specified on the command line,
  the configuration parameter remotes.default will get used; if
- remotes.default is not defined, all remotes which do not the
+ remotes.default is not defined, all remotes which do not have the
  configuration parameter remote.<name>.skipDefaultUpdate set to true will
  be updated.  (See gitlink:git-config[1]).
  
index 20cf8ff81673265629028b49c34e0393063fd6b1,cf4ee2ebe5d8a10e23d129a0b8e7788de3f2dcf1..19bd25f29900c99d78592fe290772856092e2dc5
@@@ -148,22 -148,23 +148,23 @@@ with `$Id$` upon check-in
  `filter`
  ^^^^^^^^
  
- A `filter` attribute can be set to a string value.  This names
+ A `filter` attribute can be set to a string value that names a
  filter driver specified in the configuration.
  
- A filter driver consists of `clean` command and `smudge`
+ A filter driver consists of a `clean` command and a `smudge`
  command, either of which can be left unspecified.  Upon
- checkout, when `smudge` command is specified, the command is fed
- the blob object from its standard input, and its standard output
- is used to update the worktree file.  Similarly, `clean` command
- is used to convert the contents of worktree file upon checkin.
+ checkout, when the `smudge` command is specified, the command is
+ fed the blob object from its standard input, and its standard
+ output is used to update the worktree file.  Similarly, the
+ `clean` command is used to convert the contents of worktree file
+ upon checkin.
  
Missing filter driver definition in the config is not an error
A missing filter driver definition in the config is not an error
  but makes the filter a no-op passthru.
  
  The content filtering is done to massage the content into a
  shape that is more convenient for the platform, filesystem, and
- the user to use.  The keyword here is "more convenient" and not
+ the user to use.  The key phrase here is "more convenient" and not
  "turning something unusable into usable".  In other words, the
  intent is that if someone unsets the filter driver definition,
  or does not have the appropriate filter program, the project
@@@ -409,23 -410,6 +410,23 @@@ frotz    unspecifie
  ----------------------------------------------------------------
  
  
 +Creating an archive
 +~~~~~~~~~~~~~~~~~~~
 +
 +`export-subst`
 +^^^^^^^^^^^^^^
 +
 +If the attribute `export-subst` is set for a file then git will expand
 +several placeholders when adding this file to an archive.  The
 +expansion depends on the availability of a commit ID, i.e. if
 +gitlink:git-archive[1] has been given a tree instead of a commit or a
 +tag then no replacement will be done.  The placeholders are the same
 +as those for the option `--pretty=format:` of gitlink:git-log[1],
 +except that they need to be wrapped like this: `$Format:PLACEHOLDERS$`
 +in the file.  E.g. the string `$Format:%H$` will be replaced by the
 +commit hash.
 +
 +
  GIT
  ---
  Part of the gitlink:git[7] suite
diff --combined Makefile
index 9c6776e5970b38d0a8de8d35680aa83d54f41c01,41c90ad1d49c92829cd678f5f4ce8ad9f2e05985..e830bc7445c5753b29b6bb329ec8d977557df97b
+++ b/Makefile
@@@ -28,8 -28,6 +28,8 @@@ all:
  #
  # Define NO_STRCASESTR if you don't have strcasestr.
  #
 +# Define NO_MEMMEM if you don't have memmem.
 +#
  # Define NO_STRLCPY if you don't have strlcpy.
  #
  # Define NO_STRTOUMAX if you don't have strtoumax in the C library.
@@@ -38,8 -36,6 +38,8 @@@
  #
  # Define NO_SETENV if you don't have setenv in the C library.
  #
 +# Define NO_MKDTEMP if you don't have mkdtemp in the C library.
 +#
  # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
  # Enable it on Windows.  By default, symrefs are still used.
  #
@@@ -98,8 -94,6 +98,8 @@@
  # Define OLD_ICONV if your library has an old iconv(), where the second
  # (input buffer pointer) parameter is declared with type (const char **).
  #
 +# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
 +#
  # Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib"
  # that tells runtime paths to dynamic libraries;
  # "-Wl,-rpath=/path/lib" is used instead.
  #
  # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
  #
+ # Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
+ #
  # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
  # MakeMaker (e.g. using ActiveState under Cygwin).
  #
  # If not set it defaults to the bare 'wish'. If it is set to the empty
  # string then NO_TCLTK will be forced (this is used by configure script).
  #
 +# Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
 +# parallel delta searching when packing objects.
 +#
  
  GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -169,7 -162,6 +171,7 @@@ GITWEB_CONFIG = gitweb_config.per
  GITWEB_HOME_LINK_STR = projects
  GITWEB_SITENAME =
  GITWEB_PROJECTROOT = /pub/git
 +GITWEB_PROJECT_MAXDEPTH = 2007
  GITWEB_EXPORT_OK =
  GITWEB_STRICT_EXPORT =
  GITWEB_BASE_URL =
@@@ -212,10 -204,11 +214,10 @@@ BASIC_LDFLAGS 
  SCRIPT_SH = \
        git-bisect.sh git-checkout.sh \
        git-clean.sh git-clone.sh git-commit.sh \
 -      git-fetch.sh \
        git-ls-remote.sh \
        git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh git-rebase--interactive.sh \
 -      git-repack.sh git-request-pull.sh git-reset.sh \
 +      git-repack.sh git-request-pull.sh \
        git-sh-setup.sh \
        git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
  SCRIPT_PERL = \
        git-add--interactive.perl \
        git-archimport.perl git-cvsimport.perl git-relink.perl \
 -      git-cvsserver.perl git-remote.perl \
 -      git-svnimport.perl git-cvsexportcommit.perl \
 +      git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
        git-send-email.perl git-svn.perl
  
  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
  
  # ... and all the rest that could be moved out of bindir to gitexecdir
  PROGRAMS = \
 -      git-convert-objects$X git-fetch-pack$X \
 -      git-hash-object$X git-index-pack$X git-local-fetch$X \
 +      git-fetch-pack$X \
 +      git-hash-object$X git-index-pack$X \
        git-fast-import$X \
        git-daemon$X \
        git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
        git-peek-remote$X git-receive-pack$X \
        git-send-pack$X git-shell$X \
 -      git-show-index$X git-ssh-fetch$X \
 -      git-ssh-upload$X git-unpack-file$X \
 +      git-show-index$X \
 +      git-unpack-file$X \
        git-update-server-info$X \
        git-upload-pack$X \
        git-pack-redundant$X git-var$X \
@@@ -272,6 -266,9 +274,6 @@@ ifndef NO_TCLT
  OTHER_PROGRAMS += gitk-wish
  endif
  
 -# Backward compatibility -- to be removed after 1.0
 -PROGRAMS += git-ssh-pull$X git-ssh-push$X
 -
  # Set paths to tools early so that they can be used for version tests.
  ifndef SHELL_PATH
        SHELL_PATH = /bin/sh
@@@ -291,7 -288,7 +293,7 @@@ LIB_H = 
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
        tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
        utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
 -      mailmap.h remote.h
 +      mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h
  
  DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
  LIB_OBJS = \
        blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
        date.o diff-delta.o entry.o exec_cmd.o ident.o \
 -      interpolate.o \
 +      pretty.o interpolate.o hash.o \
        lockfile.o \
        patch-ids.o \
        object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
        write_or_die.o trace.o list-objects.o grep.o match-trees.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
        color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
 -      convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o
 +      convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
 +      transport.o bundle.o walker.o parse-options.o
  
  BUILTIN_OBJS = \
        builtin-add.o \
        builtin-diff-files.o \
        builtin-diff-index.o \
        builtin-diff-tree.o \
 +      builtin-fetch.o \
 +      builtin-fetch-pack.o \
        builtin-fetch--tool.o \
        builtin-fmt-merge-msg.o \
        builtin-for-each-ref.o \
        builtin-reflog.o \
        builtin-config.o \
        builtin-rerere.o \
 +      builtin-reset.o \
        builtin-rev-list.o \
        builtin-rev-parse.o \
        builtin-revert.o \
@@@ -405,27 -398,23 +407,27 @@@ ifeq ($(uname_S),Darwin
        NEEDS_LIBICONV = YesPlease
        OLD_ICONV = UnfortunatelyYes
        NO_STRLCPY = YesPlease
 +      NO_MEMMEM = YesPlease
  endif
  ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
        NEEDS_NSL = YesPlease
        SHELL_PATH = /bin/bash
        NO_STRCASESTR = YesPlease
 +      NO_MEMMEM = YesPlease
        NO_HSTRERROR = YesPlease
        ifeq ($(uname_R),5.8)
                NEEDS_LIBICONV = YesPlease
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
 +              NO_MKDTEMP = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
        endif
        ifeq ($(uname_R),5.9)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
 +              NO_MKDTEMP = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
        endif
@@@ -437,7 -426,6 +439,7 @@@ ifeq ($(uname_O),Cygwin
        NO_D_TYPE_IN_DIRENT = YesPlease
        NO_D_INO_IN_DIRENT = YesPlease
        NO_STRCASESTR = YesPlease
 +      NO_MEMMEM = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
  endif
  ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
 +      NO_MEMMEM = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
  endif
  ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
 +      NO_MEMMEM = YesPlease
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
@@@ -472,7 -458,6 +474,7 @@@ ifeq ($(uname_S),NetBSD
  endif
  ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
 +      NO_MEMMEM = YesPlease
        NO_STRLCPY = YesPlease
        NEEDS_LIBICONV=YesPlease
  endif
@@@ -484,7 -469,6 +486,7 @@@ ifeq ($(uname_S),IRIX64
        NO_IPV6=YesPlease
        NO_SETENV=YesPlease
        NO_STRCASESTR=YesPlease
 +      NO_MEMMEM = YesPlease
        NO_STRLCPY = YesPlease
        NO_SOCKADDR_STORAGE=YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
@@@ -522,9 -506,7 +524,9 @@@ els
        CC_LD_DYNPATH = -R
  endif
  
 -ifndef NO_CURL
 +ifdef NO_CURL
 +      BASIC_CFLAGS += -DNO_CURL
 +else
        ifdef CURLDIR
                # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
                BASIC_CFLAGS += -I$(CURLDIR)/include
        else
                CURL_LIBCURL = -lcurl
        endif
 -      PROGRAMS += git-http-fetch$X
 +      BUILTIN_OBJS += builtin-http-fetch.o
 +      EXTLIBS += $(CURL_LIBCURL)
 +      LIB_OBJS += http.o http-walker.o
        curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
@@@ -616,10 -596,6 +618,10 @@@ ifdef NO_SETEN
        COMPAT_CFLAGS += -DNO_SETENV
        COMPAT_OBJS += compat/setenv.o
  endif
 +ifdef NO_MKDTEMP
 +      COMPAT_CFLAGS += -DNO_MKDTEMP
 +      COMPAT_OBJS += compat/mkdtemp.o
 +endif
  ifdef NO_UNSETENV
        COMPAT_CFLAGS += -DNO_UNSETENV
        COMPAT_OBJS += compat/unsetenv.o
@@@ -663,10 -639,6 +665,10 @@@ ifdef OLD_ICON
        BASIC_CFLAGS += -DOLD_ICONV
  endif
  
 +ifdef NO_DEFLATE_BOUND
 +      BASIC_CFLAGS += -DNO_DEFLATE_BOUND
 +endif
 +
  ifdef PPC_SHA1
        SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
@@@ -691,15 -663,6 +693,15 @@@ ifdef NO_HSTRERRO
        COMPAT_CFLAGS += -DNO_HSTRERROR
        COMPAT_OBJS += compat/hstrerror.o
  endif
 +ifdef NO_MEMMEM
 +      COMPAT_CFLAGS += -DNO_MEMMEM
 +      COMPAT_OBJS += compat/memmem.o
 +endif
 +
 +ifdef THREADED_DELTA_SEARCH
 +      BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
 +      EXTLIBS += -lpthread
 +endif
  
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK=NoThanks
@@@ -848,7 -811,6 +850,7 @@@ gitweb/gitweb.cgi: gitweb/gitweb.per
            -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
            -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
            -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
 +          -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
            -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
            -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
            -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
@@@ -905,23 -867,35 +907,23 @@@ http.o: http.c GIT-CFLAG
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
  
  ifdef NO_EXPAT
 -http-fetch.o: http-fetch.c http.h GIT-CFLAGS
 +http-walker.o: http-walker.c http.h GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
  endif
  
  git-%$X: %.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
  
 -ssh-pull.o: ssh-fetch.c
 -ssh-push.o: ssh-upload.c
 -git-local-fetch$X: fetch.o
 -git-ssh-fetch$X: rsh.o fetch.o
 -git-ssh-upload$X: rsh.o
 -git-ssh-pull$X: rsh.o fetch.o
 -git-ssh-push$X: rsh.o
 -
  git-imap-send$X: imap-send.o $(LIB_FILE)
  
 -http.o http-fetch.o http-push.o: http.h
 -git-http-fetch$X: fetch.o http.o http-fetch.o $(GITLIBS)
 -      $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 -              $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 +http.o http-walker.o http-push.o: http.h
  
  git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
 -$(LIB_OBJS) $(BUILTIN_OBJS) fetch.o: $(LIB_H)
 +$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
  $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
 -$(DIFF_OBJS): diffcore.h
  builtin-revert.o builtin-runstatus.o wt-status.o: wt-status.h
  
  $(LIB_FILE): $(LIB_OBJS)
@@@ -950,10 -924,6 +952,10 @@@ tags
        $(RM) tags
        $(FIND) . -name '*.[hcS]' -print | xargs ctags -a
  
 +cscope:
 +      $(RM) cscope*
 +      $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 +
  ### Detect prefix changes
  TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
               $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
@@@ -981,7 -951,7 +983,7 @@@ endi
  
  ### Testing rules
  
 -TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X
 +TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X
  
  all:: $(TEST_PROGRAMS)
  
@@@ -998,8 -968,6 +1000,8 @@@ test-date$X: date.o ctype.
  
  test-delta$X: diff-delta.o patch-delta.o
  
 +test-parse-options$X: parse-options.o
 +
  .PRECIOUS: $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
  
  test-%$X: test-%.o $(GITLIBS)
@@@ -1105,7 -1073,7 +1107,7 @@@ clean
                $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
 -      $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
 +      $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
        $(RM) -r autom4te.cache
        $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
@@@ -1123,19 -1091,19 +1125,19 @@@ endi
        $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
  
  .PHONY: all install clean strip
 -.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags .FORCE-GIT-CFLAGS
 +.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
  
  ### Check documentation
  #
  check-docs::
 -      @for v in $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk; \
 +      @(for v in $(ALL_PROGRAMS) $(BUILT_INS) git gitk; \
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
 -              git-merge-resolve | git-merge-stupid | \
 +              git-merge-resolve | git-merge-stupid | git-merge-subtree | \
                git-add--interactive | git-fsck-objects | git-init-db | \
 -              git-repo-config | git-fetch--tool | \
 -              git-ssh-pull | git-ssh-push ) continue ;; \
 +              git-rebase--interactive | \
 +              git-repo-config | git-fetch--tool ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
                echo "no doc: $$v"; \
                git) ;; \
                *) echo "no link: $$v";; \
                esac ; \
 -      done | sort
 +      done; \
 +      ( \
 +              sed -e '1,/^__DATA__/d' \
 +                  -e 's/[     ].*//' \
 +                  -e 's/^/listed /' Documentation/cmd-list.perl; \
 +              ls -1 Documentation/git*txt | \
 +              sed -e 's|Documentation/|documented |' \
 +                  -e 's/\.txt//'; \
 +      ) | while read how cmd; \
 +      do \
 +              case "$$how,$$cmd" in \
 +              *,git-citool | \
 +              *,git-gui | \
 +              documented,gitattributes | \
 +              documented,gitignore | \
 +              documented,gitmodules | \
 +              documented,git-tools | \
 +              sentinel,not,matching,is,ok ) continue ;; \
 +              esac; \
 +              case " $(ALL_PROGRAMS) $(BUILT_INS) git gitk " in \
 +              *" $$cmd "*)    ;; \
 +              *) echo "removed but $$how: $$cmd" ;; \
 +              esac; \
 +      done ) | sort
  
  ### Make sure built-ins do not have dups and listed in git.c
  #
diff --combined builtin-revert.c
index afc28845f211a8a575baee00f73354ce91ea5a8d,94e77771d24decc60c50adf0cd3ab272ce36d3f2..365b330f9e1f2989683611077d260fa49abcb889
@@@ -7,7 -7,6 +7,7 @@@
  #include "run-command.h"
  #include "exec_cmd.h"
  #include "utf8.h"
 +#include "parse-options.h"
  
  /*
   * This implements the builtins revert and cherry-pick.
   * Copyright (c) 2005 Junio C Hamano
   */
  
 -static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] <commit-ish>";
 +static const char * const revert_usage[] = {
 +      "git-revert [options] <commit-ish>",
 +      NULL
 +};
  
 -static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] <commit-ish>";
 +static const char * const cherry_pick_usage[] = {
 +      "git-cherry-pick [options] <commit-ish>",
 +      NULL
 +};
  
 -static int edit;
 -static int replay;
 +static int edit, no_replay, no_commit, needed_deref, mainline;
  static enum { REVERT, CHERRY_PICK } action;
 -static int no_commit;
  static struct commit *commit;
 -static int needed_deref;
  
  static const char *me;
  
  #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
  
 -static void parse_options(int argc, const char **argv)
 +static void parse_args(int argc, const char **argv)
  {
 -      const char *usage_str = action == REVERT ?
 -              revert_usage : cherry_pick_usage;
 +      const char * const * usage_str =
 +              action == REVERT ?  revert_usage : cherry_pick_usage;
        unsigned char sha1[20];
        const char *arg;
 -      int i;
 +      int noop;
 +      struct option options[] = {
 +              OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"),
 +              OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
 +              OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"),
 +              OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
 +              OPT_INTEGER('m', "mainline", &mainline, "parent number"),
 +              OPT_END(),
 +      };
 +
 +      if (parse_options(argc, argv, options, usage_str, 0) != 1)
 +              usage_with_options(usage_str, options);
 +      arg = argv[0];
  
 -      if (argc < 2)
 -              usage(usage_str);
 -
 -      for (i = 1; i < argc; i++) {
 -              arg = argv[i];
 -              if (arg[0] != '-')
 -                      break;
 -              if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit"))
 -                      no_commit = 1;
 -              else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit"))
 -                      edit = 1;
 -              else if (!strcmp(arg, "--no-edit"))
 -                      edit = 0;
 -              else if (!strcmp(arg, "-x") || !strcmp(arg, "--i-really-want-"
 -                              "to-expose-my-private-commit-object-name"))
 -                      replay = 0;
 -              else if (strcmp(arg, "-r"))
 -                      usage(usage_str);
 -      }
 -      if (i != argc - 1)
 -              usage(usage_str);
 -      arg = argv[argc - 1];
        if (get_sha1(arg, sha1))
                die ("Cannot find '%s'", arg);
        commit = (struct commit *)parse_object(sha1);
@@@ -162,7 -168,9 +162,7 @@@ static void set_author_ident_env(const 
                        char *line, *pend, *email, *timestamp;
  
                        p += 7;
 -                      line = xmalloc(eol + 1 - p);
 -                      memcpy(line, p, eol - p);
 -                      line[eol - p] = '\0';
 +                      line = xmemdupz(p, eol - p);
                        email = strchr(line, '<');
                        if (!email)
                                die ("Could not extract author email from %s",
@@@ -228,7 -236,7 +228,7 @@@ static int merge_recursive(const char *
  static int revert_or_cherry_pick(int argc, const char **argv)
  {
        unsigned char head[20];
 -      struct commit *base, *next;
 +      struct commit *base, *next, *parent;
        int i;
        char *oneline, *reencoded_message = NULL;
        const char *message, *encoding;
        git_config(git_default_config);
        me = action == REVERT ? "revert" : "cherry-pick";
        setenv(GIT_REFLOG_ACTION, me, 0);
 -      parse_options(argc, argv);
 +      parse_args(argc, argv);
  
        /* this is copied from the shell script, but it's never triggered... */
 -      if (action == REVERT && replay)
 +      if (action == REVERT && !no_replay)
                die("revert is incompatible with replay");
  
        if (no_commit) {
                /*
                 * We do not intend to commit immediately.  We just want to
 -               * merge the differences in.
 +               * merge the differences in, so let's compute the tree
 +               * that represents the "current" state for merge-recursive
 +               * to work on.
                 */
                if (write_tree(head, 0, NULL))
                        die ("Your index file is unmerged.");
                if (get_sha1("HEAD", head))
                        die ("You do not have a valid HEAD");
                wt_status_prepare(&s);
-               if (s.commitable || s.workdir_dirty)
+               if (s.commitable)
                        die ("Dirty index: cannot %s", me);
                discard_cache();
        }
  
        if (!commit->parents)
                die ("Cannot %s a root commit", me);
 -      if (commit->parents->next)
 -              die ("Cannot %s a multi-parent commit.", me);
 +      if (commit->parents->next) {
 +              /* Reverting or cherry-picking a merge commit */
 +              int cnt;
 +              struct commit_list *p;
 +
 +              if (!mainline)
 +                      die("Commit %s is a merge but no -m option was given.",
 +                          sha1_to_hex(commit->object.sha1));
 +
 +              for (cnt = 1, p = commit->parents;
 +                   cnt != mainline && p;
 +                   cnt++)
 +                      p = p->next;
 +              if (cnt != mainline || !p)
 +                      die("Commit %s does not have parent %d",
 +                          sha1_to_hex(commit->object.sha1), mainline);
 +              parent = p->item;
 +      } else if (0 < mainline)
 +              die("Mainline was specified but commit %s is not a merge.",
 +                  sha1_to_hex(commit->object.sha1));
 +      else
 +              parent = commit->parents->item;
 +
        if (!(message = commit->buffer))
                die ("Cannot get commit message for %s",
                                sha1_to_hex(commit->object.sha1));
                char *oneline_body = strchr(oneline, ' ');
  
                base = commit;
 -              next = commit->parents->item;
 +              next = parent;
                add_to_msg("Revert \"");
                add_to_msg(oneline_body + 1);
                add_to_msg("\"\n\nThis reverts commit ");
                add_to_msg(sha1_to_hex(commit->object.sha1));
                add_to_msg(".\n");
        } else {
 -              base = commit->parents->item;
 +              base = parent;
                next = commit;
                set_author_ident_env(message);
                add_message_to_msg(message);
 -              if (!replay) {
 +              if (no_replay) {
                        add_to_msg("(cherry picked from commit ");
                        add_to_msg(sha1_to_hex(commit->object.sha1));
                        add_to_msg(")\n");
@@@ -405,14 -390,13 +405,14 @@@ int cmd_revert(int argc, const char **a
  {
        if (isatty(0))
                edit = 1;
 +      no_replay = 1;
        action = REVERT;
        return revert_or_cherry_pick(argc, argv);
  }
  
  int cmd_cherry_pick(int argc, const char **argv, const char *prefix)
  {
 -      replay = 1;
 +      no_replay = 0;
        action = CHERRY_PICK;
        return revert_or_cherry_pick(argc, argv);
  }
diff --combined fast-import.c
index f93d7d6c9bf2db021ceb65766da87af32aecc1d1,5e83296bf49f9b41b019185539cbd4ec1bf94513..98c2bd535957a45e5ef189875859d4788d937e7e
@@@ -149,17 -149,21 +149,20 @@@ Format of STDIN stream
  #include "pack.h"
  #include "refs.h"
  #include "csum-file.h"
 -#include "strbuf.h"
  #include "quote.h"
  
  #define PACK_ID_BITS 16
  #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
+ #define DEPTH_BITS 13
+ #define MAX_DEPTH ((1<<DEPTH_BITS)-1)
  
  struct object_entry
  {
        struct object_entry *next;
        uint32_t offset;
-       unsigned type : TYPE_BITS;
-       unsigned pack_id : PACK_ID_BITS;
+       uint32_t type : TYPE_BITS,
+               pack_id : PACK_ID_BITS,
+               depth : DEPTH_BITS;
        unsigned char sha1[20];
  };
  
@@@ -182,10 -186,11 +185,10 @@@ struct mark_se
  
  struct last_object
  {
 -      void *data;
 -      unsigned long len;
 +      struct strbuf data;
        uint32_t offset;
        unsigned int depth;
 -      unsigned no_free:1;
 +      unsigned no_swap : 1;
  };
  
  struct mem_pool
@@@ -249,6 -254,12 +252,6 @@@ struct ta
        unsigned char sha1[20];
  };
  
 -struct dbuf
 -{
 -      void *buffer;
 -      size_t capacity;
 -};
 -
  struct hash_list
  {
        struct hash_list *next;
@@@ -309,15 -320,15 +312,15 @@@ static struct mark_set *marks
  static const char* mark_file;
  
  /* Our last blob */
 -static struct last_object last_blob;
 +static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 };
  
  /* Tree management */
  static unsigned int tree_entry_alloc = 1000;
  static void *avail_tree_entry;
  static unsigned int avail_tree_table_sz = 100;
  static struct avail_tree_content **avail_tree_table;
 -static struct dbuf old_tree;
 -static struct dbuf new_tree;
 +static struct strbuf old_tree = STRBUF_INIT;
 +static struct strbuf new_tree = STRBUF_INIT;
  
  /* Branch data */
  static unsigned long max_active_branches = 5;
@@@ -332,14 -343,14 +335,14 @@@ static struct tag *last_tag
  
  /* Input stream parsing */
  static whenspec_type whenspec = WHENSPEC_RAW;
 -static struct strbuf command_buf;
 +static struct strbuf command_buf = STRBUF_INIT;
  static int unread_command_buf;
  static struct recent_command cmd_hist = {&cmd_hist, &cmd_hist, NULL};
  static struct recent_command *cmd_tail = &cmd_hist;
  static struct recent_command *rc_free;
  static unsigned int cmd_save = 100;
  static uintmax_t next_mark;
 -static struct dbuf new_data;
 +static struct strbuf new_data = STRBUF_INIT;
  
  static void write_branch_report(FILE *rpt, struct branch *b)
  {
@@@ -559,6 -570,17 +562,6 @@@ static char *pool_strdup(const char *s
        return r;
  }
  
 -static void size_dbuf(struct dbuf *b, size_t maxlen)
 -{
 -      if (b->buffer) {
 -              if (b->capacity >= maxlen)
 -                      return;
 -              free(b->buffer);
 -      }
 -      b->capacity = ((maxlen / 1024) + 1) * 1024;
 -      b->buffer = xmalloc(b->capacity);
 -}
 -
  static void insert_mark(uintmax_t idnum, struct object_entry *oe)
  {
        struct mark_set *s = marks;
@@@ -949,7 -971,9 +952,7 @@@ static void end_packfile(void
        free(old_p);
  
        /* We can't carry a delta across packfiles. */
 -      free(last_blob.data);
 -      last_blob.data = NULL;
 -      last_blob.len = 0;
 +      strbuf_release(&last_blob.data);
        last_blob.offset = 0;
        last_blob.depth = 0;
  }
@@@ -985,7 -1009,8 +988,7 @@@ static size_t encode_header
  
  static int store_object(
        enum object_type type,
 -      void *dat,
 -      size_t datlen,
 +      struct strbuf *dat,
        struct last_object *last,
        unsigned char *sha1out,
        uintmax_t mark)
        z_stream s;
  
        hdrlen = sprintf((char*)hdr,"%s %lu", typename(type),
 -              (unsigned long)datlen) + 1;
 +              (unsigned long)dat->len) + 1;
        SHA1_Init(&c);
        SHA1_Update(&c, hdr, hdrlen);
 -      SHA1_Update(&c, dat, datlen);
 +      SHA1_Update(&c, dat->buf, dat->len);
        SHA1_Final(sha1, &c);
        if (sha1out)
                hashcpy(sha1out, sha1);
                return 1;
        }
  
 -      if (last && last->data && last->depth < max_depth) {
 -              delta = diff_delta(last->data, last->len,
 -                      dat, datlen,
 +      if (last && last->data.buf && last->depth < max_depth) {
 +              delta = diff_delta(last->data.buf, last->data.len,
 +                      dat->buf, dat->len,
                        &deltalen, 0);
 -              if (delta && deltalen >= datlen) {
 +              if (delta && deltalen >= dat->len) {
                        free(delta);
                        delta = NULL;
                }
                s.next_in = delta;
                s.avail_in = deltalen;
        } else {
 -              s.next_in = dat;
 -              s.avail_in = datlen;
 +              s.next_in = (void *)dat->buf;
 +              s.avail_in = dat->len;
        }
        s.avail_out = deflateBound(&s, s.avail_in);
        s.next_out = out = xmalloc(s.avail_out);
  
                        memset(&s, 0, sizeof(s));
                        deflateInit(&s, zlib_compression_level);
 -                      s.next_in = dat;
 -                      s.avail_in = datlen;
 +                      s.next_in = (void *)dat->buf;
 +                      s.avail_in = dat->len;
                        s.avail_out = deflateBound(&s, s.avail_in);
                        s.next_out = out = xrealloc(out, s.avail_out);
                        while (deflate(&s, Z_FINISH) == Z_OK)
                unsigned pos = sizeof(hdr) - 1;
  
                delta_count_by_type[type]++;
-               last->depth++;
+               e->depth = last->depth + 1;
  
                hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
                write_or_die(pack_data->pack_fd, hdr, hdrlen);
                write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos);
                pack_size += sizeof(hdr) - pos;
        } else {
-               if (last)
-                       last->depth = 0;
+               e->depth = 0;
 -              hdrlen = encode_header(type, datlen, hdr);
 +              hdrlen = encode_header(type, dat->len, hdr);
                write_or_die(pack_data->pack_fd, hdr, hdrlen);
                pack_size += hdrlen;
        }
        free(out);
        free(delta);
        if (last) {
 -              if (!last->no_free)
 -                      free(last->data);
 -              last->data = dat;
 +              if (last->no_swap) {
 +                      last->data = *dat;
 +              } else {
 +                      strbuf_swap(&last->data, dat);
 +              }
                last->offset = e->offset;
 -              last->len = datlen;
+               last->depth = e->depth;
        }
        return 0;
  }
@@@ -1160,7 -1184,7 +1163,7 @@@ static void load_tree(struct tree_entr
        if (myoe && myoe->pack_id != MAX_PACK_ID) {
                if (myoe->type != OBJ_TREE)
                        die("Not a tree: %s", sha1_to_hex(sha1));
-               t->delta_depth = 0;
+               t->delta_depth = myoe->depth;
                buf = gfi_unpack_entry(myoe, &size);
        } else {
                enum object_type type;
@@@ -1209,10 -1233,14 +1212,10 @@@ static int tecmp1 (const void *_a, cons
                b->name->str_dat, b->name->str_len, b->versions[1].mode);
  }
  
 -static void mktree(struct tree_content *t,
 -      int v,
 -      unsigned long *szp,
 -      struct dbuf *b)
 +static void mktree(struct tree_content *t, int v, struct strbuf *b)
  {
        size_t maxlen = 0;
        unsigned int i;
 -      char *c;
  
        if (!v)
                qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp0);
                        maxlen += t->entries[i]->name->str_len + 34;
        }
  
 -      size_dbuf(b, maxlen);
 -      c = b->buffer;
 +      strbuf_reset(b);
 +      strbuf_grow(b, maxlen);
        for (i = 0; i < t->entry_count; i++) {
                struct tree_entry *e = t->entries[i];
                if (!e->versions[v].mode)
                        continue;
 -              c += sprintf(c, "%o", (unsigned int)e->versions[v].mode);
 -              *c++ = ' ';
 -              strcpy(c, e->name->str_dat);
 -              c += e->name->str_len + 1;
 -              hashcpy((unsigned char*)c, e->versions[v].sha1);
 -              c += 20;
 +              strbuf_addf(b, "%o %s%c", (unsigned int)e->versions[v].mode,
 +                                      e->name->str_dat, '\0');
 +              strbuf_add(b, e->versions[v].sha1, 20);
        }
 -      *szp = c - (char*)b->buffer;
  }
  
  static void store_tree(struct tree_entry *root)
  {
        struct tree_content *t = root->tree;
        unsigned int i, j, del;
 -      unsigned long new_len;
 -      struct last_object lo;
 +      struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 };
        struct object_entry *le;
  
        if (!is_null_sha1(root->versions[1].sha1))
        }
  
        le = find_object(root->versions[0].sha1);
 -      if (!S_ISDIR(root->versions[0].mode)
 -              || !le
 -              || le->pack_id != pack_id) {
 -              lo.data = NULL;
 -              lo.depth = 0;
 -              lo.no_free = 0;
 -      } else {
 -              mktree(t, 0, &lo.len, &old_tree);
 -              lo.data = old_tree.buffer;
 +      if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
 +              mktree(t, 0, &old_tree);
 +              lo.data = old_tree;
                lo.offset = le->offset;
                lo.depth = t->delta_depth;
 -              lo.no_free = 1;
        }
  
 -      mktree(t, 1, &new_len, &new_tree);
 -      store_object(OBJ_TREE, new_tree.buffer, new_len,
 -              &lo, root->versions[1].sha1, 0);
 +      mktree(t, 1, &new_tree);
 +      store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].sha1, 0);
  
        t->delta_depth = lo.depth;
        for (i = 0, j = 0, del = 0; i < t->entry_count; i++) {
@@@ -1547,25 -1588,20 +1550,25 @@@ static void dump_marks(void
                        mark_file, strerror(errno));
  }
  
 -static void read_next_command(void)
 +static int read_next_command(void)
  {
 +      static int stdin_eof = 0;
 +
 +      if (stdin_eof) {
 +              unread_command_buf = 0;
 +              return EOF;
 +      }
 +
        do {
                if (unread_command_buf) {
                        unread_command_buf = 0;
 -                      if (command_buf.eof)
 -                              return;
                } else {
                        struct recent_command *rc;
  
 -                      command_buf.buf = NULL;
 -                      read_line(&command_buf, stdin, '\n');
 -                      if (command_buf.eof)
 -                              return;
 +                      strbuf_detach(&command_buf, NULL);
 +                      stdin_eof = strbuf_getline(&command_buf, stdin, '\n');
 +                      if (stdin_eof)
 +                              return EOF;
  
                        rc = rc_free;
                        if (rc)
                        cmd_tail = rc;
                }
        } while (command_buf.buf[0] == '#');
 +
 +      return 0;
  }
  
  static void skip_optional_lf(void)
@@@ -1605,36 -1639,42 +1608,36 @@@ static void cmd_mark(void
                next_mark = 0;
  }
  
 -static void *cmd_data (size_t *size)
 +static void cmd_data(struct strbuf *sb)
  {
 -      size_t length;
 -      char *buffer;
 +      strbuf_reset(sb);
  
        if (prefixcmp(command_buf.buf, "data "))
                die("Expected 'data n' command, found: %s", command_buf.buf);
  
        if (!prefixcmp(command_buf.buf + 5, "<<")) {
                char *term = xstrdup(command_buf.buf + 5 + 2);
 -              size_t sz = 8192, term_len = command_buf.len - 5 - 2;
 -              length = 0;
 -              buffer = xmalloc(sz);
 -              command_buf.buf = NULL;
 +              size_t term_len = command_buf.len - 5 - 2;
 +
 +              strbuf_detach(&command_buf, NULL);
                for (;;) {
 -                      read_line(&command_buf, stdin, '\n');
 -                      if (command_buf.eof)
 +                      if (strbuf_getline(&command_buf, stdin, '\n') == EOF)
                                die("EOF in data (terminator '%s' not found)", term);
                        if (term_len == command_buf.len
                                && !strcmp(term, command_buf.buf))
                                break;
 -                      ALLOC_GROW(buffer, length + command_buf.len, sz);
 -                      memcpy(buffer + length,
 -                              command_buf.buf,
 -                              command_buf.len - 1);
 -                      length += command_buf.len - 1;
 -                      buffer[length++] = '\n';
 +                      strbuf_addbuf(sb, &command_buf);
 +                      strbuf_addch(sb, '\n');
                }
                free(term);
        }
        else {
 -              size_t n = 0;
 +              size_t n = 0, length;
 +
                length = strtoul(command_buf.buf + 5, NULL, 10);
 -              buffer = xmalloc(length);
 +
                while (n < length) {
 -                      size_t s = fread(buffer + n, 1, length - n, stdin);
 +                      size_t s = strbuf_fread(sb, length - n, stdin);
                        if (!s && feof(stdin))
                                die("EOF in data (%lu bytes remaining)",
                                        (unsigned long)(length - n));
        }
  
        skip_optional_lf();
 -      *size = length;
 -      return buffer;
  }
  
  static int validate_raw_date(const char *src, char *result, int maxlen)
@@@ -1705,12 -1747,15 +1708,12 @@@ static char *parse_ident(const char *bu
  
  static void cmd_new_blob(void)
  {
 -      size_t l;
 -      void *d;
 +      static struct strbuf buf = STRBUF_INIT;
  
        read_next_command();
        cmd_mark();
 -      d = cmd_data(&l);
 -
 -      if (store_object(OBJ_BLOB, d, l, &last_blob, NULL, next_mark))
 -              free(d);
 +      cmd_data(&buf);
 +      store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark);
  }
  
  static void unload_one_branch(void)
@@@ -1760,7 -1805,7 +1763,7 @@@ static void load_branch(struct branch *
  static void file_change_m(struct branch *b)
  {
        const char *p = command_buf.buf + 2;
 -      char *p_uq;
 +      static struct strbuf uq = STRBUF_INIT;
        const char *endp;
        struct object_entry *oe = oe;
        unsigned char sha1[20];
        if (*p++ != ' ')
                die("Missing space after SHA1: %s", command_buf.buf);
  
 -      p_uq = unquote_c_style(p, &endp);
 -      if (p_uq) {
 +      strbuf_reset(&uq);
 +      if (!unquote_c_style(&uq, p, &endp)) {
                if (*endp)
                        die("Garbage after path in: %s", command_buf.buf);
 -              p = p_uq;
 +              p = uq.buf;
        }
  
        if (inline_data) {
 -              size_t l;
 -              void *d;
 -              if (!p_uq)
 -                      p = p_uq = xstrdup(p);
 +              static struct strbuf buf = STRBUF_INIT;
 +
 +              if (p != uq.buf) {
 +                      strbuf_addstr(&uq, p);
 +                      p = uq.buf;
 +              }
                read_next_command();
 -              d = cmd_data(&l);
 -              if (store_object(OBJ_BLOB, d, l, &last_blob, sha1, 0))
 -                      free(d);
 +              cmd_data(&buf);
 +              store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0);
        } else if (oe) {
                if (oe->type != OBJ_BLOB)
                        die("Not a blob (actually a %s): %s",
        }
  
        tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode, NULL);
 -      free(p_uq);
  }
  
  static void file_change_d(struct branch *b)
  {
        const char *p = command_buf.buf + 2;
 -      char *p_uq;
 +      static struct strbuf uq = STRBUF_INIT;
        const char *endp;
  
 -      p_uq = unquote_c_style(p, &endp);
 -      if (p_uq) {
 +      strbuf_reset(&uq);
 +      if (!unquote_c_style(&uq, p, &endp)) {
                if (*endp)
                        die("Garbage after path in: %s", command_buf.buf);
 -              p = p_uq;
 +              p = uq.buf;
        }
        tree_content_remove(&b->branch_tree, p, NULL);
 -      free(p_uq);
  }
  
  static void file_change_cr(struct branch *b, int rename)
  {
        const char *s, *d;
 -      char *s_uq, *d_uq;
 +      static struct strbuf s_uq = STRBUF_INIT;
 +      static struct strbuf d_uq = STRBUF_INIT;
        const char *endp;
        struct tree_entry leaf;
  
        s = command_buf.buf + 2;
 -      s_uq = unquote_c_style(s, &endp);
 -      if (s_uq) {
 +      strbuf_reset(&s_uq);
 +      if (!unquote_c_style(&s_uq, s, &endp)) {
                if (*endp != ' ')
                        die("Missing space after source: %s", command_buf.buf);
 -      }
 -      else {
 +      } else {
                endp = strchr(s, ' ');
                if (!endp)
                        die("Missing space after source: %s", command_buf.buf);
 -              s_uq = xmalloc(endp - s + 1);
 -              memcpy(s_uq, s, endp - s);
 -              s_uq[endp - s] = 0;
 +              strbuf_add(&s_uq, s, endp - s);
        }
 -      s = s_uq;
 +      s = s_uq.buf;
  
        endp++;
        if (!*endp)
                die("Missing dest: %s", command_buf.buf);
  
        d = endp;
 -      d_uq = unquote_c_style(d, &endp);
 -      if (d_uq) {
 +      strbuf_reset(&d_uq);
 +      if (!unquote_c_style(&d_uq, d, &endp)) {
                if (*endp)
                        die("Garbage after dest in: %s", command_buf.buf);
 -              d = d_uq;
 +              d = d_uq.buf;
        }
  
        memset(&leaf, 0, sizeof(leaf));
                leaf.versions[1].sha1,
                leaf.versions[1].mode,
                leaf.tree);
 -
 -      free(s_uq);
 -      free(d_uq);
  }
  
  static void file_change_deleteall(struct branch *b)
@@@ -2014,8 -2065,9 +2017,8 @@@ static struct hash_list *cmd_merge(unsi
  
  static void cmd_new_commit(void)
  {
 +      static struct strbuf msg = STRBUF_INIT;
        struct branch *b;
 -      void *msg;
 -      size_t msglen;
        char *sp;
        char *author = NULL;
        char *committer = NULL;
        }
        if (!committer)
                die("Expected committer but didn't get one");
 -      msg = cmd_data(&msglen);
 +      cmd_data(&msg);
        read_next_command();
        cmd_from(b);
        merge_list = cmd_merge(&merge_count);
        }
  
        /* file_change* */
 -      while (!command_buf.eof && command_buf.len > 1) {
 +      while (command_buf.len > 0) {
                if (!prefixcmp(command_buf.buf, "M "))
                        file_change_m(b);
                else if (!prefixcmp(command_buf.buf, "D "))
                        unread_command_buf = 1;
                        break;
                }
 -              read_next_command();
 +              if (read_next_command() == EOF)
 +                      break;
        }
  
        /* build the tree and the commit */
        store_tree(&b->branch_tree);
        hashcpy(b->branch_tree.versions[0].sha1,
                b->branch_tree.versions[1].sha1);
 -      size_dbuf(&new_data, 114 + msglen
 -              + merge_count * 49
 -              + (author
 -                      ? strlen(author) + strlen(committer)
 -                      : 2 * strlen(committer)));
 -      sp = new_data.buffer;
 -      sp += sprintf(sp, "tree %s\n",
 +
 +      strbuf_reset(&new_data);
 +      strbuf_addf(&new_data, "tree %s\n",
                sha1_to_hex(b->branch_tree.versions[1].sha1));
        if (!is_null_sha1(b->sha1))
 -              sp += sprintf(sp, "parent %s\n", sha1_to_hex(b->sha1));
 +              strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(b->sha1));
        while (merge_list) {
                struct hash_list *next = merge_list->next;
 -              sp += sprintf(sp, "parent %s\n", sha1_to_hex(merge_list->sha1));
 +              strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(merge_list->sha1));
                free(merge_list);
                merge_list = next;
        }
 -      sp += sprintf(sp, "author %s\n", author ? author : committer);
 -      sp += sprintf(sp, "committer %s\n", committer);
 -      *sp++ = '\n';
 -      memcpy(sp, msg, msglen);
 -      sp += msglen;
 +      strbuf_addf(&new_data,
 +              "author %s\n"
 +              "committer %s\n"
 +              "\n",
 +              author ? author : committer, committer);
 +      strbuf_addbuf(&new_data, &msg);
        free(author);
        free(committer);
 -      free(msg);
  
 -      if (!store_object(OBJ_COMMIT,
 -              new_data.buffer, sp - (char*)new_data.buffer,
 -              NULL, b->sha1, next_mark))
 +      if (!store_object(OBJ_COMMIT, &new_data, NULL, b->sha1, next_mark))
                b->pack_id = pack_id;
        b->last_commit = object_count_by_type[OBJ_COMMIT];
  }
  
  static void cmd_new_tag(void)
  {
 +      static struct strbuf msg = STRBUF_INIT;
        char *sp;
        const char *from;
        char *tagger;
        struct branch *s;
 -      void *msg;
 -      size_t msglen;
        struct tag *t;
        uintmax_t from_mark = 0;
        unsigned char sha1[20];
  
        /* tag payload/message */
        read_next_command();
 -      msg = cmd_data(&msglen);
 +      cmd_data(&msg);
  
        /* build the tag object */
 -      size_dbuf(&new_data, 67+strlen(t->name)+strlen(tagger)+msglen);
 -      sp = new_data.buffer;
 -      sp += sprintf(sp, "object %s\n", sha1_to_hex(sha1));
 -      sp += sprintf(sp, "type %s\n", commit_type);
 -      sp += sprintf(sp, "tag %s\n", t->name);
 -      sp += sprintf(sp, "tagger %s\n", tagger);
 -      *sp++ = '\n';
 -      memcpy(sp, msg, msglen);
 -      sp += msglen;
 +      strbuf_reset(&new_data);
 +      strbuf_addf(&new_data,
 +              "object %s\n"
 +              "type %s\n"
 +              "tag %s\n"
 +              "tagger %s\n"
 +              "\n",
 +              sha1_to_hex(sha1), commit_type, t->name, tagger);
 +      strbuf_addbuf(&new_data, &msg);
        free(tagger);
 -      free(msg);
  
 -      if (store_object(OBJ_TAG, new_data.buffer,
 -              sp - (char*)new_data.buffer,
 -              NULL, t->sha1, 0))
 +      if (store_object(OBJ_TAG, &new_data, NULL, t->sha1, 0))
                t->pack_id = MAX_PACK_ID;
        else
                t->pack_id = pack_id;
@@@ -2198,7 -2259,7 +2201,7 @@@ static void cmd_reset_branch(void
        else
                b = new_branch(sp);
        read_next_command();
 -      if (!cmd_from(b) && command_buf.len > 1)
 +      if (!cmd_from(b) && command_buf.len > 0)
                unread_command_buf = 1;
  }
  
@@@ -2215,7 -2276,7 +2218,7 @@@ static void cmd_checkpoint(void
  
  static void cmd_progress(void)
  {
 -      fwrite(command_buf.buf, 1, command_buf.len - 1, stdout);
 +      fwrite(command_buf.buf, 1, command_buf.len, stdout);
        fputc('\n', stdout);
        fflush(stdout);
        skip_optional_lf();
@@@ -2265,7 -2326,7 +2268,7 @@@ int main(int argc, const char **argv
  
        git_config(git_default_config);
        alloc_objects(object_entry_alloc);
 -      strbuf_init(&command_buf);
 +      strbuf_init(&command_buf, 0);
        atom_table = xcalloc(atom_table_sz, sizeof(struct atom_str*));
        branch_table = xcalloc(branch_table_sz, sizeof(struct branch*));
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
                }
                else if (!prefixcmp(a, "--max-pack-size="))
                        max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024;
-               else if (!prefixcmp(a, "--depth="))
+               else if (!prefixcmp(a, "--depth=")) {
                        max_depth = strtoul(a + 8, NULL, 0);
+                       if (max_depth > MAX_DEPTH)
+                               die("--depth cannot exceed %u", MAX_DEPTH);
+               }
                else if (!prefixcmp(a, "--active-branches="))
                        max_active_branches = strtoul(a + 18, NULL, 0);
                else if (!prefixcmp(a, "--import-marks="))
        prepare_packed_git();
        start_packfile();
        set_die_routine(die_nicely);
 -      for (;;) {
 -              read_next_command();
 -              if (command_buf.eof)
 -                      break;
 -              else if (!strcmp("blob", command_buf.buf))
 +      while (read_next_command() != EOF) {
 +              if (!strcmp("blob", command_buf.buf))
                        cmd_new_blob();
                else if (!prefixcmp(command_buf.buf, "commit "))
                        cmd_new_commit();
diff --combined git-clean.sh
index 521fabc20f9a7bbee222cb60c937e8c0f15b8fd6,931d1aa4e4ed6cefb52c2dfe31aa5f059d0b2129..ad68595fbf313c1cab7de565e6d44f2c87aff384
@@@ -20,13 -20,12 +20,13 @@@ require_work_tre
  ignored=
  ignoredonly=
  cleandir=
 -disabled="`git config --bool clean.requireForce`"
  rmf="rm -f --"
  rmrf="rm -rf --"
  rm_refuse="echo Not removing"
  echo1="echo"
  
 +disabled=$(git config --bool clean.requireForce)
 +
  while test $# != 0
  do
        case "$1" in
                cleandir=1
                ;;
        -f)
 -              disabled=
 +              disabled=false
                ;;
        -n)
 -              disabled=
 +              disabled=false
                rmf="echo Would remove"
                rmrf="echo Would remove"
                rm_refuse="echo Would not remove"
        shift
  done
  
 -if [ "$disabled" = true ]; then
 -      echo "clean.requireForce set and -n or -f not given; refusing to clean"
 -      exit 1
 -fi
 +# requireForce used to default to false but now it defaults to true.
 +# IOW, lack of explicit "clean.requireForce = false" is taken as
 +# "clean.requireForce = true".
 +case "$disabled" in
 +"")
 +      die "clean.requireForce not set and -n or -f not given; refusing to clean"
 +      ;;
 +"true")
 +      die "clean.requireForce set and -n or -f not given; refusing to clean"
 +      ;;
 +esac
  
  case "$ignored,$ignoredonly" in
        1,1) usage;;
@@@ -83,15 -75,22 +83,22 @@@ esa
  
  if [ -z "$ignored" ]; then
        excl="--exclude-per-directory=.gitignore"
+       excl_info= excludes_file=
        if [ -f "$GIT_DIR/info/exclude" ]; then
                excl_info="--exclude-from=$GIT_DIR/info/exclude"
        fi
+       if cfg_excl=$(git config core.excludesfile) && test -f "$cfg_excl"
+       then
+               excludes_file="--exclude-from=$cfg_excl"
+       fi
        if [ "$ignoredonly" ]; then
                excl="$excl --ignored"
        fi
  fi
  
- git ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" |
+ git ls-files --others --directory \
+       $excl ${excl_info:+"$excl_info"} ${excludes_file:+"$excludes_file"} \
+       -- "$@" |
  while read -r file; do
        if [ -d "$file" -a ! -L "$file" ]; then
                if [ -z "$cleandir" ]; then
index 11139048fe2238a3e06972252d7410da4058dccb,984146b5c2216e4dc4c7bd62bb60bd8ef9c3b615..f1039d1a2146ed68217d849d7588d9ca65af785b
@@@ -80,7 -80,7 +80,7 @@@ cat "$1".tm
  action=pick
  for line in $FAKE_LINES; do
        case $line in
 -      squash)
 +      squash|edit)
                action="$line";;
        *)
                echo sed -n "${line}s/^pick/$action/p"
@@@ -149,7 -149,7 +149,7 @@@ test_expect_success 'stop on conflictin
        diff -u expect .git/.dotest-merge/patch &&
        diff -u expect2 file1 &&
        test 4 = $(grep -v "^#" < .git/.dotest-merge/done | wc -l) &&
-       test 0 = $(grep -v "^#" < .git/.dotest-merge/todo | wc -l)
+       test 0 = $(grep -v "^#" < .git/.dotest-merge/git-rebase-todo | wc -l)
  '
  
  test_expect_success 'abort' '
@@@ -297,24 -297,4 +297,24 @@@ test_expect_success 'ignore patch if i
        test $HEAD = $(git rev-parse HEAD^)
  '
  
 +test_expect_success '--continue tries to commit, even for "edit"' '
 +      parent=$(git rev-parse HEAD^) &&
 +      test_tick &&
 +      FAKE_LINES="edit 1" git rebase -i HEAD^ &&
 +      echo edited > file7 &&
 +      git add file7 &&
 +      FAKE_COMMIT_MESSAGE="chouette!" git rebase --continue &&
 +      test edited = $(git show HEAD:file7) &&
 +      git show HEAD | grep chouette &&
 +      test $parent = $(git rev-parse HEAD^)
 +'
 +
 +test_expect_success 'rebase a detached HEAD' '
 +      grandparent=$(git rev-parse HEAD~2) &&
 +      git checkout $(git rev-parse HEAD) &&
 +      test_tick &&
 +      FAKE_LINES="2 1" git rebase -i HEAD~2 &&
 +      test $grandparent = $(git rev-parse HEAD~2)
 +'
 +
  test_done
diff --combined t/t7300-clean.sh
index 25d3102dedd31f43bfaabd1ae05fdebbc51863f8,0ed4ae282728a1701a8d67ae16572db14f1dee69..f013c176ed910d278cbb886004429869f288ebc7
@@@ -7,8 -7,6 +7,8 @@@ test_description='git-clean basic tests
  
  . ./test-lib.sh
  
 +git config clean.requireForce no
 +
  test_expect_success 'setup' '
  
        mkdir -p src &&
@@@ -39,93 -37,6 +39,93 @@@ test_expect_success 'git-clean' 
  
  '
  
 +test_expect_success 'git-clean src/' '
 +
 +      mkdir -p build docs &&
 +      touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
 +      git-clean src/ &&
 +      test -f Makefile &&
 +      test -f README &&
 +      test -f src/part1.c &&
 +      test -f src/part2.c &&
 +      test -f a.out &&
 +      test ! -f src/part3.c &&
 +      test -f docs/manual.txt &&
 +      test -f obj.o &&
 +      test -f build/lib.so
 +
 +'
 +
 +test_expect_success 'git-clean src/ src/' '
 +
 +      mkdir -p build docs &&
 +      touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
 +      git-clean src/ src/ &&
 +      test -f Makefile &&
 +      test -f README &&
 +      test -f src/part1.c &&
 +      test -f src/part2.c &&
 +      test -f a.out &&
 +      test ! -f src/part3.c &&
 +      test -f docs/manual.txt &&
 +      test -f obj.o &&
 +      test -f build/lib.so
 +
 +'
 +
 +test_expect_success 'git-clean with prefix' '
 +
 +      mkdir -p build docs &&
 +      touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
 +      (cd src/ && git-clean) &&
 +      test -f Makefile &&
 +      test -f README &&
 +      test -f src/part1.c &&
 +      test -f src/part2.c &&
 +      test -f a.out &&
 +      test ! -f src/part3.c &&
 +      test -f docs/manual.txt &&
 +      test -f obj.o &&
 +      test -f build/lib.so
 +
 +'
 +test_expect_success 'git-clean -d with prefix and path' '
 +
 +      mkdir -p build docs src/feature &&
 +      touch a.out src/part3.c src/feature/file.c docs/manual.txt obj.o build/lib.so &&
 +      (cd src/ && git-clean -d feature/) &&
 +      test -f Makefile &&
 +      test -f README &&
 +      test -f src/part1.c &&
 +      test -f src/part2.c &&
 +      test -f a.out &&
 +      test -f src/part3.c &&
 +      test ! -f src/feature/file.c &&
 +      test -f docs/manual.txt &&
 +      test -f obj.o &&
 +      test -f build/lib.so
 +
 +'
 +
 +test_expect_success 'git-clean symbolic link' '
 +
 +      mkdir -p build docs &&
 +      touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
 +      ln -s docs/manual.txt src/part4.c
 +      git-clean &&
 +      test -f Makefile &&
 +      test -f README &&
 +      test -f src/part1.c &&
 +      test -f src/part2.c &&
 +      test ! -f a.out &&
 +      test ! -f src/part3.c &&
 +      test ! -f src/part4.c &&
 +      test -f docs/manual.txt &&
 +      test -f obj.o &&
 +      test -f build/lib.so
 +
 +'
 +
  test_expect_success 'git-clean -n' '
  
        mkdir -p build docs &&
@@@ -160,24 -71,6 +160,24 @@@ test_expect_success 'git-clean -d' 
  
  '
  
 +test_expect_success 'git-clean -d src/ examples/' '
 +
 +      mkdir -p build docs examples &&
 +      touch a.out src/part3.c docs/manual.txt obj.o build/lib.so examples/1.c &&
 +      git-clean -d src/ examples/ &&
 +      test -f Makefile &&
 +      test -f README &&
 +      test -f src/part1.c &&
 +      test -f src/part2.c &&
 +      test -f a.out &&
 +      test ! -f src/part3.c &&
 +      test ! -f examples/1.c &&
 +      test -f docs/manual.txt &&
 +      test -f obj.o &&
 +      test -f build/lib.so
 +
 +'
 +
  test_expect_success 'git-clean -x' '
  
        mkdir -p build docs &&
@@@ -246,13 -139,6 +246,13 @@@ test_expect_success 'git-clean -d -X' 
  
  '
  
 +test_expect_success 'clean.requireForce defaults to true' '
 +
 +      git config --unset clean.requireForce &&
 +      ! git-clean
 +
 +'
 +
  test_expect_success 'clean.requireForce' '
  
        git config clean.requireForce true &&
@@@ -291,4 -177,15 +291,15 @@@ test_expect_success 'clean.requireForc
  
  '
  
+ test_expect_success 'core.excludesfile' '
+       echo excludes >excludes &&
+       echo included >included &&
+       git config core.excludesfile excludes &&
+       output=$(git clean -n excludes included 2>&1) &&
+       expr "$output" : ".*included" >/dev/null &&
+       ! expr "$output" : ".*excludes" >/dev/null
+ '
  test_done