Merge branch 'sn/doc-typofix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 24 Jul 2012 03:55:21 +0000 (20:55 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 24 Jul 2012 03:55:22 +0000 (20:55 -0700)
* sn/doc-typofix:
doc: A few minor copy edits.

60 files changed:
.gitignore
Documentation/RelNotes/1.7.11.3.txt [new file with mode: 0644]
Documentation/RelNotes/1.7.12.txt
Documentation/config.txt
Documentation/git-apply.txt
Documentation/git-daemon.txt
Documentation/git-p4.txt
Documentation/git-rebase.txt
Documentation/git-rev-parse.txt
Documentation/git.txt
Makefile
builtin/add.c
builtin/apply.c
builtin/blame.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/config.c
builtin/index-pack.c
builtin/log.c
builtin/pack-objects.c
builtin/reset.c
builtin/rev-parse.c
builtin/update-index.c
cache.h
commit.c
config.c
date.c
git-am.sh
git-filter-branch.sh
git-p4.py
git-rebase--interactive.sh
git-rebase.sh
git-submodule.sh
git-svn.perl
gitweb/gitweb.perl
read-cache.c
rerere.c
revision.c
revision.h
setup.c
sha1_name.c
t/t1512-rev-parse-disambiguation.sh [new file with mode: 0755]
t/t3006-ls-files-long.sh [new file with mode: 0755]
t/t3404-rebase-interactive.sh
t/t3412-rebase-root.sh
t/t4012-diff-binary.sh
t/t4108-apply-threeway.sh [new file with mode: 0755]
t/t4117-apply-reject.sh
t/t7003-filter-branch.sh
t/t7406-submodule-update.sh
t/t7501-commit.sh
t/t9814-git-p4-rename.sh
t/test-lib-functions.sh
tree.c
unpack-trees.c
wt-status.c
wt-status.h
index c188d0b461f170b4a59a3a97d34861d29b299774..bb5c91e712349686ccd008d9ccfd5fa8c2320834 100644 (file)
@@ -2,6 +2,9 @@
 /GIT-CFLAGS
 /GIT-LDFLAGS
 /GIT-GUI-VARS
+/GIT-PREFIX
+/GIT-SCRIPT-DEFINES
+/GIT-USER-AGENT
 /GIT-VERSION-FILE
 /bin-wrappers/
 /git
diff --git a/Documentation/RelNotes/1.7.11.3.txt b/Documentation/RelNotes/1.7.11.3.txt
new file mode 100644 (file)
index 0000000..64494f8
--- /dev/null
@@ -0,0 +1,53 @@
+Git v1.7.11.3 Release Notes
+===========================
+
+Fixes since v1.7.11.3
+---------------------
+
+ * The error message from "git push $there :bogo" (and its equivalent
+   "git push $there --delete bogo") mentioned that we tried and failed
+   to guess what ref is being deleted based on the LHS of the refspec,
+   which we don't.
+
+ * A handful of files and directories we create had tighter than
+   necessary permission bits when the user wanted to have group
+   writability (e.g. by setting "umask 002").
+
+ * "commit --amend" used to refuse amending a commit with an empty log
+   message, with or without "--allow-empty-message".
+
+ * "git commit --amend --only --" was meant to allow "Clever" people to
+   rewrite the commit message without making any change even when they
+   have already changes for the next commit added to their index, but
+   it never worked as advertised since it was introduced in 1.3.0 era.
+
+ * Even though the index can record pathnames longer than 1<<12 bytes,
+   in some places we were not comparing them in full, potentially
+   replacing index entries instead of adding.
+
+ * "git show"'s auto-walking behaviour was an unreliable and
+   unpredictable hack; it now behaves just like "git log" does when it
+   walks.
+
+ * "git diff", "git status" and anything that internally uses the
+   comparison machinery was utterly broken when the difference
+   involved a file with "-" as its name.  This was due to the way "git
+   diff --no-index" was incorrectly bolted on to the system, making
+   any comparison that involves a file "-" at the root level
+   incorrectly read from the standard input.
+
+ * We did not have test to make sure "git rebase" without extra options
+   filters out an empty commit in the original history.
+
+ * "git fast-export" produced an input stream for fast-import without
+   properly quoting pathnames when they contain SPs in them.
+
+ * "git checkout --detach", when you are still on an unborn branch,
+   should be forbidden, but it wasn't.
+
+ * Some implementations of Perl terminates "lines" with CRLF even when
+   the script is operating on just a sequence of bytes.  Make sure to
+   use "$PERL_PATH", the version of Perl the user told Git to use, in
+   our tests to avoid unnecessary breakages in tests.
+
+Also contains minor typofixes and documentation updates.
index 067c476e3c8cfda5ea1c5cde92aa5b536e10176a..eddef23c7139ad1504bd75fa3d7d4c40707f6ff6 100644 (file)
@@ -22,6 +22,15 @@ UI, Workflows & Features
    $HOME/.config/attributes and $HOME/.config/ignore respectively when
    these files exist.
 
+ * Logic to disambiguate abbreviated object names have been taught to
+   take advantage of object types that are expected in the context,
+   e.g. XXXXXX in the "git describe" output v1.2.3-gXXXXXX must be a
+   commit object, not a blob nor a tree.  This will help us prolong
+   the lifetime of abbreviated object names.
+
+ * "git apply" learned to wiggle the base version and perform three-way
+   merge when a patch does not exactly apply to the version you have.
+
  * Scripted Porcelain writers now have access to the credential API via
    the "git credential" plumbing command.
 
@@ -51,6 +60,9 @@ UI, Workflows & Features
    default location where the build procedure installs them locally;
    the variable can even point at a http:// URL.
 
+ * "git rebase [-i] --root $tip" can now be used to rewrite all the
+   history leading to "$tip" down to the root commit.
+
  * "git rebase -i" learned "-x <cmd>" to insert "exec <cmd>" after
    each commit in the resulting history.
 
@@ -71,6 +83,8 @@ Foreign Interface
  * "mediawiki" remote helper (in contrib/) learned to handle file
    attachments.
 
+ * "git p4" now uses "Jobs:" and "p4 move" when appropriate.
+
  * vcs-svn has been updated to clean-up compilation, lift 32-bit
    limitations, etc.
 
@@ -95,6 +109,10 @@ Performance, Internal Implementation, etc. (please report possible regressions)
    fnmatch() by comparing fixed leading substring literally when
    possible.
 
+ * "git log -n 1 -- rarely-touched-path" was spending unnecessary
+   cycles after showing the first change to find the next one, only to
+   discard it.
+
 
 Also contains minor documentation updates and code clean-ups.
 
@@ -106,33 +124,17 @@ Unless otherwise noted, all the fixes since v1.7.11 in the maintenance
 releases are contained in this release (see release notes to them for
 details).
 
- * "git show"'s auto-walking behaviour was an unreliable and
-   unpredictable hack; it now behaves just like "git log" does when it
-   walks.
-   (merge c5941f1 tr/maint-show-walk later to maint).
-
- * "git diff", "git status" and anything that internally uses the
-   comparison machinery was utterly broken when the difference
-   involved a file with "-" as its name.  This was due to the way "git
-   diff --no-index" was incorrectly bolted on to the system, making
-   any comparison that involves a file "-" at the root level
-   incorrectly read from the standard input.
-   (merge 4682d85 jc/refactor-diff-stdin later to maint).
-
- * We did not have test to make sure "git rebase" without extra options
-   filters out an empty commit in the original history.
-   (merge 2b5ba7b mz/empty-rebase-test later to maint).
-
- * "git fast-export" produced an input stream for fast-import without
-   properly quoting pathnames when they contain SPs in them.
-   (merge ff59f6d js/fast-export-paths-with-spaces later to maint).
-
- * "git checkout --detach", when you are still on an unborn branch,
-   should be forbidden, but it wasn't.
-   (merge 8ced1aa cw/no-detaching-an-unborn later to maint).
-
- * Some implementations of Perl terminates "lines" with CRLF even when
-   the script is operating on just a sequence of bytes.  Make sure to
-   use "$PERL_PATH", the version of Perl the user told Git to use, in
-   our tests to avoid unnecessary breakages in tests.
-   (merge ad78585 vr/use-our-perl-in-tests later to maint).
+ * When "git am" failed, old timers knew to check .git/rebase-apply/patch
+   to see what went wrong, but we never told the users about it.
+   (merge 14bf2d5 pg/maint-1.7.9-am-where-is-patch later to maint).
+
+ * When "git submodule add" clones a submodule repository, it can get
+   confused where to store the resulting submodule repository in the
+   superproject's .git/ directory when there is a symbolic link in the
+   path to the current directory.
+   (merge 6eafa6d jl/maint-1.7.10-recurse-submodules-with-symlink later to maint).
+
+ * In 1.7.9 era, we taught "git rebase" about the raw timestamp format
+   but we did not teach the same trick to "filter-branch", which rolled
+   a similar logic on its own.
+   (merge 44b85e89 jc/maint-filter-branch-epoch-date later to maint).
index 7bc0e5384871dc4c10df038cacee7c9d9370c4da..a95e5a4ac9a7e35623ada63d7919b193d7f86a6e 100644 (file)
@@ -177,6 +177,9 @@ advice.*::
                Advice shown when you used linkgit:git-checkout[1] to
                move to the detach HEAD state, to instruct how to create
                a local branch after the fact.
+       amWorkDir::
+               Advice that shows the location of the patch file when
+               linkgit:git-am[1] fails to apply it.
 --
 
 core.fileMode::
index afd2c9ae59e9145f377d5c702df27bf861c9b2f5..634b84e4b9a06b58f4c818a314c5cb2d5880e1bd 100644 (file)
@@ -9,7 +9,7 @@ git-apply - Apply a patch to files and/or to the index
 SYNOPSIS
 --------
 [verse]
-'git apply' [--stat] [--numstat] [--summary] [--check] [--index]
+'git apply' [--stat] [--numstat] [--summary] [--check] [--index] [--3way]
          [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
          [--allow-binary-replacement | --binary] [--reject] [-z]
          [-p<n>] [-C<n>] [--inaccurate-eof] [--recount] [--cached]
@@ -72,6 +72,15 @@ OPTIONS
        cached data, apply the patch, and store the result in the index
        without using the working tree. This implies `--index`.
 
+-3::
+--3way::
+       When the patch does not apply cleanly, fall back on 3-way merge if
+       the patch records the identity of blobs it is supposed to apply to,
+       and we have those blobs available locally, possibly leaving the
+       conflict markers in the files in the working tree for the user to
+       resolve.  This option implies the `--index` option, and is incompatible
+       with the `--reject` and the `--cached` options.
+
 --build-fake-ancestor=<file>::
        Newer 'git diff' output has embedded 'index information'
        for each blob to help identify the original version that
index 31b28fc29fc6e01e8505f2179b08082cf734377c..e8f757704c7fcf93e8e19f66c34557b8887ef29a 100644 (file)
@@ -204,7 +204,7 @@ receive-pack::
        can push anything into the repository, including removal
        of refs).  This is solely meant for a closed LAN setting
        where everybody is friendly.  This service can be
-       enabled by `daemon.receivepack` configuration item to
+       enabled by setting `daemon.receivepack` configuration item to
        `true`.
 
 EXAMPLES
index fe1f49bc6f26950987bc5520267663f4f335dab0..8228f33e3fdf0e6ec9c66dd3f55ad8094dfeb6aa 100644 (file)
@@ -255,7 +255,7 @@ These options can be used to modify 'git p4 submit' behavior.
        p4.  By default, this is the most recent p4 commit reachable
        from 'HEAD'.
 
--M[<n>]::
+-M::
        Detect renames.  See linkgit:git-diff[1].  Renames will be
        represented in p4 using explicit 'move' operations.  There
        is no corresponding option to detect copies, but there are
@@ -465,13 +465,15 @@ git-p4.useClientSpec::
 Submit variables
 ~~~~~~~~~~~~~~~~
 git-p4.detectRenames::
-       Detect renames.  See linkgit:git-diff[1].
+       Detect renames.  See linkgit:git-diff[1].  This can be true,
+       false, or a score as expected by 'git diff -M'.
 
 git-p4.detectCopies::
-       Detect copies.  See linkgit:git-diff[1].
+       Detect copies.  See linkgit:git-diff[1].  This can be true,
+       false, or a score as expected by 'git diff -C'.
 
 git-p4.detectCopiesHarder::
-       Detect copies harder.  See linkgit:git-diff[1].
+       Detect copies harder.  See linkgit:git-diff[1].  A boolean.
 
 git-p4.preserveUser::
        On submit, re-author changes to reflect the git author,
index 2cab7e21bad470805342b1c9b7cd604534475a35..fd535b06abf85ae166413ad0d24226240f8dd066 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
        [<upstream>] [<branch>]
-'git rebase' [-i | --interactive] [options] [--exec <cmd>] --onto <newbase>
+'git rebase' [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>]
        --root [<branch>]
 'git rebase' --continue | --skip | --abort
 
@@ -369,10 +369,11 @@ squash/fixup series.
 --root::
        Rebase all commits reachable from <branch>, instead of
        limiting them with an <upstream>.  This allows you to rebase
-       the root commit(s) on a branch.  Must be used with --onto, and
+       the root commit(s) on a branch.  When used with --onto, it
        will skip changes already contained in <newbase> (instead of
-       <upstream>).  When used together with --preserve-merges, 'all'
-       root commits will be rewritten to have <newbase> as parent
+       <upstream>) whereas without --onto it will operate on every change.
+       When used together with both --onto and --preserve-merges,
+       'all' root commits will be rewritten to have <newbase> as parent
        instead.
 
 --autosquash::
index 4cc3e9586fcfd79ddeb7f636141e1a878285b0b5..3c63561f02aca6f934c47a2897f8e50cf4ac5fe5 100644 (file)
@@ -101,6 +101,12 @@ OPTIONS
        The option core.warnAmbiguousRefs is used to select the strict
        abbreviation mode.
 
+--disambiguate=<prefix>::
+       Show every object whose name begins with the given prefix.
+       The <prefix> must be at least 4 hexadecimal digits long to
+       avoid listing each and every object in the repository by
+       mistake.
+
 --all::
        Show all refs found in `refs/`.
 
index 43f9a1bebd1096714c274a2ca6f23ffd33317d22..bf22ad527c7b19eecf7338a83ad12d40cf07b948 100644 (file)
@@ -44,9 +44,10 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.11.2/git.html[documentation for release 1.7.11.2]
+* link:v1.7.11.3/git.html[documentation for release 1.7.11.3]
 
 * release notes for
+  link:RelNotes/1.7.11.3.txt[1.7.11.3],
   link:RelNotes/1.7.11.2.txt[1.7.11.2],
   link:RelNotes/1.7.11.1.txt[1.7.11.1],
   link:RelNotes/1.7.11.txt[1.7.11].
index dc709029632edc77ac509f24b6352ee48ffa3dc0..285c660e1454ad3b591660907226fb5b89a3cf4e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -397,12 +397,9 @@ BUILTIN_OBJS =
 BUILT_INS =
 COMPAT_CFLAGS =
 COMPAT_OBJS =
-XDIFF_H =
 XDIFF_OBJS =
-VCSSVN_H =
 VCSSVN_OBJS =
-VCSSVN_TEST_OBJS =
-MISC_H =
+GENERATED_H =
 EXTRA_CPPFLAGS =
 LIB_H =
 LIB_OBJS =
@@ -562,46 +559,38 @@ LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
 VCSSVN_LIB=vcs-svn/lib.a
 
-XDIFF_H += xdiff/xinclude.h
-XDIFF_H += xdiff/xmacros.h
-XDIFF_H += xdiff/xdiff.h
-XDIFF_H += xdiff/xtypes.h
-XDIFF_H += xdiff/xutils.h
-XDIFF_H += xdiff/xprepare.h
-XDIFF_H += xdiff/xdiffi.h
-XDIFF_H += xdiff/xemit.h
-
-VCSSVN_H += vcs-svn/line_buffer.h
-VCSSVN_H += vcs-svn/sliding_window.h
-VCSSVN_H += vcs-svn/repo_tree.h
-VCSSVN_H += vcs-svn/fast_export.h
-VCSSVN_H += vcs-svn/svndiff.h
-VCSSVN_H += vcs-svn/svndump.h
-
-MISC_H += bisect.h
-MISC_H += branch.h
-MISC_H += bundle.h
-MISC_H += common-cmds.h
-MISC_H += fetch-pack.h
-MISC_H += reachable.h
-MISC_H += send-pack.h
-MISC_H += shortlog.h
-MISC_H += tar.h
-MISC_H += thread-utils.h
-MISC_H += url.h
-MISC_H += walker.h
-MISC_H += wt-status.h
+LIB_H += xdiff/xinclude.h
+LIB_H += xdiff/xmacros.h
+LIB_H += xdiff/xdiff.h
+LIB_H += xdiff/xtypes.h
+LIB_H += xdiff/xutils.h
+LIB_H += xdiff/xprepare.h
+LIB_H += xdiff/xdiffi.h
+LIB_H += xdiff/xemit.h
+
+LIB_H += vcs-svn/line_buffer.h
+LIB_H += vcs-svn/sliding_window.h
+LIB_H += vcs-svn/repo_tree.h
+LIB_H += vcs-svn/fast_export.h
+LIB_H += vcs-svn/svndiff.h
+LIB_H += vcs-svn/svndump.h
+
+GENERATED_H += common-cmds.h
 
 LIB_H += advice.h
 LIB_H += archive.h
 LIB_H += argv-array.h
 LIB_H += attr.h
+LIB_H += bisect.h
 LIB_H += blob.h
+LIB_H += branch.h
 LIB_H += builtin.h
 LIB_H += bulk-checkin.h
-LIB_H += cache.h
+LIB_H += bundle.h
 LIB_H += cache-tree.h
+LIB_H += cache.h
 LIB_H += color.h
+LIB_H += column.h
 LIB_H += commit.h
 LIB_H += compat/bswap.h
 LIB_H += compat/cygwin.h
@@ -623,6 +612,7 @@ LIB_H += diff.h
 LIB_H += diffcore.h
 LIB_H += dir.h
 LIB_H += exec_cmd.h
+LIB_H += fetch-pack.h
 LIB_H += fmt-merge-msg.h
 LIB_H += fsck.h
 LIB_H += gettext.h
@@ -632,6 +622,7 @@ LIB_H += graph.h
 LIB_H += grep.h
 LIB_H += hash.h
 LIB_H += help.h
+LIB_H += http.h
 LIB_H += kwset.h
 LIB_H += levenshtein.h
 LIB_H += list-objects.h
@@ -641,19 +632,20 @@ LIB_H += mailmap.h
 LIB_H += merge-file.h
 LIB_H += merge-recursive.h
 LIB_H += mergesort.h
-LIB_H += notes.h
 LIB_H += notes-cache.h
 LIB_H += notes-merge.h
+LIB_H += notes.h
 LIB_H += object.h
-LIB_H += pack.h
 LIB_H += pack-refs.h
 LIB_H += pack-revindex.h
+LIB_H += pack.h
 LIB_H += parse-options.h
 LIB_H += patch-ids.h
 LIB_H += pkt-line.h
 LIB_H += progress.h
 LIB_H += prompt.h
 LIB_H += quote.h
+LIB_H += reachable.h
 LIB_H += reflog-walk.h
 LIB_H += refs.h
 LIB_H += remote.h
@@ -661,9 +653,11 @@ LIB_H += rerere.h
 LIB_H += resolve-undo.h
 LIB_H += revision.h
 LIB_H += run-command.h
+LIB_H += send-pack.h
 LIB_H += sequencer.h
 LIB_H += sha1-array.h
 LIB_H += sha1-lookup.h
+LIB_H += shortlog.h
 LIB_H += sideband.h
 LIB_H += sigchain.h
 LIB_H += strbuf.h
@@ -671,14 +665,18 @@ LIB_H += streaming.h
 LIB_H += string-list.h
 LIB_H += submodule.h
 LIB_H += tag.h
+LIB_H += tar.h
 LIB_H += thread-utils.h
 LIB_H += transport.h
-LIB_H += tree.h
 LIB_H += tree-walk.h
+LIB_H += tree.h
 LIB_H += unpack-trees.h
+LIB_H += url.h
 LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += varint.h
+LIB_H += walker.h
+LIB_H += wt-status.h
 LIB_H += xdiff-interface.h
 LIB_H += xdiff/xdiff.h
 
@@ -1934,7 +1932,10 @@ endif
 GIT_USER_AGENT_SQ = $(subst ','\'',$(GIT_USER_AGENT))
 GIT_USER_AGENT_CQ = "$(subst ",\",$(subst \,\\,$(GIT_USER_AGENT)))"
 GIT_USER_AGENT_CQ_SQ = $(subst ','\'',$(GIT_USER_AGENT_CQ))
-BASIC_CFLAGS += -DGIT_USER_AGENT='$(GIT_USER_AGENT_CQ_SQ)'
+GIT-USER-AGENT: FORCE
+       @if test x'$(GIT_USER_AGENT_SQ)' != x"`cat GIT-USER-AGENT 2>/dev/null`"; then \
+               echo '$(GIT_USER_AGENT_SQ)' >GIT-USER-AGENT; \
+       fi
 
 ifdef DEFAULT_HELP_FORMAT
 BASIC_CFLAGS += -DDEFAULT_HELP_FORMAT='"$(DEFAULT_HELP_FORMAT)"'
@@ -1986,7 +1987,40 @@ shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
 strip: $(PROGRAMS) git$X
        $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
 
-git.o: common-cmds.h
+### Target-specific flags and dependencies
+
+# The generic compilation pattern rule and automatically
+# computed header dependencies (falling back to a dependency on
+# LIB_H) are enough to describe how most targets should be built,
+# but some targets are special enough to need something a little
+# different.
+#
+# - When a source file "foo.c" #includes a generated header file,
+#   we need to list that dependency for the "foo.o" target.
+#
+#   We also list it from other targets that are built from foo.c
+#   like "foo.sp" and "foo.s", even though that is easy to forget
+#   to do because the generated header is already present around
+#   after a regular build attempt.
+#
+# - Some code depends on configuration kept in makefile
+#   variables. The target-specific variable EXTRA_CPPFLAGS can
+#   be used to convey that information to the C preprocessor
+#   using -D options.
+#
+#   The "foo.o" target should have a corresponding dependency on
+#   a file that changes when the value of the makefile variable
+#   changes.  For example, targets making use of the
+#   $(GIT_VERSION) variable depend on GIT-VERSION-FILE.
+#
+#   Technically the ".sp" and ".s" targets do not need this
+#   dependency because they are force-built, but they get the
+#   same dependency for consistency. This way, you do not have to
+#   know how each target is implemented. And it means the
+#   dependencies here will not need to change if the force-build
+#   details change some day.
+
+git.sp git.s git.o: GIT-PREFIX
 git.sp git.s git.o: EXTRA_CPPFLAGS = \
        '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
        '-DGIT_MAN_PATH="$(mandir_SQ)"' \
@@ -1996,16 +2030,18 @@ git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
-help.sp help.o: common-cmds.h
+help.sp help.s help.o: common-cmds.h
 
-builtin/help.sp builtin/help.o: common-cmds.h
+builtin/help.sp builtin/help.s builtin/help.o: common-cmds.h GIT-PREFIX
 builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
        '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
        '-DGIT_MAN_PATH="$(mandir_SQ)"' \
        '-DGIT_INFO_PATH="$(infodir_SQ)"'
 
+version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
 version.sp version.s version.o: EXTRA_CPPFLAGS = \
-       '-DGIT_VERSION="$(GIT_VERSION)"'
+       '-DGIT_VERSION="$(GIT_VERSION)"' \
+       '-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)'
 
 $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
@@ -2018,36 +2054,47 @@ common-cmds.h: ./generate-cmdlist.sh command-list.txt
 common-cmds.h: $(wildcard Documentation/git-*.txt)
        $(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@
 
+SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
+       $(localedir_SQ):$(NO_CURL):$(USE_GETTEXT_SCHEME):$(SANE_TOOL_PATH_SQ):\
+       $(gitwebdir_SQ):$(PERL_PATH_SQ)
 define cmd_munge_script
 $(RM) $@ $@+ && \
 sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
     -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
     -e 's|@@DIFF@@|$(DIFF_SQ)|' \
-    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-    -e 's|@@GIT_USER_AGENT@@|$(GIT_USER_AGENT_SQ)|g' \
     -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' \
     -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
     -e 's/@@USE_GETTEXT_SCHEME@@/$(USE_GETTEXT_SCHEME)/g' \
     -e $(BROKEN_PATH_FIX) \
+    -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
+    -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
     $@.sh >$@+
 endef
 
-$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
+GIT-SCRIPT-DEFINES: FORCE
+       @FLAGS='$(SCRIPT_DEFINES)'; \
+           if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \
+               echo 1>&2 "    * new script parameters"; \
+               echo "$$FLAGS" >$@; \
+            fi
+
+
+$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh GIT-SCRIPT-DEFINES
        $(QUIET_GEN)$(cmd_munge_script) && \
        chmod +x $@+ && \
        mv $@+ $@
 
-$(SCRIPT_LIB) : % : %.sh
+$(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEFINES
        $(QUIET_GEN)$(cmd_munge_script) && \
        mv $@+ $@
 
 ifndef NO_PERL
 $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
 
-perl/perl.mak: GIT-CFLAGS perl/Makefile perl/Makefile.PL
+perl/perl.mak: GIT-CFLAGS GIT-PREFIX perl/Makefile perl/Makefile.PL
        $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
 
-$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
+$(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
        INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C perl -s --no-print-directory instlibdir` && \
        sed -e '1{' \
@@ -2067,14 +2114,8 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
 gitweb:
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
 
-git-instaweb: git-instaweb.sh gitweb
-       $(QUIET_GEN)$(RM) $@ $@+ && \
-       sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
-           -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-           -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-           -e 's|@@GITWEBDIR@@|$(gitwebdir_SQ)|g' \
-           -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-           $@.sh > $@+ && \
+git-instaweb: git-instaweb.sh gitweb GIT-SCRIPT-DEFINES
+       $(QUIET_GEN)$(cmd_munge_script) && \
        chmod +x $@+ && \
        mv $@+ $@
 else # NO_PERL
@@ -2088,7 +2129,7 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git-instaweb: % : unimplemented.sh
 endif # NO_PERL
 
 ifndef NO_PYTHON
-$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS
+$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX
 $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
        $(QUIET_GEN)$(RM) $@ $@+ && \
        INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
@@ -2110,26 +2151,13 @@ $(patsubst %.py,%,$(SCRIPT_PYTHON)): % : unimplemented.sh
        mv $@+ $@
 endif # NO_PYTHON
 
-configure: configure.ac
+configure: configure.ac GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $<+ && \
        sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            $< > $<+ && \
        autoconf -o $@ $<+ && \
        $(RM) $<+
 
-# These can record GIT_VERSION
-version.o git.spec \
-       $(patsubst %.sh,%,$(SCRIPT_SH)) \
-       $(patsubst %.perl,%,$(SCRIPT_PERL)) \
-       : GIT-VERSION-FILE
-
-TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
-GIT_OBJS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
-       git.o
-ifndef NO_CURL
-       GIT_OBJS += http.o http-walker.o remote-curl.o
-endif
-
 XDIFF_OBJS += xdiff/xdiffi.o
 XDIFF_OBJS += xdiff/xprepare.o
 XDIFF_OBJS += xdiff/xutils.o
@@ -2145,9 +2173,14 @@ VCSSVN_OBJS += vcs-svn/fast_export.o
 VCSSVN_OBJS += vcs-svn/svndiff.o
 VCSSVN_OBJS += vcs-svn/svndump.o
 
-VCSSVN_TEST_OBJS += test-line-buffer.o
-
-OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS)
+TEST_OBJS := $(patsubst test-%$X,test-%.o,$(TEST_PROGRAMS))
+OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
+       $(XDIFF_OBJS) \
+       $(VCSSVN_OBJS) \
+       git.o
+ifndef NO_CURL
+       OBJECTS += http.o http-walker.o remote-curl.o
+endif
 
 dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)
 dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))
@@ -2246,45 +2279,29 @@ else
 # Dependencies on automatically generated headers such as common-cmds.h
 # should _not_ be included here, since they are necessary even when
 # building an object for the first time.
-#
-# XXX. Please check occasionally that these include all dependencies
-# gcc detects!
-
-$(GIT_OBJS): $(LIB_H)
-builtin/branch.o builtin/checkout.o builtin/clone.o builtin/reset.o branch.o transport.o: branch.h
-builtin/bundle.o bundle.o transport.o: bundle.h
-builtin/bisect--helper.o builtin/rev-list.o bisect.o: bisect.h
-builtin/clone.o builtin/fetch-pack.o transport.o: fetch-pack.h
-builtin/index-pack.o builtin/grep.o builtin/pack-objects.o transport-helper.o thread-utils.o: thread-utils.h
-builtin/send-pack.o transport.o: send-pack.h
-builtin/log.o builtin/shortlog.o: shortlog.h
-builtin/prune.o builtin/reflog.o reachable.o: reachable.h
-builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
-builtin/tar-tree.o archive-tar.o: tar.h
-connect.o transport.o url.o http-backend.o: url.h
-builtin/branch.o builtin/commit.o builtin/tag.o column.o help.o pager.o: column.h
-http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
-http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h
-
-xdiff-interface.o $(XDIFF_OBJS): $(XDIFF_H)
 
-$(VCSSVN_OBJS) $(VCSSVN_TEST_OBJS): $(LIB_H) $(VCSSVN_H)
+$(OBJECTS): $(LIB_H)
 endif
 
+exec_cmd.sp exec_cmd.s exec_cmd.o: GIT-PREFIX
 exec_cmd.sp exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
        '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
        '-DBINDIR="$(bindir_relative_SQ)"' \
        '-DPREFIX="$(prefix_SQ)"'
 
+builtin/init-db.sp builtin/init-db.s builtin/init-db.o: GIT-PREFIX
 builtin/init-db.sp builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
        -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
 
+config.sp config.s config.o: GIT-PREFIX
 config.sp config.s config.o: EXTRA_CPPFLAGS = \
        -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
 
+attr.sp attr.s attr.o: GIT-PREFIX
 attr.sp attr.s attr.o: EXTRA_CPPFLAGS = \
        -DETC_GITATTRIBUTES='"$(ETC_GITATTRIBUTES_SQ)"'
 
+gettext.sp gettext.s gettext.o: GIT-PREFIX
 gettext.sp gettext.s gettext.o: EXTRA_CPPFLAGS = \
        -DGIT_LOCALE_PATH='"$(localedir_SQ)"'
 
@@ -2361,7 +2378,7 @@ XGETTEXT_FLAGS_C = $(XGETTEXT_FLAGS) --language=C \
        --keyword=_ --keyword=N_ --keyword="Q_:1,2"
 XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) --language=Shell
 XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
-LOCALIZED_C := $(C_OBJ:o=c) $(LIB_H) $(XDIFF_H) $(VCSSVN_H) $(MISC_H)
+LOCALIZED_C := $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 LOCALIZED_SH := $(SCRIPT_SH)
 LOCALIZED_PERL := $(SCRIPT_PERL)
 
@@ -2408,14 +2425,22 @@ cscope:
        $(FIND_SOURCE_FILES) | xargs cscope -b
 
 ### Detect prefix changes
-TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)):\
-             $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ):\
-             $(localedir_SQ):$(USE_GETTEXT_SCHEME)
+TRACK_PREFIX = $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ):\
+               $(localedir_SQ)
+
+GIT-PREFIX: FORCE
+       @FLAGS='$(TRACK_PREFIX)'; \
+       if test x"$$FLAGS" != x"`cat GIT-PREFIX 2>/dev/null`" ; then \
+               echo 1>&2 "    * new prefix flags"; \
+               echo "$$FLAGS" >GIT-PREFIX; \
+       fi
+
+TRACK_CFLAGS = $(CC):$(subst ','\'',$(ALL_CFLAGS)):$(USE_GETTEXT_SCHEME)
 
 GIT-CFLAGS: FORCE
        @FLAGS='$(TRACK_CFLAGS)'; \
            if test x"$$FLAGS" != x"`cat GIT-CFLAGS 2>/dev/null`" ; then \
-               echo 1>&2 "    * new build flags or prefix"; \
+               echo 1>&2 "    * new build flags"; \
                echo "$$FLAGS" >GIT-CFLAGS; \
             fi
 
@@ -2667,7 +2692,7 @@ quick-install-html:
 
 ### Maintainer's dist rules
 
-git.spec: git.spec.in
+git.spec: git.spec.in GIT-VERSION-FILE
        sed -e 's/@@VERSION@@/$(GIT_VERSION)/g' < $< > $@+
        mv $@+ $@
 
@@ -2751,6 +2776,7 @@ ifndef NO_TCLTK
        $(MAKE) -C git-gui clean
 endif
        $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
+       $(RM) GIT-USER-AGENT GIT-PREFIX GIT-SCRIPT-DEFINES
 
 .PHONY: all install profile-clean clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
index 87446cf92a686ed69717c85a6b38302f59351460..89dce56a240d69ccb224590bf134c9656d8a0522 100644 (file)
@@ -281,7 +281,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
        argc = setup_revisions(argc, argv, &rev, NULL);
        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
        DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
-       out = open(file, O_CREAT | O_WRONLY, 0644);
+       out = open(file, O_CREAT | O_WRONLY, 0666);
        if (out < 0)
                die (_("Could not open '%s' for writing."), file);
        rev.diffopt.file = xfdopen(out, "w");
index b4428ea34f53d94e3733796777866e73531f06b5..d453c833782c6aae4dd04fb9f9f68c3a8211d3d5 100644 (file)
@@ -16,6 +16,9 @@
 #include "dir.h"
 #include "diff.h"
 #include "parse-options.h"
+#include "xdiff-interface.h"
+#include "ll-merge.h"
+#include "rerere.h"
 
 /*
  *  --check turns on checking that the working tree matches the
@@ -46,6 +49,7 @@ static int apply_with_reject;
 static int apply_verbosely;
 static int allow_overlap;
 static int no_add;
+static int threeway;
 static const char *fake_ancestor;
 static int line_termination = '\n';
 static unsigned int p_context = UINT_MAX;
@@ -193,12 +197,17 @@ struct patch {
        unsigned int is_copy:1;
        unsigned int is_rename:1;
        unsigned int recount:1;
+       unsigned int conflicted_threeway:1;
+       unsigned int direct_to_threeway:1;
        struct fragment *fragments;
        char *result;
        size_t resultsize;
        char old_sha1_prefix[41];
        char new_sha1_prefix[41];
        struct patch *next;
+
+       /* three-way fallback result */
+       unsigned char threeway_stage[3][20];
 };
 
 static void free_fragment_list(struct fragment *list)
@@ -371,8 +380,8 @@ static void prepare_image(struct image *image, char *buf, size_t len,
 static void clear_image(struct image *image)
 {
        free(image->buf);
-       image->buf = NULL;
-       image->len = 0;
+       free(image->line_allocated);
+       memset(image, 0, sizeof(*image));
 }
 
 /* fmt must contain _one_ %s and no other substitution */
@@ -2937,20 +2946,17 @@ static int apply_fragments(struct image *img, struct patch *patch)
        return 0;
 }
 
-static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
+static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsigned mode)
 {
-       if (!ce)
-               return 0;
-
-       if (S_ISGITLINK(ce->ce_mode)) {
+       if (S_ISGITLINK(mode)) {
                strbuf_grow(buf, 100);
-               strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(ce->sha1));
+               strbuf_addf(buf, "Subproject commit %s\n", sha1_to_hex(sha1));
        } else {
                enum object_type type;
                unsigned long sz;
                char *result;
 
-               result = read_sha1_file(ce->sha1, &type, &sz);
+               result = read_sha1_file(sha1, &type, &sz);
                if (!result)
                        return -1;
                /* XXX read_sha1_file NUL-terminates */
@@ -2959,6 +2965,13 @@ static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
        return 0;
 }
 
+static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
+{
+       if (!ce)
+               return 0;
+       return read_blob_object(buf, ce->sha1, ce->ce_mode);
+}
+
 static struct patch *in_fn_table(const char *name)
 {
        struct string_list_item *item;
@@ -2977,9 +2990,15 @@ static struct patch *in_fn_table(const char *name)
  * item->util in the filename table records the status of the path.
  * Usually it points at a patch (whose result records the contents
  * of it after applying it), but it could be PATH_WAS_DELETED for a
- * path that a previously applied patch has already removed.
+ * path that a previously applied patch has already removed, or
+ * PATH_TO_BE_DELETED for a path that a later patch would remove.
+ *
+ * The latter is needed to deal with a case where two paths A and B
+ * are swapped by first renaming A to B and then renaming B to A;
+ * moving A to B should not be prevented due to presense of B as we
+ * will remove it in a later patch.
  */
- #define PATH_TO_BE_DELETED ((struct patch *) -2)
+#define PATH_TO_BE_DELETED ((struct patch *) -2)
 #define PATH_WAS_DELETED ((struct patch *) -1)
 
 static int to_be_deleted(struct patch *patch)
@@ -3031,127 +3050,324 @@ static void prepare_fn_table(struct patch *patch)
        }
 }
 
-static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
+static int checkout_target(struct cache_entry *ce, struct stat *st)
+{
+       struct checkout costate;
+
+       memset(&costate, 0, sizeof(costate));
+       costate.base_dir = "";
+       costate.refresh_cache = 1;
+       if (checkout_entry(ce, &costate, NULL) || lstat(ce->name, st))
+               return error(_("cannot checkout %s"), ce->name);
+       return 0;
+}
+
+static struct patch *previous_patch(struct patch *patch, int *gone)
+{
+       struct patch *previous;
+
+       *gone = 0;
+       if (patch->is_copy || patch->is_rename)
+               return NULL; /* "git" patches do not depend on the order */
+
+       previous = in_fn_table(patch->old_name);
+       if (!previous)
+               return NULL;
+
+       if (to_be_deleted(previous))
+               return NULL; /* the deletion hasn't happened yet */
+
+       if (was_deleted(previous))
+               *gone = 1;
+
+       return previous;
+}
+
+static int verify_index_match(struct cache_entry *ce, struct stat *st)
+{
+       if (S_ISGITLINK(ce->ce_mode)) {
+               if (!S_ISDIR(st->st_mode))
+                       return -1;
+               return 0;
+       }
+       return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
+}
+
+#define SUBMODULE_PATCH_WITHOUT_INDEX 1
+
+static int load_patch_target(struct strbuf *buf,
+                            struct cache_entry *ce,
+                            struct stat *st,
+                            const char *name,
+                            unsigned expected_mode)
+{
+       if (cached) {
+               if (read_file_or_gitlink(ce, buf))
+                       return error(_("read of %s failed"), name);
+       } else if (name) {
+               if (S_ISGITLINK(expected_mode)) {
+                       if (ce)
+                               return read_file_or_gitlink(ce, buf);
+                       else
+                               return SUBMODULE_PATCH_WITHOUT_INDEX;
+               } else {
+                       if (read_old_data(st, name, buf))
+                               return error(_("read of %s failed"), name);
+               }
+       }
+       return 0;
+}
+
+/*
+ * We are about to apply "patch"; populate the "image" with the
+ * current version we have, from the working tree or from the index,
+ * depending on the situation e.g. --cached/--index.  If we are
+ * applying a non-git patch that incrementally updates the tree,
+ * we read from the result of a previous diff.
+ */
+static int load_preimage(struct image *image,
+                        struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
        struct strbuf buf = STRBUF_INIT;
-       struct image image;
        size_t len;
        char *img;
-       struct patch *tpatch;
+       struct patch *previous;
+       int status;
 
-       if (!(patch->is_copy || patch->is_rename) &&
-           (tpatch = in_fn_table(patch->old_name)) != NULL && !to_be_deleted(tpatch)) {
-               if (was_deleted(tpatch)) {
-                       return error(_("patch %s has been renamed/deleted"),
-                               patch->old_name);
-               }
+       previous = previous_patch(patch, &status);
+       if (status)
+               return error(_("path %s has been renamed/deleted"),
+                            patch->old_name);
+       if (previous) {
                /* We have a patched copy in memory; use that. */
-               strbuf_add(&buf, tpatch->result, tpatch->resultsize);
-       } else if (cached) {
-               if (read_file_or_gitlink(ce, &buf))
+               strbuf_add(&buf, previous->result, previous->resultsize);
+       } else {
+               status = load_patch_target(&buf, ce, st,
+                                          patch->old_name, patch->old_mode);
+               if (status < 0)
+                       return status;
+               else if (status == SUBMODULE_PATCH_WITHOUT_INDEX) {
+                       /*
+                        * There is no way to apply subproject
+                        * patch without looking at the index.
+                        * NEEDSWORK: shouldn't this be flagged
+                        * as an error???
+                        */
+                       free_fragment_list(patch->fragments);
+                       patch->fragments = NULL;
+               } else if (status) {
                        return error(_("read of %s failed"), patch->old_name);
-       } else if (patch->old_name) {
-               if (S_ISGITLINK(patch->old_mode)) {
-                       if (ce) {
-                               read_file_or_gitlink(ce, &buf);
-                       } else {
-                               /*
-                                * There is no way to apply subproject
-                                * patch without looking at the index.
-                                * NEEDSWORK: shouldn't this be flagged
-                                * as an error???
-                                */
-                               free_fragment_list(patch->fragments);
-                               patch->fragments = NULL;
-                       }
-               } else {
-                       if (read_old_data(st, patch->old_name, &buf))
-                               return error(_("read of %s failed"), patch->old_name);
                }
        }
 
        img = strbuf_detach(&buf, &len);
-       prepare_image(&image, img, len, !patch->is_binary);
+       prepare_image(image, img, len, !patch->is_binary);
+       return 0;
+}
 
-       if (apply_fragments(&image, patch) < 0)
-               return -1; /* note with --reject this succeeds. */
-       patch->result = image.buf;
-       patch->resultsize = image.len;
-       add_to_fn_table(patch);
-       free(image.line_allocated);
+static int three_way_merge(struct image *image,
+                          char *path,
+                          const unsigned char *base,
+                          const unsigned char *ours,
+                          const unsigned char *theirs)
+{
+       mmfile_t base_file, our_file, their_file;
+       mmbuffer_t result = { NULL };
+       int status;
 
-       if (0 < patch->is_delete && patch->resultsize)
-               return error(_("removal patch leaves file contents"));
+       read_mmblob(&base_file, base);
+       read_mmblob(&our_file, ours);
+       read_mmblob(&their_file, theirs);
+       status = ll_merge(&result, path,
+                         &base_file, "base",
+                         &our_file, "ours",
+                         &their_file, "theirs", NULL);
+       free(base_file.ptr);
+       free(our_file.ptr);
+       free(their_file.ptr);
+       if (status < 0 || !result.ptr) {
+               free(result.ptr);
+               return -1;
+       }
+       clear_image(image);
+       image->buf = result.ptr;
+       image->len = result.size;
 
+       return status;
+}
+
+/*
+ * When directly falling back to add/add three-way merge, we read from
+ * the current contents of the new_name.  In no cases other than that
+ * this function will be called.
+ */
+static int load_current(struct image *image, struct patch *patch)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int status, pos;
+       size_t len;
+       char *img;
+       struct stat st;
+       struct cache_entry *ce;
+       char *name = patch->new_name;
+       unsigned mode = patch->new_mode;
+
+       if (!patch->is_new)
+               die("BUG: patch to %s is not a creation", patch->old_name);
+
+       pos = cache_name_pos(name, strlen(name));
+       if (pos < 0)
+               return error(_("%s: does not exist in index"), name);
+       ce = active_cache[pos];
+       if (lstat(name, &st)) {
+               if (errno != ENOENT)
+                       return error(_("%s: %s"), name, strerror(errno));
+               if (checkout_target(ce, &st))
+                       return -1;
+       }
+       if (verify_index_match(ce, &st))
+               return error(_("%s: does not match index"), name);
+
+       status = load_patch_target(&buf, ce, &st, name, mode);
+       if (status < 0)
+               return status;
+       else if (status)
+               return -1;
+       img = strbuf_detach(&buf, &len);
+       prepare_image(image, img, len, !patch->is_binary);
        return 0;
 }
 
-static int check_to_create_blob(const char *new_name, int ok_if_exists)
+static int try_threeway(struct image *image, struct patch *patch,
+                       struct stat *st, struct cache_entry *ce)
 {
-       struct stat nst;
-       if (!lstat(new_name, &nst)) {
-               if (S_ISDIR(nst.st_mode) || ok_if_exists)
-                       return 0;
-               /*
-                * A leading component of new_name might be a symlink
-                * that is going to be removed with this patch, but
-                * still pointing at somewhere that has the path.
-                * In such a case, path "new_name" does not exist as
-                * far as git is concerned.
-                */
-               if (has_symlink_leading_path(new_name, strlen(new_name)))
-                       return 0;
+       unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
+       struct strbuf buf = STRBUF_INIT;
+       size_t len;
+       int status;
+       char *img;
+       struct image tmp_image;
+
+       /* No point falling back to 3-way merge in these cases */
+       if (patch->is_delete ||
+           S_ISGITLINK(patch->old_mode) || S_ISGITLINK(patch->new_mode))
+               return -1;
 
-               return error(_("%s: already exists in working directory"), new_name);
+       /* Preimage the patch was prepared for */
+       if (patch->is_new)
+               write_sha1_file("", 0, blob_type, pre_sha1);
+       else if (get_sha1(patch->old_sha1_prefix, pre_sha1) ||
+                read_blob_object(&buf, pre_sha1, patch->old_mode))
+               return error("repository lacks the necessary blob to fall back on 3-way merge.");
+
+       fprintf(stderr, "Falling back to three-way merge...\n");
+
+       img = strbuf_detach(&buf, &len);
+       prepare_image(&tmp_image, img, len, 1);
+       /* Apply the patch to get the post image */
+       if (apply_fragments(&tmp_image, patch) < 0) {
+               clear_image(&tmp_image);
+               return -1;
+       }
+       /* post_sha1[] is theirs */
+       write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_sha1);
+       clear_image(&tmp_image);
+
+       /* our_sha1[] is ours */
+       if (patch->is_new) {
+               if (load_current(&tmp_image, patch))
+                       return error("cannot read the current contents of '%s'",
+                                    patch->new_name);
+       } else {
+               if (load_preimage(&tmp_image, patch, st, ce))
+                       return error("cannot read the current contents of '%s'",
+                                    patch->old_name);
+       }
+       write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_sha1);
+       clear_image(&tmp_image);
+
+       /* in-core three-way merge between post and our using pre as base */
+       status = three_way_merge(image, patch->new_name,
+                                pre_sha1, our_sha1, post_sha1);
+       if (status < 0) {
+               fprintf(stderr, "Failed to fall back on three-way merge...\n");
+               return status;
+       }
+
+       if (status) {
+               patch->conflicted_threeway = 1;
+               if (patch->is_new)
+                       hashclr(patch->threeway_stage[0]);
+               else
+                       hashcpy(patch->threeway_stage[0], pre_sha1);
+               hashcpy(patch->threeway_stage[1], our_sha1);
+               hashcpy(patch->threeway_stage[2], post_sha1);
+               fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
+       } else {
+               fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
        }
-       else if ((errno != ENOENT) && (errno != ENOTDIR))
-               return error("%s: %s", new_name, strerror(errno));
        return 0;
 }
 
-static int verify_index_match(struct cache_entry *ce, struct stat *st)
+static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
 {
-       if (S_ISGITLINK(ce->ce_mode)) {
-               if (!S_ISDIR(st->st_mode))
+       struct image image;
+
+       if (load_preimage(&image, patch, st, ce) < 0)
+               return -1;
+
+       if (patch->direct_to_threeway ||
+           apply_fragments(&image, patch) < 0) {
+               /* Note: with --reject, apply_fragments() returns 0 */
+               if (!threeway || try_threeway(&image, patch, st, ce) < 0)
                        return -1;
-               return 0;
        }
-       return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
+       patch->result = image.buf;
+       patch->resultsize = image.len;
+       add_to_fn_table(patch);
+       free(image.line_allocated);
+
+       if (0 < patch->is_delete && patch->resultsize)
+               return error(_("removal patch leaves file contents"));
+
+       return 0;
 }
 
+/*
+ * If "patch" that we are looking at modifies or deletes what we have,
+ * we would want it not to lose any local modification we have, either
+ * in the working tree or in the index.
+ *
+ * This also decides if a non-git patch is a creation patch or a
+ * modification to an existing empty file.  We do not check the state
+ * of the current tree for a creation patch in this function; the caller
+ * check_patch() separately makes sure (and errors out otherwise) that
+ * the path the patch creates does not exist in the current tree.
+ */
 static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
 {
        const char *old_name = patch->old_name;
-       struct patch *tpatch = NULL;
-       int stat_ret = 0;
+       struct patch *previous = NULL;
+       int stat_ret = 0, status;
        unsigned st_mode = 0;
 
-       /*
-        * Make sure that we do not have local modifications from the
-        * index when we are looking at the index.  Also make sure
-        * we have the preimage file to be patched in the work tree,
-        * unless --cached, which tells git to apply only in the index.
-        */
        if (!old_name)
                return 0;
 
        assert(patch->is_new <= 0);
+       previous = previous_patch(patch, &status);
 
-       if (!(patch->is_copy || patch->is_rename) &&
-           (tpatch = in_fn_table(old_name)) != NULL && !to_be_deleted(tpatch)) {
-               if (was_deleted(tpatch))
-                       return error(_("%s: has been deleted/renamed"), old_name);
-               st_mode = tpatch->new_mode;
+       if (status)
+               return error(_("path %s has been renamed/deleted"), old_name);
+       if (previous) {
+               st_mode = previous->new_mode;
        } else if (!cached) {
                stat_ret = lstat(old_name, st);
                if (stat_ret && errno != ENOENT)
                        return error(_("%s: %s"), old_name, strerror(errno));
        }
 
-       if (to_be_deleted(tpatch))
-               tpatch = NULL;
-
-       if (check_index && !tpatch) {
+       if (check_index && !previous) {
                int pos = cache_name_pos(old_name, strlen(old_name));
                if (pos < 0) {
                        if (patch->is_new < 0)
@@ -3160,13 +3376,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                }
                *ce = active_cache[pos];
                if (stat_ret < 0) {
-                       struct checkout costate;
-                       /* checkout */
-                       memset(&costate, 0, sizeof(costate));
-                       costate.base_dir = "";
-                       costate.refresh_cache = 1;
-                       if (checkout_entry(*ce, &costate, NULL) ||
-                           lstat(old_name, st))
+                       if (checkout_target(*ce, st))
                                return -1;
                }
                if (!cached && verify_index_match(*ce, st))
@@ -3179,7 +3389,7 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
                return error(_("%s: %s"), old_name, strerror(errno));
        }
 
-       if (!cached && !tpatch)
+       if (!cached && !previous)
                st_mode = ce_mode_from_stat(*ce, st->st_mode);
 
        if (patch->is_new < 0)
@@ -3203,6 +3413,41 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
        return 0;
 }
 
+
+#define EXISTS_IN_INDEX 1
+#define EXISTS_IN_WORKTREE 2
+
+static int check_to_create(const char *new_name, int ok_if_exists)
+{
+       struct stat nst;
+
+       if (check_index &&
+           cache_name_pos(new_name, strlen(new_name)) >= 0 &&
+           !ok_if_exists)
+               return EXISTS_IN_INDEX;
+       if (cached)
+               return 0;
+
+       if (!lstat(new_name, &nst)) {
+               if (S_ISDIR(nst.st_mode) || ok_if_exists)
+                       return 0;
+               /*
+                * A leading component of new_name might be a symlink
+                * that is going to be removed with this patch, but
+                * still pointing at somewhere that has the path.
+                * In such a case, path "new_name" does not exist as
+                * far as git is concerned.
+                */
+               if (has_symlink_leading_path(new_name, strlen(new_name)))
+                       return 0;
+
+               return EXISTS_IN_WORKTREE;
+       } else if ((errno != ENOENT) && (errno != ENOTDIR)) {
+               return error("%s: %s", new_name, strerror(errno));
+       }
+       return 0;
+}
+
 /*
  * Check and apply the patch in-core; leave the result in patch->result
  * for the caller to write it out to the final destination.
@@ -3225,31 +3470,45 @@ static int check_patch(struct patch *patch)
                return status;
        old_name = patch->old_name;
 
+       /*
+        * A type-change diff is always split into a patch to delete
+        * old, immediately followed by a patch to create new (see
+        * diff.c::run_diff()); in such a case it is Ok that the entry
+        * to be deleted by the previous patch is still in the working
+        * tree and in the index.
+        *
+        * A patch to swap-rename between A and B would first rename A
+        * to B and then rename B to A.  While applying the first one,
+        * the presense of B should not stop A from getting renamed to
+        * B; ask to_be_deleted() about the later rename.  Removal of
+        * B and rename from A to B is handled the same way by asking
+        * was_deleted().
+        */
        if ((tpatch = in_fn_table(new_name)) &&
-                       (was_deleted(tpatch) || to_be_deleted(tpatch)))
-               /*
-                * A type-change diff is always split into a patch to
-                * delete old, immediately followed by a patch to
-                * create new (see diff.c::run_diff()); in such a case
-                * it is Ok that the entry to be deleted by the
-                * previous patch is still in the working tree and in
-                * the index.
-                */
+           (was_deleted(tpatch) || to_be_deleted(tpatch)))
                ok_if_exists = 1;
        else
                ok_if_exists = 0;
 
        if (new_name &&
            ((0 < patch->is_new) | (0 < patch->is_rename) | patch->is_copy)) {
-               if (check_index &&
-                   cache_name_pos(new_name, strlen(new_name)) >= 0 &&
-                   !ok_if_exists)
+               int err = check_to_create(new_name, ok_if_exists);
+
+               if (err && threeway) {
+                       patch->direct_to_threeway = 1;
+               } else switch (err) {
+               case 0:
+                       break; /* happy */
+               case EXISTS_IN_INDEX:
                        return error(_("%s: already exists in index"), new_name);
-               if (!cached) {
-                       int err = check_to_create_blob(new_name, ok_if_exists);
-                       if (err)
-                               return err;
+                       break;
+               case EXISTS_IN_WORKTREE:
+                       return error(_("%s: already exists in working directory"),
+                                    new_name);
+               default:
+                       return err;
                }
+
                if (!patch->new_mode) {
                        if (0 < patch->is_new)
                                patch->new_mode = S_IFREG | 0644;
@@ -3330,7 +3589,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
                name = patch->old_name ? patch->old_name : patch->new_name;
                if (0 < patch->is_new)
                        continue;
-               else if (get_sha1(patch->old_sha1_prefix, sha1))
+               else if (get_sha1_blob(patch->old_sha1_prefix, sha1))
                        /* git diff has no index line for mode/type changes */
                        if (!patch->lines_added && !patch->lines_deleted) {
                                if (get_current_sha1(patch->old_name, sha1))
@@ -3510,7 +3769,8 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
        ce = xcalloc(1, ce_size);
        memcpy(ce->name, path, namelen);
        ce->ce_mode = create_ce_mode(mode);
-       ce->ce_flags = namelen;
+       ce->ce_flags = create_ce_flags(0);
+       ce->ce_namelen = namelen;
        if (S_ISGITLINK(mode)) {
                const char *s = buf;
 
@@ -3612,6 +3872,33 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
        die_errno(_("unable to write file '%s' mode %o"), path, mode);
 }
 
+static void add_conflicted_stages_file(struct patch *patch)
+{
+       int stage, namelen;
+       unsigned ce_size, mode;
+       struct cache_entry *ce;
+
+       if (!update_index)
+               return;
+       namelen = strlen(patch->new_name);
+       ce_size = cache_entry_size(namelen);
+       mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644);
+
+       remove_file_from_cache(patch->new_name);
+       for (stage = 1; stage < 4; stage++) {
+               if (is_null_sha1(patch->threeway_stage[stage - 1]))
+                       continue;
+               ce = xcalloc(1, ce_size);
+               memcpy(ce->name, patch->new_name, namelen);
+               ce->ce_mode = create_ce_mode(mode);
+               ce->ce_flags = create_ce_flags(stage);
+               ce->ce_namelen = namelen;
+               hashcpy(ce->sha1, patch->threeway_stage[stage - 1]);
+               if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
+                       die(_("unable to add cache entry for %s"), patch->new_name);
+       }
+}
+
 static void create_file(struct patch *patch)
 {
        char *path = patch->new_name;
@@ -3622,7 +3909,11 @@ static void create_file(struct patch *patch)
        if (!mode)
                mode = S_IFREG | 0644;
        create_one_file(path, mode, buf, size);
-       add_index_file(path, mode, buf, size);
+
+       if (patch->conflicted_threeway)
+               add_conflicted_stages_file(patch);
+       else
+               add_index_file(path, mode, buf, size);
 }
 
 /* phase zero is to remove, phase one is to create */
@@ -3724,6 +4015,7 @@ static int write_out_results(struct patch *list)
        int phase;
        int errs = 0;
        struct patch *l;
+       struct string_list cpath = STRING_LIST_INIT_DUP;
 
        for (phase = 0; phase < 2; phase++) {
                l = list;
@@ -3732,12 +4024,30 @@ static int write_out_results(struct patch *list)
                                errs = 1;
                        else {
                                write_out_one_result(l, phase);
-                               if (phase == 1 && write_out_one_reject(l))
-                                       errs = 1;
+                               if (phase == 1) {
+                                       if (write_out_one_reject(l))
+                                               errs = 1;
+                                       if (l->conflicted_threeway) {
+                                               string_list_append(&cpath, l->new_name);
+                                               errs = 1;
+                                       }
+                               }
                        }
                        l = l->next;
                }
        }
+
+       if (cpath.nr) {
+               struct string_list_item *item;
+
+               sort_string_list(&cpath);
+               for_each_string_list_item(item, &cpath)
+                       fprintf(stderr, "U %s\n", item->string);
+               string_list_clear(&cpath, 0);
+
+               rerere(0);
+       }
+
        return errs;
 }
 
@@ -3860,8 +4170,12 @@ static int apply_patch(int fd, const char *filename, int options)
            !apply_with_reject)
                exit(1);
 
-       if (apply && write_out_results(list))
-               exit(1);
+       if (apply && write_out_results(list)) {
+               if (apply_with_reject)
+                       exit(1);
+               /* with --3way, we still need to write the index out */
+               return 1;
+       }
 
        if (fake_ancestor)
                build_fake_ancestor(list, fake_ancestor);
@@ -3994,6 +4308,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                        N_("apply a patch without touching the working tree")),
                OPT_BOOLEAN(0, "apply", &force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
+               OPT_BOOL('3', "3way", &threeway,
+                        N_( "attempt three-way merge if a patch does not apply")),
                OPT_FILENAME(0, "build-fake-ancestor", &fake_ancestor,
                        N_("build a temporary index based on embedded index information")),
                { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
@@ -4042,6 +4358,15 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
        argc = parse_options(argc, argv, prefix, builtin_apply_options,
                        apply_usage, 0);
 
+       if (apply_with_reject && threeway)
+               die("--reject and --3way cannot be used together.");
+       if (cached && threeway)
+               die("--cached and --3way cannot be used together.");
+       if (threeway) {
+               if (is_not_gitdir)
+                       die(_("--3way outside a repository"));
+               check_index = 1;
+       }
        if (apply_with_reject)
                apply = apply_verbosely = 1;
        if (!force_apply && (diffstat || numstat || summary || check || fake_ancestor))
index 960c58d855a6f1a04ad1d08637fc75c3da240a30..0d50273ce975d956cba3c17160bf3704b57f3f9c 100644 (file)
@@ -2171,7 +2171,8 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        ce = xcalloc(1, size);
        hashcpy(ce->sha1, origin->blob_sha1);
        memcpy(ce->name, path, len);
-       ce->ce_flags = create_ce_flags(len, 0);
+       ce->ce_flags = create_ce_flags(0);
+       ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 
index 36a9104433e23422aab39b1912e998a7f54cd3f4..af74e775a182bdf764436362cb37471e74930459 100644 (file)
@@ -91,7 +91,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
        unsigned long size;
        struct object_context obj_context;
 
-       if (get_sha1_with_context(obj_name, sha1, &obj_context))
+       if (get_sha1_with_context(obj_name, 0, sha1, &obj_context))
                die("Not a valid object name %s", obj_name);
 
        buf = NULL;
index 3980d5d06ea5aba4f4d6d30089307fe76c8adff9..6acca75f4740e9412c418124ce791c00f7b4ba4b 100644 (file)
@@ -73,7 +73,8 @@ static int update_some(const unsigned char *sha1, const char *base, int baselen,
        hashcpy(ce->sha1, sha1);
        memcpy(ce->name, base, baselen);
        memcpy(ce->name + baselen, pathname, len - baselen);
-       ce->ce_flags = create_ce_flags(len, 0) | CE_UPDATE;
+       ce->ce_flags = create_ce_flags(0) | CE_UPDATE;
+       ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
        add_cache_entry(ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
        return 0;
index d3b7fdccecb7a368b4931d3a8c6ea0483509dccc..e314b0b6d2133b09307dc09fa256577e6cdba03a 100644 (file)
@@ -708,7 +708,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                if (safe_create_leading_directories_const(work_tree) < 0)
                        die_errno(_("could not create leading directories of '%s'"),
                                  work_tree);
-               if (!dest_exists && mkdir(work_tree, 0755))
+               if (!dest_exists && mkdir(work_tree, 0777))
                        die_errno(_("could not create work tree dir '%s'."),
                                  work_tree);
                set_git_work_tree(work_tree);
index 164b655df93fea1ec2f63f5238c1ad7a377c8385..cb982c550332abc846eee690f8e33c66db742053 100644 (file)
@@ -48,8 +48,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        if (argc < 2 || !strcmp(argv[1], "-h"))
                usage(commit_tree_usage);
 
-       if (get_sha1(argv[1], tree_sha1))
-               die("Not a valid object name %s", argv[1]);
+       if (get_sha1_tree(argv[1], tree_sha1))
+               die("Not a valid tree object name %s", argv[1]);
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
@@ -57,7 +57,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                        unsigned char sha1[20];
                        if (argc <= ++i)
                                usage(commit_tree_usage);
-                       if (get_sha1(argv[i], sha1))
+                       if (get_sha1_commit(argv[i], sha1))
                                die("Not a valid object name %s", argv[i]);
                        assert_sha1_type(sha1, OBJ_COMMIT);
                        new_parent(lookup_commit(sha1), &parents);
@@ -104,7 +104,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
-               if (get_sha1(arg, tree_sha1))
+               if (get_sha1_tree(arg, tree_sha1))
                        die("Not a valid object name %s", arg);
                if (got_tree)
                        die("Cannot give more than one trees");
index f43eaafb3b0afc1fb2dcc994433a6ad3be725654..95eeab1d5146628277b0ba2c4e4bc7a6f77f3a4a 100644 (file)
@@ -184,6 +184,9 @@ static int list_paths(struct string_list *list, const char *with_tree,
        int i;
        char *m;
 
+       if (!pattern)
+               return 0;
+
        for (i = 0; pattern[i]; i++)
                ;
        m = xcalloc(1, i);
@@ -345,7 +348,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
         * and create commit from the_index.
         * We still need to refresh the index here.
         */
-       if (!pathspec || !*pathspec) {
+       if (!only && (!pathspec || !*pathspec)) {
                fd = hold_locked_index(&index_lock, 1);
                refresh_cache_or_die(refresh_flags);
                if (active_cache_changed) {
@@ -640,7 +643,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                hook_arg1 = "message";
        } else if (use_message) {
                buffer = strstr(use_message_buffer, "\n\n");
-               if (!buffer || buffer[2] == '\0')
+               if (!use_editor && (!buffer || buffer[2] == '\0'))
                        die(_("commit has empty message"));
                strbuf_add(&sb, buffer + 2, strlen(buffer + 2));
                hook_arg1 = "commit";
index e8e1c0a4567f190c4b5d939bf9b0d363765bfd30..8cd08da99122bc79025d2a78204d316f1b7ba478 100644 (file)
@@ -387,12 +387,20 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 
                home_config_paths(&user_config, &xdg_config, "config");
 
-               if (access(user_config, R_OK) && !access(xdg_config, R_OK))
+               if (!user_config)
+                       /*
+                        * It is unknown if HOME/.gitconfig exists, so
+                        * we do not know if we should write to XDG
+                        * location; error out even if XDG_CONFIG_HOME
+                        * is set and points at a sane location.
+                        */
+                       die("$HOME not set");
+
+               if (access(user_config, R_OK) &&
+                   xdg_config && !access(xdg_config, R_OK))
                        given_config_file = xdg_config;
-               else if (user_config)
-                       given_config_file = user_config;
                else
-                       die("$HOME not set");
+                       given_config_file = user_config;
        }
        else if (use_system_config)
                given_config_file = git_etc_gitconfig();
index 5a0372ab08e7d7fe8381356669702b5e5838f78f..953dd3004e285ce7aa0432f4fe0eacb0d0c2c887 100644 (file)
@@ -524,7 +524,6 @@ static void *unpack_data(struct object_entry *obj,
        stream.avail_out = consume ? 64*1024 : obj->size;
 
        do {
-               unsigned char *last_out = stream.next_out;
                ssize_t n = (len < 64*1024) ? len : 64*1024;
                n = pread(pack_fd, inbuf, n, from);
                if (n < 0)
@@ -538,15 +537,19 @@ static void *unpack_data(struct object_entry *obj,
                len -= n;
                stream.next_in = inbuf;
                stream.avail_in = n;
-               status = git_inflate(&stream, 0);
-               if (consume) {
-                       if (consume(last_out, stream.next_out - last_out, cb_data)) {
-                               free(inbuf);
-                               free(data);
-                               return NULL;
-                       }
-                       stream.next_out = data;
-                       stream.avail_out = 64*1024;
+               if (!consume)
+                       status = git_inflate(&stream, 0);
+               else {
+                       do {
+                               status = git_inflate(&stream, 0);
+                               if (consume(data, stream.next_out - data, cb_data)) {
+                                       free(inbuf);
+                                       free(data);
+                                       return NULL;
+                               }
+                               stream.next_out = data;
+                               stream.avail_out = 64*1024;
+                       } while (status == Z_OK && stream.avail_in);
                }
        } while (len && status == Z_OK && !stream.avail_in);
 
index adcbcf1f2446573c99f00fb2480c8b582237cae1..ecc2793690496531546765516d80032a1cbf8844 100644 (file)
@@ -367,6 +367,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
        rev.simplify_history = 0;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
+       opt.revarg_opt = REVARG_COMMITTISH;
        cmd_log_init(argc, argv, prefix, &rev, &opt);
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_RAW;
@@ -557,6 +558,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
        rev.always_show_header = 1;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
+       opt.revarg_opt = REVARG_COMMITTISH;
        cmd_log_init(argc, argv, prefix, &rev, &opt);
        return cmd_log_walk(&rev);
 }
@@ -1132,6 +1134,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
        s_r_opt.def = "HEAD";
+       s_r_opt.revarg_opt = REVARG_COMMITTISH;
 
        if (default_attach) {
                rev.mime_boundary = default_attach;
index f3348208d89a4fbeb3ae9f79376d7d21a8f9015c..782e7d0c38aa939a7db03266c3e6ff4993e4d2bd 100644 (file)
@@ -2373,7 +2373,7 @@ static void get_object_list(int ac, const char **av)
                        }
                        die("not a rev '%s'", line);
                }
-               if (handle_revision_arg(line, &revs, flags, 1))
+               if (handle_revision_arg(line, &revs, flags, REVARG_CANNOT_BE_FILENAME))
                        die("bad revision '%s'", line);
        }
 
index 4cc34c908446fe2d3db5acf315e47f2768ba07bd..74442bd7663387ab9864927f9c0d511e04e6e379 100644 (file)
@@ -276,7 +276,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                 * Otherwise, argv[i] could be either <rev> or <paths> and
                 * has to be unambiguous.
                 */
-               else if (!get_sha1(argv[i], sha1)) {
+               else if (!get_sha1_committish(argv[i], sha1)) {
                        /*
                         * Ok, argv[i] looks like a rev; it should not
                         * be a filename.
@@ -289,9 +289,15 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                }
        }
 
-       if (get_sha1(rev, sha1))
+       if (get_sha1_committish(rev, sha1))
                die(_("Failed to resolve '%s' as a valid ref."), rev);
 
+       /*
+        * NOTE: As "git reset $treeish -- $path" should be usable on
+        * any tree-ish, this is not strictly correct. We are not
+        * moving the HEAD to any commit; we are merely resetting the
+        * entries in the index to that of a treeish.
+        */
        commit = lookup_commit_reference(sha1);
        if (!commit)
                die(_("Could not parse object '%s'."), rev);
index 13495b88f5da1efc2094c0e69abfe93605ee8c03..32788a9f86427237d0f4838362c1e3bc722fa258 100644 (file)
@@ -195,6 +195,12 @@ static int anti_reference(const char *refname, const unsigned char *sha1, int fl
        return 0;
 }
 
+static int show_abbrev(const unsigned char *sha1, void *cb_data)
+{
+       show_rev(NORMAL, sha1, NULL);
+       return 0;
+}
+
 static void show_datestring(const char *flag, const char *datestr)
 {
        static char buffer[100];
@@ -238,7 +244,7 @@ static int try_difference(const char *arg)
                next = "HEAD";
        if (dotdot == arg)
                this = "HEAD";
-       if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
+       if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) {
                show_rev(NORMAL, end, next);
                show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
                if (symmetric) {
@@ -278,7 +284,7 @@ static int try_parent_shorthands(const char *arg)
                return 0;
 
        *dotdot = 0;
-       if (get_sha1(arg, sha1))
+       if (get_sha1_committish(arg, sha1))
                return 0;
 
        if (!parents_only)
@@ -589,6 +595,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
+                       if (!prefixcmp(arg, "--disambiguate=")) {
+                               for_each_abbrev(arg + 15, show_abbrev, NULL);
+                               continue;
+                       }
                        if (!strcmp(arg, "--bisect")) {
                                for_each_ref_in("refs/bisect/bad", show_reference, NULL);
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
index 5a4e9ea55a10afe2eb0f6e138cb7fce1ef74393f..4ce341ceebbe27cc21b0341e4a4339af84af51de 100644 (file)
@@ -95,7 +95,8 @@ static int add_one_path(struct cache_entry *old, const char *path, int len, stru
        size = cache_entry_size(len);
        ce = xcalloc(1, size);
        memcpy(ce->name, path, len);
-       ce->ce_flags = len;
+       ce->ce_flags = create_ce_flags(0);
+       ce->ce_namelen = len;
        fill_stat_cache_info(ce, st);
        ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
 
@@ -229,7 +230,8 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
 
        hashcpy(ce->sha1, sha1);
        memcpy(ce->name, path, len);
-       ce->ce_flags = create_ce_flags(len, stage);
+       ce->ce_flags = create_ce_flags(stage);
+       ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
        if (assume_unchanged)
                ce->ce_flags |= CE_VALID;
@@ -427,7 +429,8 @@ static struct cache_entry *read_one_ent(const char *which,
 
        hashcpy(ce->sha1, sha1);
        memcpy(ce->name, path, namelen);
-       ce->ce_flags = create_ce_flags(namelen, stage);
+       ce->ce_flags = create_ce_flags(stage);
+       ce->ce_namelen = namelen;
        ce->ce_mode = create_ce_mode(mode);
        return ce;
 }
diff --git a/cache.h b/cache.h
index 6a1aff5e2aa0cbbdc6f1cb49425f45421b2ea18f..67f28b4da97b5aded337e1594043a73c075ea02f 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -128,13 +128,13 @@ struct cache_entry {
        unsigned int ce_gid;
        unsigned int ce_size;
        unsigned int ce_flags;
+       unsigned int ce_namelen;
        unsigned char sha1[20];
        struct cache_entry *next;
        struct cache_entry *dir_next;
        char name[FLEX_ARRAY]; /* more */
 };
 
-#define CE_NAMEMASK  (0x0fff)
 #define CE_STAGEMASK (0x3000)
 #define CE_EXTENDED  (0x4000)
 #define CE_VALID     (0x8000)
@@ -198,21 +198,12 @@ static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry
        dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
 }
 
-static inline unsigned create_ce_flags(size_t len, unsigned stage)
+static inline unsigned create_ce_flags(unsigned stage)
 {
-       if (len >= CE_NAMEMASK)
-               len = CE_NAMEMASK;
-       return (len | (stage << CE_STAGESHIFT));
-}
-
-static inline size_t ce_namelen(const struct cache_entry *ce)
-{
-       size_t len = ce->ce_flags & CE_NAMEMASK;
-       if (len < CE_NAMEMASK)
-               return len;
-       return strlen(ce->name + CE_NAMEMASK) + CE_NAMEMASK;
+       return (stage << CE_STAGESHIFT);
 }
 
+#define ce_namelen(ce) ((ce)->ce_namelen)
 #define ce_size(ce) cache_entry_size(ce_namelen(ce))
 #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
 #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
@@ -451,6 +442,7 @@ extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
 extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
+extern int index_name_stage_pos(const struct index_state *, const char *name, int namelen, int stage);
 extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
@@ -790,17 +782,25 @@ struct object_context {
        unsigned mode;
 };
 
+#define GET_SHA1_QUIETLY        01
+#define GET_SHA1_COMMIT         02
+#define GET_SHA1_COMMITTISH     04
+#define GET_SHA1_TREE          010
+#define GET_SHA1_TREEISH       020
+#define GET_SHA1_BLOB         040
+#define GET_SHA1_ONLY_TO_DIE 04000
+
 extern int get_sha1(const char *str, unsigned char *sha1);
-extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int only_to_die, const char *prefix);
-static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
-{
-       return get_sha1_with_mode_1(str, sha1, mode, 0, NULL);
-}
-extern int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *orc, int only_to_die, const char *prefix);
-static inline int get_sha1_with_context(const char *str, unsigned char *sha1, struct object_context *orc)
-{
-       return get_sha1_with_context_1(str, sha1, orc, 0, NULL);
-}
+extern int get_sha1_commit(const char *str, unsigned char *sha1);
+extern int get_sha1_committish(const char *str, unsigned char *sha1);
+extern int get_sha1_tree(const char *str, unsigned char *sha1);
+extern int get_sha1_treeish(const char *str, unsigned char *sha1);
+extern int get_sha1_blob(const char *str, unsigned char *sha1);
+extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
+extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
+
+typedef int each_abbrev_fn(const unsigned char *sha1, void *);
+extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
 
 /*
  * Try to read a SHA1 in hexadecimal format from the 40 characters
@@ -864,6 +864,7 @@ extern int validate_headref(const char *ref);
 extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int df_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
 extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
+extern int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2);
 
 extern void *read_object_with_reference(const unsigned char *sha1,
                                        const char *required_type,
index 8248a994a50ab91874600567e544aa3735e4aa98..42af4c1f238b96eeb9e88b227b6f6c10ef317058 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -68,7 +68,7 @@ struct commit *lookup_commit_reference_by_name(const char *name)
        unsigned char sha1[20];
        struct commit *commit;
 
-       if (get_sha1(name, sha1))
+       if (get_sha1_committish(name, sha1))
                return NULL;
        commit = lookup_commit_reference(sha1);
        if (!commit || parse_commit(commit))
index 40818e872ffb61e725d63e0bbf1424ea8987009c..2b706ea2053714fdcec8997d978ce5688d072941 100644 (file)
--- a/config.c
+++ b/config.c
@@ -945,12 +945,12 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
                found += 1;
        }
 
-       if (!access(xdg_config, R_OK)) {
+       if (xdg_config && !access(xdg_config, R_OK)) {
                ret += git_config_from_file(fn, xdg_config, data);
                found += 1;
        }
 
-       if (!access(user_config, R_OK)) {
+       if (user_config && !access(user_config, R_OK)) {
                ret += git_config_from_file(fn, user_config, data);
                found += 1;
        }
diff --git a/date.c b/date.c
index 1fdcf7c6eae0386552c5d7e069f5242fca3c2e0e..57331ed406e2391e0a2bf327fbb86d3b62012324 100644 (file)
--- a/date.c
+++ b/date.c
@@ -624,7 +624,7 @@ static int match_object_header_date(const char *date, unsigned long *timestamp,
        unsigned long stamp;
        int ofs;
 
-       if (*date < '0' || '9' <= *date)
+       if (*date < '0' || '9' < *date)
                return -1;
        stamp = strtoul(date, &end, 10);
        if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
index b6a530018e18527efc8ed788e815b380d0206787..c02e62d2aa4f3bd2221c687effa3a4ec44918383 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -857,6 +857,11 @@ did you forget to use 'git add'?"
        if test $apply_status != 0
        then
                eval_gettextln 'Patch failed at $msgnum $FIRSTLINE'
+               if test "$(git config --bool advice.amworkdir)" != false
+               then
+                       eval_gettextln "The copy of the patch that failed is found in:
+   $dotest/patch"
+               fi
                stop_here_user_resolve $this
        fi
 
index add2c0247fa91e0f629428c295fc581f19cf85e1..178e45305d2b66bbe5a0c0902d989ea66888869b 100755 (executable)
@@ -84,7 +84,7 @@ set_ident () {
                        s/.*/GIT_'$uid'_EMAIL='\''&'\''; export GIT_'$uid'_EMAIL/p
 
                        g
-                       s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/
+                       s/^'$lid' [^<]* <[^>]*> \(.*\)$/@\1/
                        s/'\''/'\''\'\'\''/g
                        s/.*/GIT_'$uid'_DATE='\''&'\''; export GIT_'$uid'_DATE/p
 
index 73da3fe913846894ce1c8a62fa8b0b2d0567f89e..e67d37d2f93361337df6e680d2b2ab961e31ad66 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -120,6 +120,15 @@ def p4_read_pipe_lines(c):
     real_cmd = p4_build_cmd(c)
     return read_pipe_lines(real_cmd)
 
+def p4_has_command(cmd):
+    """Ask p4 for help on this command.  If it returns an error, the
+       command does not exist in this version of p4."""
+    real_cmd = p4_build_cmd(["help", cmd])
+    p = subprocess.Popen(real_cmd, stdout=subprocess.PIPE,
+                                   stderr=subprocess.PIPE)
+    p.communicate()
+    return p.returncode == 0
+
 def system(cmd):
     expand = isinstance(cmd,basestring)
     if verbose:
@@ -157,6 +166,9 @@ def p4_revert(f):
 def p4_reopen(type, f):
     p4_system(["reopen", "-t", type, wildcard_encode(f)])
 
+def p4_move(src, dest):
+    p4_system(["move", "-k", wildcard_encode(src), wildcard_encode(dest)])
+
 #
 # Canonicalize the p4 type and return a tuple of the
 # base type, plus any modifiers.  See "p4 help filetypes"
@@ -849,6 +861,7 @@ def __init__(self):
         self.preserveUser = gitConfig("git-p4.preserveUser").lower() == "true"
         self.isWindows = (platform.system() == "Windows")
         self.exportLabels = False
+        self.p4HasMoveCommand = p4_has_command("move")
 
     def check(self):
         if len(p4CmdList("opened ...")) > 0:
@@ -1079,27 +1092,7 @@ def applyCommit(self, id):
 
         (p4User, gitEmail) = self.p4UserForCommit(id)
 
-        if not self.detectRenames:
-            # If not explicitly set check the config variable
-            self.detectRenames = gitConfig("git-p4.detectRenames")
-
-        if self.detectRenames.lower() == "false" or self.detectRenames == "":
-            diffOpts = ""
-        elif self.detectRenames.lower() == "true":
-            diffOpts = "-M"
-        else:
-            diffOpts = "-M%s" % self.detectRenames
-
-        detectCopies = gitConfig("git-p4.detectCopies")
-        if detectCopies.lower() == "true":
-            diffOpts += " -C"
-        elif detectCopies != "" and detectCopies.lower() != "false":
-            diffOpts += " -C%s" % detectCopies
-
-        if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true":
-            diffOpts += " --find-copies-harder"
-
-        diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (diffOpts, id, id))
+        diff = read_pipe_lines("git diff-tree -r %s \"%s^\" \"%s\"" % (self.diffOpts, id, id))
         filesToAdd = set()
         filesToDelete = set()
         editedFiles = set()
@@ -1139,17 +1132,23 @@ def applyCommit(self, id):
                 editedFiles.add(dest)
             elif modifier == "R":
                 src, dest = diff['src'], diff['dst']
-                p4_integrate(src, dest)
-                if diff['src_sha1'] != diff['dst_sha1']:
-                    p4_edit(dest)
+                if self.p4HasMoveCommand:
+                    p4_edit(src)        # src must be open before move
+                    p4_move(src, dest)  # opens for (move/delete, move/add)
                 else:
-                    pureRenameCopy.add(dest)
+                    p4_integrate(src, dest)
+                    if diff['src_sha1'] != diff['dst_sha1']:
+                        p4_edit(dest)
+                    else:
+                        pureRenameCopy.add(dest)
                 if isModeExecChanged(diff['src_mode'], diff['dst_mode']):
-                    p4_edit(dest)
+                    if not self.p4HasMoveCommand:
+                        p4_edit(dest)   # with move: already open, writable
                     filesToChangeExecBit[dest] = diff['dst_mode']
-                os.unlink(dest)
+                if not self.p4HasMoveCommand:
+                    os.unlink(dest)
+                    filesToDelete.add(src)
                 editedFiles.add(dest)
-                filesToDelete.add(src)
             else:
                 die("unknown modifier %s for %s" % (modifier, path))
 
@@ -1457,6 +1456,37 @@ def run(self, args):
         if self.preserveUser:
             self.checkValidP4Users(commits)
 
+        #
+        # Build up a set of options to be passed to diff when
+        # submitting each commit to p4.
+        #
+        if self.detectRenames:
+            # command-line -M arg
+            self.diffOpts = "-M"
+        else:
+            # If not explicitly set check the config variable
+            detectRenames = gitConfig("git-p4.detectRenames")
+
+            if detectRenames.lower() == "false" or detectRenames == "":
+                self.diffOpts = ""
+            elif detectRenames.lower() == "true":
+                self.diffOpts = "-M"
+            else:
+                self.diffOpts = "-M%s" % detectRenames
+
+        # no command-line arg for -C or --find-copies-harder, just
+        # config variables
+        detectCopies = gitConfig("git-p4.detectCopies")
+        if detectCopies.lower() == "false" or detectCopies == "":
+            pass
+        elif detectCopies.lower() == "true":
+            self.diffOpts += " -C"
+        else:
+            self.diffOpts += " -C%s" % detectCopies
+
+        if gitConfig("git-p4.detectCopiesHarder", "--bool") == "true":
+            self.diffOpts += " --find-copies-harder"
+
         while len(commits) > 0:
             commit = commits[0]
             commits = commits[1:]
index 3a3c3823571162853e4e88876e7646287c7c1af5..bef7bc0444bab7b7e511c6e29417ce6db44b77ef 100644 (file)
@@ -415,6 +415,29 @@ record_in_rewritten() {
        esac
 }
 
+do_pick () {
+       if test "$(git rev-parse HEAD)" = "$squash_onto"
+       then
+               # Set the correct commit message and author info on the
+               # sentinel root before cherry-picking the original changes
+               # without committing (-n).  Finally, update the sentinel again
+               # to include these changes.  If the cherry-pick results in a
+               # conflict, this means our behaviour is similar to a standard
+               # failed cherry-pick during rebase, with a dirty index to
+               # resolve before manually running git commit --amend then git
+               # rebase --continue.
+               git commit --allow-empty --allow-empty-message --amend \
+                          --no-post-rewrite -n -q -C $1 &&
+                       pick_one -n $1 &&
+                       git commit --allow-empty --allow-empty-message \
+                                  --amend --no-post-rewrite -n -q -C $1 ||
+                       die_with_patch $1 "Could not apply $1... $2"
+       else
+               pick_one $1 ||
+                       die_with_patch $1 "Could not apply $1... $2"
+       fi
+}
+
 do_next () {
        rm -f "$msg" "$author_script" "$amend" || exit
        read -r command sha1 rest < "$todo"
@@ -426,16 +449,14 @@ do_next () {
                comment_for_reflog pick
 
                mark_action_done
-               pick_one $sha1 ||
-                       die_with_patch $sha1 "Could not apply $sha1... $rest"
+               do_pick $sha1 "$rest"
                record_in_rewritten $sha1
                ;;
        reword|r)
                comment_for_reflog reword
 
                mark_action_done
-               pick_one $sha1 ||
-                       die_with_patch $sha1 "Could not apply $sha1... $rest"
+               do_pick $sha1 "$rest"
                git commit --amend --no-post-rewrite || {
                        warn "Could not amend commit after successfully picking $sha1... $rest"
                        warn "This is most likely due to an empty commit message, or the pre-commit hook"
@@ -449,8 +470,7 @@ do_next () {
                comment_for_reflog edit
 
                mark_action_done
-               pick_one $sha1 ||
-                       die_with_patch $sha1 "Could not apply $sha1... $rest"
+               do_pick $sha1 "$rest"
                warn "Stopped at $sha1... $rest"
                exit_with_patch $sha1 0
                ;;
index 5bddfa9690e8dfbedd07e31dc7e768b60f156213..1cd0633b80a8daf00dbc50657ccba228bd578a73 100755 (executable)
@@ -32,7 +32,7 @@ SUBDIRECTORY_OK=Yes
 OPTIONS_KEEPDASHDASH=
 OPTIONS_SPEC="\
 git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
-git rebase [-i] [options] [--exec <cmd>] --onto <newbase> --root [<branch>]
+git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
 git-rebase [-i] --continue | --abort | --skip
 --
  Available options are
@@ -378,6 +378,11 @@ and run me again.  I am stopping in case you still have something
 valuable there.'
 fi
 
+if test -n "$rebase_root" && test -z "$onto"
+then
+       test -z "$interactive_rebase" && interactive_rebase=implied
+fi
+
 if test -n "$interactive_rebase"
 then
        type=interactive
@@ -411,7 +416,12 @@ then
        die "invalid upstream $upstream_name"
        upstream_arg="$upstream_name"
 else
-       test -z "$onto" && die "You must specify --onto when using --root"
+       if test -z "$onto"
+       then
+               empty_tree=`git hash-object -t tree /dev/null`
+               onto=`git commit-tree $empty_tree </dev/null`
+               squash_onto="$onto"
+       fi
        unset upstream_name
        unset upstream
        test $# -gt 1 && usage
index 5629d875e6a0c0a390d046f28bf2d768116bf974..dba4d39e1fd3e971336e0d85f24c114c29c4d1fd 100755 (executable)
@@ -186,8 +186,10 @@ module_clone()
                die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
        fi
 
-       a=$(cd "$gitdir" && pwd)/
-       b=$(cd "$sm_path" && pwd)/
+       # We already are at the root of the work tree but cd_to_toplevel will
+       # resolve any symlinks that might be present in $PWD
+       a=$(cd_to_toplevel && cd "$gitdir" && pwd)/
+       b=$(cd_to_toplevel && cd "$sm_path" && pwd)/
        # normalize Windows-style absolute paths to POSIX-style absolute paths
        case $a in [a-zA-Z]:/*) a=/${a%%:*}${a#*:} ;; esac
        case $b in [a-zA-Z]:/*) b=/${b%%:*}${b#*:} ;; esac
index 0b074c4c63ebb6e1334cf05371cd22087fb79f63..6673d21f841c7f5a4806bc7785c5ae7755f9297b 100755 (executable)
@@ -367,9 +367,9 @@ package main;
 eval {
        Git::SVN::verify_remotes_sanity();
        $cmd{$cmd}->[0]->(@ARGV);
+       post_fetch_checkout();
 };
 fatal $@ if $@;
-post_fetch_checkout();
 exit 0;
 
 ####################### primary functions ######################
@@ -1598,8 +1598,8 @@ sub rebase_cmd {
 
 sub post_fetch_checkout {
        return if $_no_checkout;
+       return if verify_ref('HEAD^0');
        my $gs = $Git::SVN::_head or return;
-       return if verify_ref('refs/heads/master^0');
 
        # look for "trunk" ref if it exists
        my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
@@ -1612,9 +1612,8 @@ sub post_fetch_checkout {
                }
        }
 
-       my $valid_head = verify_ref('HEAD^0');
-       command_noisy(qw(update-ref refs/heads/master), $gs->refname);
-       return if ($valid_head || !verify_ref('HEAD^0'));
+       command_noisy(qw(update-ref HEAD), $gs->refname);
+       return unless verify_ref('HEAD^0');
 
        return if $ENV{GIT_DIR} !~ m#^(?:.*/)?\.git$#;
        my $index = $ENV{GIT_INDEX_FILE} || "$ENV{GIT_DIR}/index";
index 55e0e9ea38b3080e32467b6faf56f40d45386b96..3d6a7053881ec7646dc28a2eba991a15ff80c634 100755 (executable)
@@ -4484,30 +4484,33 @@ sub git_print_log {
        }
 
        # print log
-       my $signoff = 0;
-       my $empty = 0;
+       my $skip_blank_line = 0;
        foreach my $line (@$log) {
-               if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
-                       $signoff = 1;
-                       $empty = 0;
+               if ($line =~ m/^\s*([A-Z][-A-Za-z]*-[Bb]y|C[Cc]): /) {
                        if (! $opts{'-remove_signoff'}) {
                                print "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
-                               next;
-                       } else {
-                               # remove signoff lines
-                               next;
+                               $skip_blank_line = 1;
                        }
-               } else {
-                       $signoff = 0;
+                       next;
+               }
+
+               if ($line =~ m,\s*([a-z]*link): (https?://\S+),i) {
+                       if (! $opts{'-remove_signoff'}) {
+                               print "<span class=\"signoff\">" . esc_html($1) . ": " .
+                                       "<a href=\"" . esc_html($2) . "\">" . esc_html($2) . "</a>" .
+                                       "</span><br/>\n";
+                               $skip_blank_line = 1;
+                       }
+                       next;
                }
 
                # print only one empty line
                # do not print empty line after signoff
                if ($line eq "") {
-                       next if ($empty || $signoff);
-                       $empty = 1;
+                       next if ($skip_blank_line);
+                       $skip_blank_line = 1;
                } else {
-                       $empty = 0;
+                       $skip_blank_line = 0;
                }
 
                print format_log_line_html($line) . "<br/>\n";
@@ -4515,7 +4518,7 @@ sub git_print_log {
 
        if ($opts{'-final_empty_line'}) {
                # end with single empty line
-               print "<br/>\n" unless $empty;
+               print "<br/>\n" unless $skip_blank_line;
        }
 }
 
index 1df6adf0bf786d7428d42973b3e7bc41870ff702..2f8159fb165f853aafb5e0cee61f35aa854271ec 100644 (file)
 
 static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 
+/* Mask for the name length in ce_flags in the on-disk index */
+
+#define CE_NAMEMASK  (0x0fff)
+
 /* Index extensions.
  *
  * The first letter should be 'A'..'Z' for extensions that are not
@@ -54,8 +58,8 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
 
        new = xmalloc(cache_entry_size(namelen));
        copy_cache_entry(new, old);
-       new->ce_flags &= ~(CE_STATE_MASK | CE_NAMEMASK);
-       new->ce_flags |= (namelen >= CE_NAMEMASK ? CE_NAMEMASK : namelen);
+       new->ce_flags &= ~CE_STATE_MASK;
+       new->ce_namelen = namelen;
        memcpy(new->name, new_name, namelen + 1);
 
        cache_tree_invalidate_path(istate->cache_tree, old->name);
@@ -395,10 +399,8 @@ int df_name_compare(const char *name1, int len1, int mode1,
        return c1 - c2;
 }
 
-int cache_name_compare(const char *name1, int flags1, const char *name2, int flags2)
+int cache_name_stage_compare(const char *name1, int len1, int stage1, const char *name2, int len2, int stage2)
 {
-       int len1 = flags1 & CE_NAMEMASK;
-       int len2 = flags2 & CE_NAMEMASK;
        int len = len1 < len2 ? len1 : len2;
        int cmp;
 
@@ -410,18 +412,19 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla
        if (len1 > len2)
                return 1;
 
-       /* Compare stages  */
-       flags1 &= CE_STAGEMASK;
-       flags2 &= CE_STAGEMASK;
-
-       if (flags1 < flags2)
+       if (stage1 < stage2)
                return -1;
-       if (flags1 > flags2)
+       if (stage1 > stage2)
                return 1;
        return 0;
 }
 
-int index_name_pos(const struct index_state *istate, const char *name, int namelen)
+int cache_name_compare(const char *name1, int len1, const char *name2, int len2)
+{
+       return cache_name_stage_compare(name1, len1, 0, name2, len2, 0);
+}
+
+int index_name_stage_pos(const struct index_state *istate, const char *name, int namelen, int stage)
 {
        int first, last;
 
@@ -430,7 +433,7 @@ int index_name_pos(const struct index_state *istate, const char *name, int namel
        while (last > first) {
                int next = (last + first) >> 1;
                struct cache_entry *ce = istate->cache[next];
-               int cmp = cache_name_compare(name, namelen, ce->name, ce->ce_flags);
+               int cmp = cache_name_stage_compare(name, namelen, stage, ce->name, ce_namelen(ce), ce_stage(ce));
                if (!cmp)
                        return next;
                if (cmp < 0) {
@@ -442,6 +445,11 @@ int index_name_pos(const struct index_state *istate, const char *name, int namel
        return -first-1;
 }
 
+int index_name_pos(const struct index_state *istate, const char *name, int namelen)
+{
+       return index_name_stage_pos(istate, name, namelen, 0);
+}
+
 /* Remove entry, return true if there are more entries to go.. */
 int remove_index_entry_at(struct index_state *istate, int pos)
 {
@@ -581,7 +589,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        size = cache_entry_size(namelen);
        ce = xcalloc(1, size);
        memcpy(ce->name, path, namelen);
-       ce->ce_flags = namelen;
+       ce->ce_namelen = namelen;
        if (!intent_only)
                fill_stat_cache_info(ce, st);
        else
@@ -683,7 +691,8 @@ struct cache_entry *make_cache_entry(unsigned int mode,
 
        hashcpy(ce->sha1, sha1);
        memcpy(ce->name, path, len);
-       ce->ce_flags = create_ce_flags(len, stage);
+       ce->ce_flags = create_ce_flags(stage);
+       ce->ce_namelen = len;
        ce->ce_mode = create_ce_mode(mode);
 
        if (refresh)
@@ -820,7 +829,7 @@ static int has_dir_name(struct index_state *istate,
                }
                len = slash - name;
 
-               pos = index_name_pos(istate, name, create_ce_flags(len, stage));
+               pos = index_name_stage_pos(istate, name, len, stage);
                if (pos >= 0) {
                        /*
                         * Found one, but not so fast.  This could
@@ -910,7 +919,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
        int new_only = option & ADD_CACHE_NEW_ONLY;
 
        cache_tree_invalidate_path(istate->cache_tree, ce->name);
-       pos = index_name_pos(istate, ce->name, ce->ce_flags);
+       pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
 
        /* existing match? Just replace it. */
        if (pos >= 0) {
@@ -942,7 +951,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
                if (!ok_to_replace)
                        return error("'%s' appears as both a file and as a directory",
                                     ce->name);
-               pos = index_name_pos(istate, ce->name, ce->ce_flags);
+               pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
                pos = -pos-1;
        }
        return pos + 1;
@@ -1319,7 +1328,8 @@ static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *on
        ce->ce_uid   = ntoh_l(ondisk->uid);
        ce->ce_gid   = ntoh_l(ondisk->gid);
        ce->ce_size  = ntoh_l(ondisk->size);
-       ce->ce_flags = flags;
+       ce->ce_flags = flags & ~CE_NAMEMASK;
+       ce->ce_namelen = len;
        hashcpy(ce->sha1, ondisk->sha1);
        memcpy(ce->name, name, len);
        ce->name[len] = '\0';
@@ -1646,6 +1656,8 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
 static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
                                       struct cache_entry *ce)
 {
+       short flags;
+
        ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
        ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
        ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
@@ -1657,7 +1669,10 @@ static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
        ondisk->gid  = htonl(ce->ce_gid);
        ondisk->size = htonl(ce->ce_size);
        hashcpy(ondisk->sha1, ce->sha1);
-       ondisk->flags = htons(ce->ce_flags);
+
+       flags = ce->ce_flags;
+       flags |= (ce_namelen(ce) >= CE_NAMEMASK ? CE_NAMEMASK : ce_namelen(ce));
+       ondisk->flags = htons(flags);
        if (ce->ce_flags & CE_EXTENDED) {
                struct ondisk_cache_entry_extended *ondisk2;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
@@ -1845,7 +1860,8 @@ int read_index_unmerged(struct index_state *istate)
                size = cache_entry_size(len);
                new_ce = xcalloc(1, size);
                memcpy(new_ce->name, ce->name, len);
-               new_ce->ce_flags = create_ce_flags(len, 0) | CE_CONFLICTED;
+               new_ce->ce_flags = create_ce_flags(0) | CE_CONFLICTED;
+               new_ce->ce_namelen = len;
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, 0))
                        return error("%s: cannot drop to stage #0",
index da18fc37dfc6cd9071b0ef5fc057048724ff3025..b57a34daa718a5ecf9750d3068a8ecee4f3e261f 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -524,7 +524,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
                                continue;
                        hex = xstrdup(sha1_to_hex(sha1));
                        string_list_insert(rr, path)->util = hex;
-                       if (mkdir(git_path("rr-cache/%s", hex), 0755))
+                       if (mkdir_in_gitdir(git_path("rr-cache/%s", hex)))
                                continue;
                        handle_file(path, NULL, rerere_path(hex, "preimage"));
                        fprintf(stderr, "Recorded preimage for '%s'\n", path);
index 5b81a92e3ac65ab0295f25198511fc83b4bbf1c9..9e8f47a25d8def94cbf2a8389039e4d3d670cd06 100644 (file)
@@ -1000,7 +1000,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
                flags ^= UNINTERESTING;
                arg++;
        }
-       if (get_sha1(arg, sha1))
+       if (get_sha1_committish(arg, sha1))
                return 0;
        while (1) {
                it = get_reference(revs, arg, sha1, 0);
@@ -1114,16 +1114,16 @@ static void prepare_show_merge(struct rev_info *revs)
        revs->limited = 1;
 }
 
-int handle_revision_arg(const char *arg_, struct rev_info *revs,
-                       int flags,
-                       int cant_be_filename)
+int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
 {
-       unsigned mode;
+       struct object_context oc;
        char *dotdot;
        struct object *object;
        unsigned char sha1[20];
        int local_flags;
        const char *arg = arg_;
+       int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
+       unsigned get_sha1_flags = 0;
 
        dotdot = strstr(arg, "..");
        if (dotdot) {
@@ -1141,8 +1141,8 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs,
                        next = "HEAD";
                if (dotdot == arg)
                        this = "HEAD";
-               if (!get_sha1(this, from_sha1) &&
-                   !get_sha1(next, sha1)) {
+               if (!get_sha1_committish(this, from_sha1) &&
+                   !get_sha1_committish(next, sha1)) {
                        struct commit *a, *b;
                        struct commit_list *exclude;
 
@@ -1201,13 +1201,17 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs,
                local_flags = UNINTERESTING;
                arg++;
        }
-       if (get_sha1_with_mode(arg, sha1, &mode))
+
+       if (revarg_opt & REVARG_COMMITTISH)
+               get_sha1_flags = GET_SHA1_COMMITTISH;
+
+       if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
                return revs->ignore_missing ? 0 : -1;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
        add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
-       add_pending_object_with_mode(revs, object, arg, mode);
+       add_pending_object_with_mode(revs, object, arg, oc.mode);
        return 0;
 }
 
@@ -1257,7 +1261,7 @@ static void read_revisions_from_stdin(struct rev_info *revs,
                        }
                        die("options not supported in --stdin mode");
                }
-               if (handle_revision_arg(sb.buf, revs, 0, 1))
+               if (handle_revision_arg(sb.buf, revs, 0, REVARG_CANNOT_BE_FILENAME))
                        die("bad revision '%s'", sb.buf);
        }
        if (seen_dashdash)
@@ -1708,7 +1712,7 @@ static int handle_revision_pseudo_opt(const char *submodule,
  */
 int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct setup_revision_opt *opt)
 {
-       int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
+       int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0, revarg_opt;
        struct cmdline_pathspec prune_data;
        const char *submodule = NULL;
 
@@ -1736,6 +1740,9 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 
        /* Second, deal with arguments and options */
        flags = 0;
+       revarg_opt = opt ? opt->revarg_opt : 0;
+       if (seen_dashdash)
+               revarg_opt |= REVARG_CANNOT_BE_FILENAME;
        read_from_stdin = 0;
        for (left = i = 1; i < argc; i++) {
                const char *arg = argv[i];
@@ -1771,7 +1778,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                        continue;
                }
 
-               if (handle_revision_arg(arg, revs, flags, seen_dashdash)) {
+
+               if (handle_revision_arg(arg, revs, flags, revarg_opt)) {
                        int j;
                        if (seen_dashdash || *arg == '^')
                                die("bad revision '%s'", arg);
@@ -1822,11 +1830,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (revs->def && !revs->pending.nr && !got_rev_arg) {
                unsigned char sha1[20];
                struct object *object;
-               unsigned mode;
-               if (get_sha1_with_mode(revs->def, sha1, &mode))
+               struct object_context oc;
+               if (get_sha1_with_context(revs->def, 0, sha1, &oc))
                        die("bad default revision '%s'", revs->def);
                object = get_reference(revs, revs->def, sha1, 0);
-               add_pending_object_with_mode(revs, object, revs->def, mode);
+               add_pending_object_with_mode(revs, object, revs->def, oc.mode);
        }
 
        /* Did the user ask for any diff output? Run the diff! */
@@ -2361,29 +2369,28 @@ static struct commit *get_revision_internal(struct rev_info *revs)
        }
 
        /*
-        * Now pick up what they want to give us
+        * If our max_count counter has reached zero, then we are done. We
+        * don't simply return NULL because we still might need to show
+        * boundary commits. But we want to avoid calling get_revision_1, which
+        * might do a considerable amount of work finding the next commit only
+        * for us to throw it away.
+        *
+        * If it is non-zero, then either we don't have a max_count at all
+        * (-1), or it is still counting, in which case we decrement.
         */
-       c = get_revision_1(revs);
-       if (c) {
-               while (0 < revs->skip_count) {
-                       revs->skip_count--;
-                       c = get_revision_1(revs);
-                       if (!c)
-                               break;
+       if (revs->max_count) {
+               c = get_revision_1(revs);
+               if (c) {
+                       while (0 < revs->skip_count) {
+                               revs->skip_count--;
+                               c = get_revision_1(revs);
+                               if (!c)
+                                       break;
+                       }
                }
-       }
 
-       /*
-        * Check the max_count.
-        */
-       switch (revs->max_count) {
-       case -1:
-               break;
-       case 0:
-               c = NULL;
-               break;
-       default:
-               revs->max_count--;
+               if (revs->max_count > 0)
+                       revs->max_count--;
        }
 
        if (c)
index 863f4f64543bd9f9e830e7c9567def23e906c7d9..cb5ab3513bc9982095e292119b9119d333556256 100644 (file)
@@ -184,6 +184,7 @@ struct setup_revision_opt {
        void (*tweak)(struct rev_info *, struct setup_revision_opt *);
        const char *submodule;
        int assume_dashdash;
+       unsigned revarg_opt;
 };
 
 extern void init_revisions(struct rev_info *revs, const char *prefix);
@@ -191,7 +192,9 @@ extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, s
 extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
                                 const struct option *options,
                                 const char * const usagestr[]);
-extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
+#define REVARG_CANNOT_BE_FILENAME 01
+#define REVARG_COMMITTISH 02
+extern int handle_revision_arg(const char *arg, struct rev_info *revs, int flags, unsigned revarg_opt);
 
 extern void reset_revision_walk(void);
 extern int prepare_revision_walk(struct rev_info *revs);
diff --git a/setup.c b/setup.c
index e11497720e01dd14a66e152883e675669fc3b548..9139beefc75c4fe92cf8c7dc54a9fae972995aca 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -77,9 +77,6 @@ static void NORETURN die_verify_filename(const char *prefix,
                                         const char *arg,
                                         int diagnose_misspelt_rev)
 {
-       unsigned char sha1[20];
-       unsigned mode;
-
        if (!diagnose_misspelt_rev)
                die("%s: no such path in the working tree.\n"
                    "Use '-- <path>...' to specify paths that do not exist locally.",
@@ -88,11 +85,10 @@ static void NORETURN die_verify_filename(const char *prefix,
         * Saying "'(icase)foo' does not exist in the index" when the
         * user gave us ":(icase)foo" is just stupid.  A magic pathspec
         * begins with a colon and is followed by a non-alnum; do not
-        * let get_sha1_with_mode_1(only_to_die=1) to even trigger.
+        * let maybe_die_on_misspelt_object_name() even trigger.
         */
        if (!(arg[0] == ':' && !isalnum(arg[1])))
-               /* try a detailed diagnostic ... */
-               get_sha1_with_mode_1(arg, sha1, &mode, 1, prefix);
+               maybe_die_on_misspelt_object_name(arg, prefix);
 
        /* ... or fall back the most general message. */
        die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
index 5d81ea0564c8d90b417eb8ec5ed8c32baa2c3ea3..95003c77ea9a0ee240d60bc7958bb48245b14ad4 100644 (file)
@@ -9,14 +9,82 @@
 
 static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
 
-static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
+typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
+
+struct disambiguate_state {
+       disambiguate_hint_fn fn;
+       void *cb_data;
+       unsigned char candidate[20];
+       unsigned candidate_exists:1;
+       unsigned candidate_checked:1;
+       unsigned candidate_ok:1;
+       unsigned disambiguate_fn_used:1;
+       unsigned ambiguous:1;
+       unsigned always_call_fn:1;
+};
+
+static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
+{
+       if (ds->always_call_fn) {
+               ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
+               return;
+       }
+       if (!ds->candidate_exists) {
+               /* this is the first candidate */
+               hashcpy(ds->candidate, current);
+               ds->candidate_exists = 1;
+               return;
+       } else if (!hashcmp(ds->candidate, current)) {
+               /* the same as what we already have seen */
+               return;
+       }
+
+       if (!ds->fn) {
+               /* cannot disambiguate between ds->candidate and current */
+               ds->ambiguous = 1;
+               return;
+       }
+
+       if (!ds->candidate_checked) {
+               ds->candidate_ok = ds->fn(ds->candidate, ds->cb_data);
+               ds->disambiguate_fn_used = 1;
+               ds->candidate_checked = 1;
+       }
+
+       if (!ds->candidate_ok) {
+               /* discard the candidate; we know it does not satisify fn */
+               hashcpy(ds->candidate, current);
+               ds->candidate_checked = 0;
+               return;
+       }
+
+       /* if we reach this point, we know ds->candidate satisfies fn */
+       if (ds->fn(current, ds->cb_data)) {
+               /*
+                * if both current and candidate satisfy fn, we cannot
+                * disambiguate.
+                */
+               ds->candidate_ok = 0;
+               ds->ambiguous = 1;
+       }
+
+       /* otherwise, current can be discarded and candidate is still good */
+}
+
+static void find_short_object_filename(int len, const char *hex_pfx, struct disambiguate_state *ds)
 {
        struct alternate_object_database *alt;
        char hex[40];
-       int found = 0;
        static struct alternate_object_database *fakeent;
 
        if (!fakeent) {
+               /*
+                * Create a "fake" alternate object database that
+                * points to our own object database, to make it
+                * easier to get a temporary working space in
+                * alt->name/alt->base while iterating over the
+                * object databases including our own.
+                */
                const char *objdir = get_object_directory();
                int objdir_len = strlen(objdir);
                int entlen = objdir_len + 43;
@@ -27,33 +95,28 @@ static int find_short_object_filename(int len, const char *name, unsigned char *
        }
        fakeent->next = alt_odb_list;
 
-       sprintf(hex, "%.2s", name);
-       for (alt = fakeent; alt && found < 2; alt = alt->next) {
+       sprintf(hex, "%.2s", hex_pfx);
+       for (alt = fakeent; alt && !ds->ambiguous; alt = alt->next) {
                struct dirent *de;
                DIR *dir;
-               sprintf(alt->name, "%.2s/", name);
+               sprintf(alt->name, "%.2s/", hex_pfx);
                dir = opendir(alt->base);
                if (!dir)
                        continue;
-               while ((de = readdir(dir)) != NULL) {
+
+               while (!ds->ambiguous && (de = readdir(dir)) != NULL) {
+                       unsigned char sha1[20];
+
                        if (strlen(de->d_name) != 38)
                                continue;
-                       if (memcmp(de->d_name, name + 2, len - 2))
+                       if (memcmp(de->d_name, hex_pfx + 2, len - 2))
                                continue;
-                       if (!found) {
-                               memcpy(hex + 2, de->d_name, 38);
-                               found++;
-                       }
-                       else if (memcmp(hex + 2, de->d_name, 38)) {
-                               found = 2;
-                               break;
-                       }
+                       memcpy(hex + 2, de->d_name, 38);
+                       if (!get_sha1_hex(hex, sha1))
+                               update_candidates(ds, sha1);
                }
                closedir(dir);
        }
-       if (found == 1)
-               return get_sha1_hex(hex, sha1) == 0;
-       return found;
 }
 
 static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
@@ -71,103 +134,157 @@ static int match_sha(unsigned len, const unsigned char *a, const unsigned char *
        return 1;
 }
 
-static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
+static void unique_in_pack(int len,
+                         const unsigned char *bin_pfx,
+                          struct packed_git *p,
+                          struct disambiguate_state *ds)
 {
-       struct packed_git *p;
-       const unsigned char *found_sha1 = NULL;
-       int found = 0;
-
-       prepare_packed_git();
-       for (p = packed_git; p && found < 2; p = p->next) {
-               uint32_t num, last;
-               uint32_t first = 0;
-               open_pack_index(p);
-               num = p->num_objects;
-               last = num;
-               while (first < last) {
-                       uint32_t mid = (first + last) / 2;
-                       const unsigned char *now;
-                       int cmp;
-
-                       now = nth_packed_object_sha1(p, mid);
-                       cmp = hashcmp(match, now);
-                       if (!cmp) {
-                               first = mid;
-                               break;
-                       }
-                       if (cmp > 0) {
-                               first = mid+1;
-                               continue;
-                       }
-                       last = mid;
+       uint32_t num, last, i, first = 0;
+       const unsigned char *current = NULL;
+
+       open_pack_index(p);
+       num = p->num_objects;
+       last = num;
+       while (first < last) {
+               uint32_t mid = (first + last) / 2;
+               const unsigned char *current;
+               int cmp;
+
+               current = nth_packed_object_sha1(p, mid);
+               cmp = hashcmp(bin_pfx, current);
+               if (!cmp) {
+                       first = mid;
+                       break;
                }
-               if (first < num) {
-                       const unsigned char *now, *next;
-                      now = nth_packed_object_sha1(p, first);
-                       if (match_sha(len, match, now)) {
-                               next = nth_packed_object_sha1(p, first+1);
-                              if (!next|| !match_sha(len, match, next)) {
-                                       /* unique within this pack */
-                                       if (!found) {
-                                               found_sha1 = now;
-                                               found++;
-                                       }
-                                       else if (hashcmp(found_sha1, now)) {
-                                               found = 2;
-                                               break;
-                                       }
-                               }
-                               else {
-                                       /* not even unique within this pack */
-                                       found = 2;
-                                       break;
-                               }
-                       }
+               if (cmp > 0) {
+                       first = mid+1;
+                       continue;
                }
+               last = mid;
+       }
+
+       /*
+        * At this point, "first" is the location of the lowest object
+        * with an object name that could match "bin_pfx".  See if we have
+        * 0, 1 or more objects that actually match(es).
+        */
+       for (i = first; i < num && !ds->ambiguous; i++) {
+               current = nth_packed_object_sha1(p, i);
+               if (!match_sha(len, bin_pfx, current))
+                       break;
+               update_candidates(ds, current);
        }
-       if (found == 1)
-               hashcpy(sha1, found_sha1);
-       return found;
+}
+
+static void find_short_packed_object(int len, const unsigned char *bin_pfx,
+                                    struct disambiguate_state *ds)
+{
+       struct packed_git *p;
+
+       prepare_packed_git();
+       for (p = packed_git; p && !ds->ambiguous; p = p->next)
+               unique_in_pack(len, bin_pfx, p, ds);
 }
 
 #define SHORT_NAME_NOT_FOUND (-1)
 #define SHORT_NAME_AMBIGUOUS (-2)
 
-static int find_unique_short_object(int len, char *canonical,
-                                   unsigned char *res, unsigned char *sha1)
+static int finish_object_disambiguation(struct disambiguate_state *ds,
+                                       unsigned char *sha1)
 {
-       int has_unpacked, has_packed;
-       unsigned char unpacked_sha1[20], packed_sha1[20];
+       if (ds->ambiguous)
+               return SHORT_NAME_AMBIGUOUS;
 
-       prepare_alt_odb();
-       has_unpacked = find_short_object_filename(len, canonical, unpacked_sha1);
-       has_packed = find_short_packed_object(len, res, packed_sha1);
-       if (!has_unpacked && !has_packed)
+       if (!ds->candidate_exists)
                return SHORT_NAME_NOT_FOUND;
-       if (1 < has_unpacked || 1 < has_packed)
+
+       if (!ds->candidate_checked)
+               /*
+                * If this is the only candidate, there is no point
+                * calling the disambiguation hint callback.
+                *
+                * On the other hand, if the current candidate
+                * replaced an earlier candidate that did _not_ pass
+                * the disambiguation hint callback, then we do have
+                * more than one objects that match the short name
+                * given, so we should make sure this one matches;
+                * otherwise, if we discovered this one and the one
+                * that we previously discarded in the reverse order,
+                * we would end up showing different results in the
+                * same repository!
+                */
+               ds->candidate_ok = (!ds->disambiguate_fn_used ||
+                                   ds->fn(ds->candidate, ds->cb_data));
+
+       if (!ds->candidate_ok)
                return SHORT_NAME_AMBIGUOUS;
-       if (has_unpacked != has_packed) {
-               hashcpy(sha1, (has_packed ? packed_sha1 : unpacked_sha1));
+
+       hashcpy(sha1, ds->candidate);
+       return 0;
+}
+
+static int disambiguate_commit_only(const unsigned char *sha1, void *cb_data_unused)
+{
+       int kind = sha1_object_info(sha1, NULL);
+       return kind == OBJ_COMMIT;
+}
+
+static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused)
+{
+       struct object *obj;
+       int kind;
+
+       kind = sha1_object_info(sha1, NULL);
+       if (kind == OBJ_COMMIT)
+               return 1;
+       if (kind != OBJ_TAG)
                return 0;
-       }
-       /* Both have unique ones -- do they match? */
-       if (hashcmp(packed_sha1, unpacked_sha1))
-               return SHORT_NAME_AMBIGUOUS;
-       hashcpy(sha1, packed_sha1);
+
+       /* We need to do this the hard way... */
+       obj = deref_tag(lookup_object(sha1), NULL, 0);
+       if (obj && obj->type == OBJ_COMMIT)
+               return 1;
        return 0;
 }
 
-static int get_short_sha1(const char *name, int len, unsigned char *sha1,
-                         int quietly)
+static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
 {
-       int i, status;
-       char canonical[40];
-       unsigned char res[20];
+       int kind = sha1_object_info(sha1, NULL);
+       return kind == OBJ_TREE;
+}
 
-       if (len < MINIMUM_ABBREV || len > 40)
-               return -1;
-       hashclr(res);
-       memset(canonical, 'x', 40);
+static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused)
+{
+       struct object *obj;
+       int kind;
+
+       kind = sha1_object_info(sha1, NULL);
+       if (kind == OBJ_TREE || kind == OBJ_COMMIT)
+               return 1;
+       if (kind != OBJ_TAG)
+               return 0;
+
+       /* We need to do this the hard way... */
+       obj = deref_tag(lookup_object(sha1), NULL, 0);
+       if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
+               return 1;
+       return 0;
+}
+
+static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unused)
+{
+       int kind = sha1_object_info(sha1, NULL);
+       return kind == OBJ_BLOB;
+}
+
+static int prepare_prefixes(const char *name, int len,
+                           unsigned char *bin_pfx,
+                           char *hex_pfx)
+{
+       int i;
+
+       hashclr(bin_pfx);
+       memset(hex_pfx, 'x', 40);
        for (i = 0; i < len ;i++) {
                unsigned char c = name[i];
                unsigned char val;
@@ -181,18 +298,76 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
                }
                else
                        return -1;
-               canonical[i] = c;
+               hex_pfx[i] = c;
                if (!(i & 1))
                        val <<= 4;
-               res[i >> 1] |= val;
+               bin_pfx[i >> 1] |= val;
        }
+       return 0;
+}
+
+static int get_short_sha1(const char *name, int len, unsigned char *sha1,
+                         unsigned flags)
+{
+       int status;
+       char hex_pfx[40];
+       unsigned char bin_pfx[20];
+       struct disambiguate_state ds;
+       int quietly = !!(flags & GET_SHA1_QUIETLY);
+
+       if (len < MINIMUM_ABBREV || len > 40)
+               return -1;
+       if (prepare_prefixes(name, len, bin_pfx, hex_pfx) < 0)
+               return -1;
+
+       prepare_alt_odb();
+
+       memset(&ds, 0, sizeof(ds));
+       if (flags & GET_SHA1_COMMIT)
+               ds.fn = disambiguate_commit_only;
+       else if (flags & GET_SHA1_COMMITTISH)
+               ds.fn = disambiguate_committish_only;
+       else if (flags & GET_SHA1_TREE)
+               ds.fn = disambiguate_tree_only;
+       else if (flags & GET_SHA1_TREEISH)
+               ds.fn = disambiguate_treeish_only;
+       else if (flags & GET_SHA1_BLOB)
+               ds.fn = disambiguate_blob_only;
+
+       find_short_object_filename(len, hex_pfx, &ds);
+       find_short_packed_object(len, bin_pfx, &ds);
+       status = finish_object_disambiguation(&ds, sha1);
 
-       status = find_unique_short_object(i, canonical, res, sha1);
        if (!quietly && (status == SHORT_NAME_AMBIGUOUS))
-               return error("short SHA1 %.*s is ambiguous.", len, canonical);
+               return error("short SHA1 %.*s is ambiguous.", len, hex_pfx);
        return status;
 }
 
+
+int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
+{
+       char hex_pfx[40];
+       unsigned char bin_pfx[20];
+       struct disambiguate_state ds;
+       int len = strlen(prefix);
+
+       if (len < MINIMUM_ABBREV || len > 40)
+               return -1;
+       if (prepare_prefixes(prefix, len, bin_pfx, hex_pfx) < 0)
+               return -1;
+
+       prepare_alt_odb();
+
+       memset(&ds, 0, sizeof(ds));
+       ds.always_call_fn = 1;
+       ds.cb_data = cb_data;
+       ds.fn = fn;
+
+       find_short_object_filename(len, hex_pfx, &ds);
+       find_short_packed_object(len, bin_pfx, &ds);
+       return ds.ambiguous;
+}
+
 const char *find_unique_abbrev(const unsigned char *sha1, int len)
 {
        int status, exists;
@@ -204,7 +379,7 @@ const char *find_unique_abbrev(const unsigned char *sha1, int len)
                return hex;
        while (len < 40) {
                unsigned char sha1_ret[20];
-               status = get_short_sha1(hex, len, sha1_ret, 1);
+               status = get_short_sha1(hex, len, sha1_ret, GET_SHA1_QUIETLY);
                if (exists
                    ? !status
                    : status == SHORT_NAME_NOT_FOUND) {
@@ -255,7 +430,7 @@ static inline int upstream_mark(const char *string, int len)
        return 0;
 }
 
-static int get_sha1_1(const char *name, int len, unsigned char *sha1);
+static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags);
 
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
@@ -292,7 +467,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                ret = interpret_branch_name(str+at, &buf);
                if (ret > 0) {
                        /* substitute this branch name and restart */
-                       return get_sha1_1(buf.buf, buf.len, sha1);
+                       return get_sha1_1(buf.buf, buf.len, sha1, 0);
                } else if (ret == 0) {
                        return -1;
                }
@@ -362,7 +537,7 @@ static int get_parent(const char *name, int len,
                      unsigned char *result, int idx)
 {
        unsigned char sha1[20];
-       int ret = get_sha1_1(name, len, sha1);
+       int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
        struct commit *commit;
        struct commit_list *p;
 
@@ -395,7 +570,7 @@ static int get_nth_ancestor(const char *name, int len,
        struct commit *commit;
        int ret;
 
-       ret = get_sha1_1(name, len, sha1);
+       ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
        if (ret)
                return ret;
        commit = lookup_commit_reference(sha1);
@@ -441,6 +616,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
        unsigned char outer[20];
        const char *sp;
        unsigned int expected_type = 0;
+       unsigned lookup_flags = 0;
        struct object *o;
 
        /*
@@ -476,7 +652,10 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
        else
                return -1;
 
-       if (get_sha1_1(name, sp - name - 2, outer))
+       if (expected_type == OBJ_COMMIT)
+               lookup_flags = GET_SHA1_COMMITTISH;
+
+       if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
                return -1;
 
        o = parse_object(outer);
@@ -525,6 +704,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
 static int get_describe_name(const char *name, int len, unsigned char *sha1)
 {
        const char *cp;
+       unsigned flags = GET_SHA1_QUIETLY | GET_SHA1_COMMIT;
 
        for (cp = name + len - 1; name + 2 <= cp; cp--) {
                char ch = *cp;
@@ -535,14 +715,14 @@ static int get_describe_name(const char *name, int len, unsigned char *sha1)
                        if (ch == 'g' && cp[-1] == '-') {
                                cp++;
                                len -= cp - name;
-                               return get_short_sha1(cp, len, sha1, 1);
+                               return get_short_sha1(cp, len, sha1, flags);
                        }
                }
        }
        return -1;
 }
 
-static int get_sha1_1(const char *name, int len, unsigned char *sha1)
+static int get_sha1_1(const char *name, int len, unsigned char *sha1, unsigned lookup_flags)
 {
        int ret, has_suffix;
        const char *cp;
@@ -587,7 +767,7 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
        if (!ret)
                return 0;
 
-       return get_short_sha1(name, len, sha1, 0);
+       return get_short_sha1(name, len, sha1, lookup_flags);
 }
 
 /*
@@ -769,7 +949,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
                struct strbuf sb;
                strbuf_init(&sb, dots - name);
                strbuf_add(&sb, name, dots - name);
-               st = get_sha1(sb.buf, sha1_tmp);
+               st = get_sha1_committish(sb.buf, sha1_tmp);
                strbuf_release(&sb);
        }
        if (st)
@@ -778,7 +958,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
        if (!one)
                return -1;
 
-       if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
+       if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
                return -1;
        two = lookup_commit_reference_gently(sha1_tmp, 0);
        if (!two)
@@ -905,7 +1085,52 @@ int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
 int get_sha1(const char *name, unsigned char *sha1)
 {
        struct object_context unused;
-       return get_sha1_with_context(name, sha1, &unused);
+       return get_sha1_with_context(name, 0, sha1, &unused);
+}
+
+/*
+ * Many callers know that the user meant to name a committish by
+ * syntactical positions where the object name appears.  Calling this
+ * function allows the machinery to disambiguate shorter-than-unique
+ * abbreviated object names between committish and others.
+ *
+ * Note that this does NOT error out when the named object is not a
+ * committish. It is merely to give a hint to the disambiguation
+ * machinery.
+ */
+int get_sha1_committish(const char *name, unsigned char *sha1)
+{
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_COMMITTISH,
+                                    sha1, &unused);
+}
+
+int get_sha1_treeish(const char *name, unsigned char *sha1)
+{
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_TREEISH,
+                                    sha1, &unused);
+}
+
+int get_sha1_commit(const char *name, unsigned char *sha1)
+{
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_COMMIT,
+                                    sha1, &unused);
+}
+
+int get_sha1_tree(const char *name, unsigned char *sha1)
+{
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_TREE,
+                                    sha1, &unused);
+}
+
+int get_sha1_blob(const char *name, unsigned char *sha1)
+{
+       struct object_context unused;
+       return get_sha1_with_context(name, GET_SHA1_BLOB,
+                                    sha1, &unused);
 }
 
 /* Must be called only when object_name:filename doesn't exist. */
@@ -1004,16 +1229,6 @@ static void diagnose_invalid_index_path(int stage,
 }
 
 
-int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
-                        int only_to_die, const char *prefix)
-{
-       struct object_context oc;
-       int ret;
-       ret = get_sha1_with_context_1(name, sha1, &oc, only_to_die, prefix);
-       *mode = oc.mode;
-       return ret;
-}
-
 static char *resolve_relative_path(const char *rel)
 {
        if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
@@ -1031,20 +1246,24 @@ static char *resolve_relative_path(const char *rel)
                           rel);
 }
 
-int get_sha1_with_context_1(const char *name, unsigned char *sha1,
-                           struct object_context *oc,
-                           int only_to_die, const char *prefix)
+static int get_sha1_with_context_1(const char *name,
+                                  unsigned flags,
+                                  const char *prefix,
+                                  unsigned char *sha1,
+                                  struct object_context *oc)
 {
        int ret, bracket_depth;
        int namelen = strlen(name);
        const char *cp;
+       int only_to_die = flags & GET_SHA1_ONLY_TO_DIE;
 
        memset(oc, 0, sizeof(*oc));
        oc->mode = S_IFINVALID;
-       ret = get_sha1_1(name, namelen, sha1);
+       ret = get_sha1_1(name, namelen, sha1, flags);
        if (!ret)
                return ret;
-       /* sha1:path --> object name of path in ent sha1
+       /*
+        * sha1:path --> object name of path in ent sha1
         * :path -> object name of absolute path in index
         * :./path -> object name of path relative to cwd in index
         * :[0-3]:path -> object name of path in index at stage
@@ -1119,7 +1338,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
                        strncpy(object_name, name, cp-name);
                        object_name[cp-name] = '\0';
                }
-               if (!get_sha1_1(name, cp-name, tree_sha1)) {
+               if (!get_sha1_1(name, cp-name, tree_sha1, GET_SHA1_TREEISH)) {
                        const char *filename = cp+1;
                        char *new_filename = NULL;
 
@@ -1146,3 +1365,22 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
        }
        return ret;
 }
+
+/*
+ * Call this function when you know "name" given by the end user must
+ * name an object but it doesn't; the function _may_ die with a better
+ * diagnostic message than "no such object 'name'", e.g. "Path 'doc' does not
+ * exist in 'HEAD'" when given "HEAD:doc", or it may return in which case
+ * you have a chance to diagnose the error further.
+ */
+void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
+{
+       struct object_context oc;
+       unsigned char sha1[20];
+       get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
+}
+
+int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
+{
+       return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
+}
diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh
new file mode 100755 (executable)
index 0000000..6b3d797
--- /dev/null
@@ -0,0 +1,264 @@
+#!/bin/sh
+
+test_description='object name disambiguation
+
+Create blobs, trees, commits and a tag that all share the same
+prefix, and make sure "git rev-parse" can take advantage of
+type information to disambiguate short object names that are
+not necessarily unique.
+
+The final history used in the test has five commits, with the bottom
+one tagged as v1.0.0.  They all have one regular file each.
+
+  +-------------------------------------------+
+  |                                           |
+  |           .-------b3wettvi---- ad2uee     |
+  |          /                   /            |
+  |  a2onsxbvj---czy8f73t--ioiley5o           |
+  |                                           |
+  +-------------------------------------------+
+
+'
+
+. ./test-lib.sh
+
+test_expect_success 'blob and tree' '
+       test_tick &&
+       (
+               for i in 0 1 2 3 4 5 6 7 8 9
+               do
+                       echo $i
+               done
+               echo
+               echo b1rwzyc3
+       ) >a0blgqsjc &&
+
+       # create one blob 0000000000b36
+       git add a0blgqsjc &&
+
+       # create one tree 0000000000cdc
+       git write-tree
+'
+
+test_expect_success 'warn ambiguity when no candidate matches type hint' '
+       test_must_fail git rev-parse --verify 000000000^{commit} 2>actual &&
+       grep "short SHA1 000000000 is ambiguous" actual
+'
+
+test_expect_success 'disambiguate tree-ish' '
+       # feed tree-ish in an unambiguous way
+       git rev-parse --verify 0000000000cdc:a0blgqsjc &&
+
+       # ambiguous at the object name level, but there is only one
+       # such tree-ish (the other is a blob)
+       git rev-parse --verify 000000000:a0blgqsjc
+'
+
+test_expect_success 'disambiguate blob' '
+       sed -e "s/|$//" >patch <<-EOF &&
+       diff --git a/frotz b/frotz
+       index 000000000..ffffff 100644
+       --- a/frotz
+       +++ b/frotz
+       @@ -10,3 +10,4 @@
+        9
+        |
+        b1rwzyc3
+       +irwry
+       EOF
+       (
+               GIT_INDEX_FILE=frotz &&
+               export GIT_INDEX_FILE &&
+               git apply --build-fake-ancestor frotz patch &&
+               git cat-file blob :frotz >actual
+       ) &&
+       test_cmp a0blgqsjc actual
+'
+
+test_expect_success 'disambiguate tree' '
+       commit=$(echo "d7xm" | git commit-tree 000000000) &&
+       test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc)
+'
+
+test_expect_success 'first commit' '
+       # create one commit 0000000000e4f
+       git commit -m a2onsxbvj
+'
+
+test_expect_success 'disambiguate commit-ish' '
+       # feed commit-ish in an unambiguous way
+       git rev-parse --verify 0000000000e4f^{commit} &&
+
+       # ambiguous at the object name level, but there is only one
+       # such commit (the others are tree and blob)
+       git rev-parse --verify 000000000^{commit} &&
+
+       # likewise
+       git rev-parse --verify 000000000^0
+'
+
+test_expect_success 'disambiguate commit' '
+       commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) &&
+       test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f)
+'
+
+test_expect_success 'log name1..name2 takes only commit-ishes on both ends' '
+       git log 000000000..000000000 &&
+       git log ..000000000 &&
+       git log 000000000.. &&
+       git log 000000000...000000000 &&
+       git log ...000000000 &&
+       git log 000000000...
+'
+
+test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' '
+       git rev-parse 000000000..000000000 &&
+       git rev-parse ..000000000 &&
+       git rev-parse 000000000..
+'
+
+test_expect_success 'git log takes only commit-ish' '
+       git log 000000000
+'
+
+test_expect_success 'git reset takes only commit-ish' '
+       git reset 000000000
+'
+
+test_expect_success 'first tag' '
+       # create one tag 0000000000f8f
+       git tag -a -m j7cp83um v1.0.0
+'
+
+test_expect_failure 'two semi-ambiguous commit-ish' '
+       # Once the parser becomes ultra-smart, it could notice that
+       # 110282 before ^{commit} name many different objects, but
+       # that only two (HEAD and v1.0.0 tag) can be peeled to commit,
+       # and that peeling them down to commit yield the same commit
+       # without ambiguity.
+       git rev-parse --verify 110282^{commit} &&
+
+       # likewise
+       git log 000000000..000000000 &&
+       git log ..000000000 &&
+       git log 000000000.. &&
+       git log 000000000...000000000 &&
+       git log ...000000000 &&
+       git log 000000000...
+'
+
+test_expect_failure 'three semi-ambiguous tree-ish' '
+       # Likewise for tree-ish.  HEAD, v1.0.0 and HEAD^{tree} share
+       # the prefix but peeling them to tree yields the same thing
+       git rev-parse --verify 000000000^{tree}
+'
+
+test_expect_success 'parse describe name' '
+       # feed an unambiguous describe name
+       git rev-parse --verify v1.0.0-0-g0000000000e4f &&
+
+       # ambiguous at the object name level, but there is only one
+       # such commit (others are blob, tree and tag)
+       git rev-parse --verify v1.0.0-0-g000000000
+'
+
+test_expect_success 'more history' '
+       # commit 0000000000043
+       git mv a0blgqsjc d12cr3h8t &&
+       echo h62xsjeu >>d12cr3h8t &&
+       git add d12cr3h8t &&
+
+       test_tick &&
+       git commit -m czy8f73t &&
+
+       # commit 00000000008ec
+       git mv d12cr3h8t j000jmpzn &&
+       echo j08bekfvt >>j000jmpzn &&
+       git add j000jmpzn &&
+
+       test_tick &&
+       git commit -m ioiley5o &&
+
+       # commit 0000000005b0
+       git checkout v1.0.0^0 &&
+       git mv a0blgqsjc f5518nwu &&
+
+       for i in h62xsjeu j08bekfvt kg7xflhm
+       do
+               echo $i
+       done >>f5518nwu &&
+       git add f5518nwu &&
+
+       test_tick &&
+       git commit -m b3wettvi &&
+       side=$(git rev-parse HEAD) &&
+
+       # commit 000000000066
+       git checkout master &&
+
+       # If you use recursive, merge will fail and you will need to
+       # clean up a0blgqsjc as well.  If you use resolve, merge will
+       # succeed.
+       test_might_fail git merge --no-commit -s recursive $side &&
+       git rm -f f5518nwu j000jmpzn &&
+
+       test_might_fail git rm -f a0blgqsjc &&
+       (
+               git cat-file blob $side:f5518nwu
+               echo j3l0i9s6
+       ) >ab2gs879 &&
+       git add ab2gs879 &&
+
+       test_tick &&
+       git commit -m ad2uee
+
+'
+
+test_expect_failure 'parse describe name taking advantage of generation' '
+       # ambiguous at the object name level, but there is only one
+       # such commit at generation 0
+       git rev-parse --verify v1.0.0-0-g000000000 &&
+
+       # likewise for generation 2 and 4
+       git rev-parse --verify v1.0.0-2-g000000000 &&
+       git rev-parse --verify v1.0.0-4-g000000000
+'
+
+# Note: because rev-parse does not even try to disambiguate based on
+# the generation number, this test currently succeeds for a wrong
+# reason.  When it learns to use the generation number, the previous
+# test should succeed, and also this test should fail because the
+# describe name used in the test with generation number can name two
+# commits.  Make sure that such a future enhancement does not randomly
+# pick one.
+test_expect_success 'parse describe name not ignoring ambiguity' '
+       # ambiguous at the object name level, and there are two such
+       # commits at generation 1
+       test_must_fail git rev-parse --verify v1.0.0-1-g000000000
+'
+
+test_expect_success 'ambiguous commit-ish' '
+       # Now there are many commits that begin with the
+       # common prefix, none of these should pick one at
+       # random.  They all should result in ambiguity errors.
+       test_must_fail git rev-parse --verify 110282^{commit} &&
+
+       # likewise
+       test_must_fail git log 000000000..000000000 &&
+       test_must_fail git log ..000000000 &&
+       test_must_fail git log 000000000.. &&
+       test_must_fail git log 000000000...000000000 &&
+       test_must_fail git log ...000000000 &&
+       test_must_fail git log 000000000...
+'
+
+test_expect_success 'rev-parse --disambiguate' '
+       # The test creates 16 objects that share the prefix and two
+       # commits created by commit-tree in earlier tests share a
+       # different prefix.
+       git rev-parse --disambiguate=000000000 >actual &&
+       test $(wc -l <actual) = 16 &&
+       test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000
+'
+
+test_done
diff --git a/t/t3006-ls-files-long.sh b/t/t3006-ls-files-long.sh
new file mode 100755 (executable)
index 0000000..202ad65
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='overly long paths'
+. ./test-lib.sh
+
+test_expect_success setup '
+       p=filefilefilefilefilefilefilefile &&
+       p=$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p &&
+       p=$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p &&
+
+       path_a=${p}_a &&
+       path_z=${p}_z &&
+
+       blob_a=$(echo frotz | git hash-object -w --stdin) &&
+       blob_z=$(echo nitfol | git hash-object -w --stdin) &&
+
+       pat="100644 %s 0\t%s\n"
+'
+
+test_expect_success 'overly-long path by itself is not a problem' '
+       printf "$pat" "$blob_a" "$path_a" |
+       git update-index --add --index-info &&
+       echo "$path_a" >expect &&
+       git ls-files >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'overly-long path does not replace another by mistake' '
+       printf "$pat" "$blob_a" "$path_a" "$blob_z" "$path_z" |
+       git update-index --add --index-info &&
+       (
+               echo "$path_a"
+               echo "$path_z"
+       ) >expect &&
+       git ls-files >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 68d61480fbeee024ca463ca0d5771bdfdf980866..8078db6856ba4428e9d6b13f5e02076da7963b09 100755 (executable)
@@ -872,4 +872,35 @@ test_expect_success 'rebase -i --exec without <CMD>' '
        git checkout master
 '
 
+test_expect_success 'rebase -i --root re-order and drop commits' '
+       git checkout E &&
+       FAKE_LINES="3 1 2 5" git rebase -i --root &&
+       test E = $(git cat-file commit HEAD | sed -ne \$p) &&
+       test B = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+       test A = $(git cat-file commit HEAD^^ | sed -ne \$p) &&
+       test C = $(git cat-file commit HEAD^^^ | sed -ne \$p) &&
+       test 0 = $(git cat-file commit HEAD^^^ | grep -c ^parent\ )
+'
+
+test_expect_success 'rebase -i --root retain root commit author and message' '
+       git checkout A &&
+       echo B >file7 &&
+       git add file7 &&
+       GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
+       FAKE_LINES="2" git rebase -i --root &&
+       git cat-file commit HEAD | grep -q "^author Twerp Snog" &&
+       git cat-file commit HEAD | grep -q "^different author$"
+'
+
+test_expect_success 'rebase -i --root temporary sentinel commit' '
+       git checkout B &&
+       (
+               FAKE_LINES="2" &&
+               export FAKE_LINES &&
+               test_must_fail git rebase -i --root
+       ) &&
+       git cat-file commit HEAD | grep "^tree 4b825dc642cb" &&
+       git rebase --abort
+'
+
 test_done
index 1e9d1a737c5369d1b41bfa75f5939ad41004b944..0b521057283bf106da8ce55f25f61c7ec7e3ad35 100755 (executable)
@@ -22,11 +22,6 @@ test_expect_success 'prepare repository' '
        test_commit 4 B
 '
 
-test_expect_success 'rebase --root expects --onto' '
-       git checkout -B fail other &&
-       test_must_fail git rebase --root
-'
-
 test_expect_success 'rebase --root fails with too many args' '
        git checkout -B fail other &&
        test_must_fail git rebase --onto master --root fail fail
index 6cebb3951bc7f4e9b772efed052c485d82d1a68d..ec4deea1923919a88c6f1f9d35e11cb7737e323d 100755 (executable)
@@ -15,13 +15,14 @@ cat >expect.binary-numstat <<\EOF
 -      -       d
 EOF
 
-test_expect_success 'prepare repository' \
-       'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
-        git update-index --add a b c d &&
-        echo git >a &&
-        cat "$TEST_DIRECTORY"/test-binary-1.png >b &&
-        echo git >c &&
-        cat b b >d'
+test_expect_success 'prepare repository' '
+       echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
+       git update-index --add a b c d &&
+       echo git >a &&
+       cat "$TEST_DIRECTORY"/test-binary-1.png >b &&
+       echo git >c &&
+       cat b b >d
+'
 
 cat > expected <<\EOF
  a |    2 +-
@@ -30,16 +31,16 @@ cat > expected <<\EOF
  d |  Bin
  4 files changed, 2 insertions(+), 2 deletions(-)
 EOF
-test_expect_success '"apply --stat" output for binary file change' '
+test_expect_success 'apply --stat output for binary file change' '
        git diff >diff &&
        git apply --stat --summary <diff >current &&
        test_i18ncmp expected current
 '
 
 test_expect_success 'diff --shortstat output for binary file change' '
-       echo " 4 files changed, 2 insertions(+), 2 deletions(-)" >expected &&
+       tail -n 1 expected >expect &&
        git diff --shortstat >current &&
-       test_i18ncmp expected current
+       test_i18ncmp expect current
 '
 
 test_expect_success 'diff --shortstat output for binary file change only' '
@@ -62,49 +63,42 @@ test_expect_success 'apply --numstat understands diff --binary format' '
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
-test_expect_success 'apply detecting corrupt patch correctly' \
-       'git diff | sed -e 's/-CIT/xCIT/' >broken &&
-        if git apply --stat --summary broken 2>detected
-        then
-               echo unhappy - should have detected an error
-               (exit 1)
-        else
-               echo happy
-        fi &&
-        detected=`cat detected` &&
-        detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
-        detected=`sed -ne "${detected}p" broken` &&
-        test "$detected" = xCIT'
-
-test_expect_success 'apply detecting corrupt patch correctly' \
-       'git diff --binary | sed -e 's/-CIT/xCIT/' >broken &&
-        if git apply --stat --summary broken 2>detected
-        then
-               echo unhappy - should have detected an error
-               (exit 1)
-        else
-               echo happy
-        fi &&
-        detected=`cat detected` &&
-        detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
-        detected=`sed -ne "${detected}p" broken` &&
-        test "$detected" = xCIT'
+test_expect_success 'apply detecting corrupt patch correctly' '
+       git diff >output &&
+       sed -e "s/-CIT/xCIT/" <output >broken &&
+       test_must_fail git apply --stat --summary broken 2>detected &&
+       detected=`cat detected` &&
+       detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
+       detected=`sed -ne "${detected}p" broken` &&
+       test "$detected" = xCIT
+'
+
+test_expect_success 'apply detecting corrupt patch correctly' '
+       git diff --binary | sed -e "s/-CIT/xCIT/" >broken &&
+       test_must_fail git apply --stat --summary broken 2>detected &&
+       detected=`cat detected` &&
+       detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
+       detected=`sed -ne "${detected}p" broken` &&
+       test "$detected" = xCIT
+'
 
 test_expect_success 'initial commit' 'git commit -a -m initial'
 
 # Try removal (b), modification (d), and creation (e).
-test_expect_success 'diff-index with --binary' \
-       'echo AIT >a && mv b e && echo CIT >c && cat e >d &&
-        git update-index --add --remove a b c d e &&
-        tree0=`git write-tree` &&
-        git diff --cached --binary >current &&
-        git apply --stat --summary current'
-
-test_expect_success 'apply binary patch' \
-       'git reset --hard &&
-        git apply --binary --index <current &&
-        tree1=`git write-tree` &&
-        test "$tree1" = "$tree0"'
+test_expect_success 'diff-index with --binary' '
+       echo AIT >a && mv b e && echo CIT >c && cat e >d &&
+       git update-index --add --remove a b c d e &&
+       tree0=`git write-tree` &&
+       git diff --cached --binary >current &&
+       git apply --stat --summary current
+'
+
+test_expect_success 'apply binary patch' '
+       git reset --hard &&
+       git apply --binary --index <current &&
+       tree1=`git write-tree` &&
+       test "$tree1" = "$tree0"
+'
 
 test_expect_success 'diff --no-index with binary creation' '
        echo Q | q_to_nul >binary &&
@@ -125,7 +119,7 @@ cat >expect <<EOF
 EOF
 
 test_expect_success 'diff --stat with binary files and big change count' '
-       echo X | dd of=binfile bs=1k seek=1 &&
+       printf "\01\00%1024d" 1 >binfile &&
        git add binfile &&
        i=0 &&
        while test $i -lt 10000; do
diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh
new file mode 100755 (executable)
index 0000000..fa5d4ef
--- /dev/null
@@ -0,0 +1,157 @@
+#!/bin/sh
+
+test_description='git apply --3way'
+
+. ./test-lib.sh
+
+create_file () {
+       for i
+       do
+               echo "$i"
+       done
+}
+
+sanitize_conflicted_diff () {
+       sed -e '
+               /^index /d
+               s/^\(+[<>][<>][<>][<>]*\) .*/\1/
+       '
+}
+
+test_expect_success setup '
+       test_tick &&
+       create_file >one 1 2 3 4 5 6 7 &&
+       cat one >two &&
+       git add one two &&
+       git commit -m initial &&
+
+       git branch side &&
+
+       test_tick &&
+       create_file >one 1 two 3 4 5 six 7 &&
+       create_file >two 1 two 3 4 5 6 7 &&
+       git commit -a -m master &&
+
+       git checkout side &&
+       create_file >one 1 2 3 4 five 6 7 &&
+       create_file >two 1 2 3 4 five 6 7 &&
+       git commit -a -m side &&
+
+       git checkout master
+'
+
+test_expect_success 'apply without --3way' '
+       git diff side^ side >P.diff &&
+
+       # should fail to apply
+       git reset --hard &&
+       git checkout master^0 &&
+       test_must_fail git apply --index P.diff &&
+       # should leave things intact
+       git diff-files --exit-code &&
+       git diff-index --exit-code --cached HEAD
+'
+
+test_expect_success 'apply with --3way' '
+       # Merging side should be similar to applying this patch
+       git diff ...side >P.diff &&
+
+       # The corresponding conflicted merge
+       git reset --hard &&
+       git checkout master^0 &&
+       test_must_fail git merge --no-commit side &&
+       git ls-files -s >expect.ls &&
+       git diff HEAD | sanitize_conflicted_diff >expect.diff &&
+
+       # should fail to apply
+       git reset --hard &&
+       git checkout master^0 &&
+       test_must_fail git apply --index --3way P.diff &&
+       git ls-files -s >actual.ls &&
+       git diff HEAD | sanitize_conflicted_diff >actual.diff &&
+
+       # The result should resemble the corresponding merge
+       test_cmp expect.ls actual.ls &&
+       test_cmp expect.diff actual.diff
+'
+
+test_expect_success 'apply with --3way with rerere enabled' '
+       git config rerere.enabled true &&
+
+       # Merging side should be similar to applying this patch
+       git diff ...side >P.diff &&
+
+       # The corresponding conflicted merge
+       git reset --hard &&
+       git checkout master^0 &&
+       test_must_fail git merge --no-commit side &&
+
+       # Manually resolve and record the resolution
+       create_file 1 two 3 4 five six 7 >one &&
+       git rerere &&
+       cat one >expect &&
+
+       # should fail to apply
+       git reset --hard &&
+       git checkout master^0 &&
+       test_must_fail git apply --index --3way P.diff &&
+
+       # but rerere should have replayed the recorded resolution
+       test_cmp expect one
+'
+
+test_expect_success 'apply -3 with add/add conflict setup' '
+       git reset --hard &&
+
+       git checkout -b adder &&
+       create_file 1 2 3 4 5 6 7 >three &&
+       create_file 1 2 3 4 5 6 7 >four &&
+       git add three four &&
+       git commit -m "add three and four" &&
+
+       git checkout -b another adder^ &&
+       create_file 1 2 3 4 5 6 7 >three &&
+       create_file 1 2 3 four 5 6 7 >four &&
+       git add three four &&
+       git commit -m "add three and four" &&
+
+       # Merging another should be similar to applying this patch
+       git diff adder...another >P.diff &&
+
+       git checkout adder^0 &&
+       test_must_fail git merge --no-commit another &&
+       git ls-files -s >expect.ls &&
+       git diff HEAD | sanitize_conflicted_diff >expect.diff
+'
+
+test_expect_success 'apply -3 with add/add conflict' '
+       # should fail to apply ...
+       git reset --hard &&
+       git checkout adder^0 &&
+       test_must_fail git apply --index --3way P.diff &&
+       # ... and leave conflicts in the index and in the working tree
+       git ls-files -s >actual.ls &&
+       git diff HEAD | sanitize_conflicted_diff >actual.diff &&
+
+       # The result should resemble the corresponding merge
+       test_cmp expect.ls actual.ls &&
+       test_cmp expect.diff actual.diff
+'
+
+test_expect_success 'apply -3 with add/add conflict (dirty working tree)' '
+       # should fail to apply ...
+       git reset --hard &&
+       git checkout adder^0 &&
+       echo >>four &&
+       cat four >four.save &&
+       cat three >three.save &&
+       git ls-files -s >expect.ls &&
+       test_must_fail git apply --index --3way P.diff &&
+       # ... and should not touch anything
+       git ls-files -s >actual.ls &&
+       test_cmp expect.ls actual.ls &&
+       test_cmp four.save four &&
+       test_cmp three.save three
+'
+
+test_done
index e9ccd161ee96a5bdbb4bf77de406ea51dacfb5de..8e15ecbdfdb497fcaa077fd1e6b3f882c9a46cb4 100755 (executable)
@@ -46,6 +46,14 @@ test_expect_success setup '
        cat file1 >saved.file1
 '
 
+test_expect_success 'apply --reject is incompatible with --3way' '
+       test_when_finished "cat saved.file1 >file1" &&
+       git diff >patch.0 &&
+       git checkout file1 &&
+       test_must_fail git apply --reject --3way patch.0 &&
+       git diff --exit-code
+'
+
 test_expect_success 'apply without --reject should fail' '
 
        if git apply patch.1
index e0227730deb88413301b37501fdb165a2c90460e..4d13e10de1f2594ab304b3a2b7b5905d75d6ff84 100755 (executable)
@@ -5,7 +5,8 @@ test_description='git filter-branch'
 
 test_expect_success 'setup' '
        test_commit A &&
-       test_commit B &&
+       GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" &&
+       test_commit --notick B &&
        git checkout -b branch B &&
        test_commit D &&
        mkdir dir &&
index dcb195b4cf67af3cf5ce94d0f5ca75bf94664b9a..ce61d4c0fa3d916757e09224ed79bc191fb0cb3d 100755 (executable)
@@ -636,4 +636,17 @@ test_expect_success 'submodule update properly revives a moved submodule' '
        )
 '
 
+test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd' '
+       mkdir -p linked/dir &&
+       ln -s linked/dir linkto &&
+       (
+               cd linkto &&
+               git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
+               (
+                       cd super &&
+                       git submodule update --init --recursive
+               )
+       )
+'
+
 test_done
index 676da85b52cfd635dc2e08385b568260341f4d04..195e7477d883f39d51f5740b5b309a489dd92d3d 100755 (executable)
@@ -108,6 +108,16 @@ test_expect_success 'amend commit' '
        EDITOR=./editor git commit --amend
 '
 
+test_expect_success 'amend --only ignores staged contents' '
+       cp file file.expect &&
+       echo changed >file &&
+       git add file &&
+       git commit --no-edit --amend --only &&
+       git cat-file blob HEAD:file >file.actual &&
+       test_cmp file.expect file.actual &&
+       git diff --exit-code
+'
+
 test_expect_success 'set up editor' '
        cat >editor <<-\EOF &&
        #!/bin/sh
@@ -138,6 +148,21 @@ test_expect_success '--amend --edit' '
        test_cmp expect msg
 '
 
+test_expect_success '--amend --edit of empty message' '
+       cat >replace <<-\EOF &&
+       #!/bin/sh
+       echo "amended" >"$1"
+       EOF
+       chmod 755 replace &&
+       git commit --allow-empty --allow-empty-message -m "" &&
+       echo more bongo >file &&
+       git add file &&
+       EDITOR=./replace git commit --edit --amend &&
+       git diff-tree -s --format=%s HEAD >msg &&
+       ./replace expect &&
+       test_cmp expect msg
+'
+
 test_expect_success '-m --edit' '
        echo amended >expect &&
        git commit --allow-empty -m buffer &&
index 84fffb3142f9a402f07ce1682f0d5afa3f85955c..3bf1224ae0c5a253ec59f74c0ef566961bae6d36 100755 (executable)
@@ -77,16 +77,16 @@ test_expect_success 'detect renames' '
                git commit -a -m "Rename file1 to file4" &&
                git diff-tree -r -M HEAD &&
                git p4 submit &&
-               p4 filelog //depot/file4 &&
-               p4 filelog //depot/file4 | test_must_fail grep -q "branch from" &&
+               p4 filelog //depot/file4 >filelog &&
+               ! grep " from //depot" filelog &&
 
                git mv file4 file5 &&
                git commit -a -m "Rename file4 to file5" &&
                git diff-tree -r -M HEAD &&
                git config git-p4.detectRenames true &&
                git p4 submit &&
-               p4 filelog //depot/file5 &&
-               p4 filelog //depot/file5 | grep -q "branch from //depot/file4" &&
+               p4 filelog //depot/file5 >filelog &&
+               grep " from //depot/file4" filelog &&
 
                git mv file5 file6 &&
                echo update >>file6 &&
@@ -97,8 +97,8 @@ test_expect_success 'detect renames' '
                test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 &&
                git config git-p4.detectRenames $(($level + 2)) &&
                git p4 submit &&
-               p4 filelog //depot/file6 &&
-               p4 filelog //depot/file6 | test_must_fail grep -q "branch from" &&
+               p4 filelog //depot/file6 >filelog &&
+               ! grep " from //depot" filelog &&
 
                git mv file6 file7 &&
                echo update >>file7 &&
@@ -109,8 +109,8 @@ test_expect_success 'detect renames' '
                test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 &&
                git config git-p4.detectRenames $(($level - 2)) &&
                git p4 submit &&
-               p4 filelog //depot/file7 &&
-               p4 filelog //depot/file7 | grep -q "branch from //depot/file6"
+               p4 filelog //depot/file7 >filelog &&
+               grep " from //depot/file6" filelog
        )
 '
 
index 16397691d951864f760b832ee066a63cd83f64fc..80daaca7806cbe1a6de0ddeba40400c8a811328e 100644 (file)
@@ -143,10 +143,19 @@ test_pause () {
 # Both <file> and <contents> default to <message>.
 
 test_commit () {
-       file=${2:-"$1.t"}
+       notick= &&
+       if test "z$1" = "z--notick"
+       then
+               notick=yes
+               shift
+       fi &&
+       file=${2:-"$1.t"} &&
        echo "${3-$1}" > "$file" &&
        git add "$file" &&
-       test_tick &&
+       if test -z "$notick"
+       then
+               test_tick
+       fi &&
        git commit -m "$1" &&
        git tag "$1"
 }
diff --git a/tree.c b/tree.c
index 676e9f710ca8d5a568e0c6ea2fa88132da81b48c..62fed632d8a1ade389dd645b5887155c21ae25ba 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -22,7 +22,8 @@ static int read_one_entry_opt(const unsigned char *sha1, const char *base, int b
        ce = xcalloc(1, size);
 
        ce->ce_mode = create_ce_mode(mode);
-       ce->ce_flags = create_ce_flags(baselen + len, stage);
+       ce->ce_flags = create_ce_flags(stage);
+       ce->ce_namelen = baselen + len;
        memcpy(ce->name, base, baselen);
        memcpy(ce->name + baselen, pathname, len+1);
        hashcpy(ce->sha1, sha1);
@@ -133,8 +134,8 @@ static int cmp_cache_name_compare(const void *a_, const void *b_)
 
        ce1 = *((const struct cache_entry **)a_);
        ce2 = *((const struct cache_entry **)b_);
-       return cache_name_compare(ce1->name, ce1->ce_flags,
-                                 ce2->name, ce2->ce_flags);
+       return cache_name_stage_compare(ce1->name, ce1->ce_namelen, ce_stage(ce1),
+                                 ce2->name, ce2->ce_namelen, ce_stage(ce2));
 }
 
 int read_tree(struct tree *tree, int stage, struct pathspec *match)
index 29893bf65978d4ea14f669851ca37d2df445b503..6d9636623a2cbb5d5a38eb58d625ef0164d953c0 100644 (file)
@@ -539,7 +539,8 @@ static struct cache_entry *create_ce_entry(const struct traverse_info *info, con
        struct cache_entry *ce = xcalloc(1, cache_entry_size(len));
 
        ce->ce_mode = create_ce_mode(n->mode);
-       ce->ce_flags = create_ce_flags(len, stage);
+       ce->ce_flags = create_ce_flags(stage);
+       ce->ce_namelen = len;
        hashcpy(ce->sha1, n->sha1);
        make_traverse_path(ce->name, info, n);
 
index c749267c95bb90a512cc869760d42c4e050b3e81..c110cbc1250b08fe491d60244e1c8f27c0201532 100644 (file)
@@ -24,7 +24,6 @@ static char default_wt_status_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_GREEN,  /* WT_STATUS_LOCAL_BRANCH */
        GIT_COLOR_RED,    /* WT_STATUS_REMOTE_BRANCH */
        GIT_COLOR_NIL,    /* WT_STATUS_ONBRANCH */
-       GIT_COLOR_NORMAL, /* WT_STATUS_IN_PROGRESS */
 };
 
 static const char *color(int slot, struct wt_status *s)
@@ -931,7 +930,7 @@ static void show_bisect_in_progress(struct wt_status *s,
 
 static void wt_status_print_state(struct wt_status *s)
 {
-       const char *state_color = color(WT_STATUS_IN_PROGRESS, s);
+       const char *state_color = color(WT_STATUS_HEADER, s);
        struct wt_status_state state;
        struct stat st;
 
index c1066a0ec61a5ccc62aa31d53b68dec765a9770d..f8fc58cc0ac8ec73d81fba441b1899b934eaadb4 100644 (file)
@@ -15,7 +15,6 @@ enum color_wt_status {
        WT_STATUS_LOCAL_BRANCH,
        WT_STATUS_REMOTE_BRANCH,
        WT_STATUS_ONBRANCH,
-       WT_STATUS_IN_PROGRESS,
        WT_STATUS_MAXSLOT
 };