summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/.gitignore1
-rw-r--r--t/Makefile29
-rw-r--r--t/README208
-rw-r--r--t/annotate-tests.sh121
-rwxr-xr-xt/diff-lib.sh41
-rwxr-xr-xt/lib-read-tree-m-3way.sh158
-rwxr-xr-xt/t0000-basic.sh271
-rwxr-xr-xt/t0010-racy-git.sh33
-rwxr-xr-xt/t1000-read-tree-m-3way.sh514
-rwxr-xr-xt/t1001-read-tree-m-2way.sh344
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh344
-rwxr-xr-xt/t1100-commit-tree-options.sh45
-rwxr-xr-xt/t1200-tutorial.sh162
-rwxr-xr-xt/t1300-repo-config.sh313
-rwxr-xr-xt/t1400-update-ref.sh213
-rwxr-xr-xt/t2000-checkout-cache-clash.sh53
-rwxr-xr-xt/t2001-checkout-cache-clash.sh87
-rwxr-xr-xt/t2002-checkout-cache-u.sh33
-rwxr-xr-xt/t2003-checkout-cache-mkdir.sh96
-rwxr-xr-xt/t2004-checkout-cache-temp.sh212
-rwxr-xr-xt/t2100-update-cache-badpath.sh51
-rwxr-xr-xt/t2101-update-index-reupdate.sh84
-rwxr-xr-xt/t3000-ls-files-others.sh56
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh82
-rwxr-xr-xt/t3002-ls-files-dashpath.sh69
-rwxr-xr-xt/t3010-ls-files-killed-modified.sh96
-rwxr-xr-xt/t3020-ls-files-error-unmatch.sh27
-rwxr-xr-xt/t3100-ls-tree-restrict.sh158
-rwxr-xr-xt/t3101-ls-tree-dirname.sh138
-rwxr-xr-xt/t3200-branch.sh64
-rwxr-xr-xt/t3300-funny-names.sh160
-rwxr-xr-xt/t3400-rebase.sh34
-rwxr-xr-xt/t3401-rebase-partial.sh53
-rwxr-xr-xt/t3500-cherry.sh54
-rwxr-xr-xt/t3600-rm.sh87
-rwxr-xr-xt/t3700-add.sh22
-rwxr-xr-xt/t4000-diff-format.sh62
-rwxr-xr-xt/t4001-diff-rename.sh67
-rwxr-xr-xt/t4002-diff-basic.sh247
-rwxr-xr-xt/t4003-diff-rename-1.sh128
-rwxr-xr-xt/t4004-diff-rename-symlink.sh67
-rwxr-xr-xt/t4005-diff-rename-2.sh86
-rwxr-xr-xt/t4006-diff-mode.sh44
-rwxr-xr-xt/t4007-rename-3.sh90
-rwxr-xr-xt/t4008-diff-break-rewrite.sh188
-rwxr-xr-xt/t4009-diff-rename-4.sh95
-rwxr-xr-xt/t4010-diff-pathspec.sh65
-rwxr-xr-xt/t4011-diff-symlink.sh85
-rwxr-xr-xt/t4012-diff-binary.sh80
-rwxr-xr-xt/t4100-apply-stat.sh47
-rw-r--r--t/t4100/t-apply-1.expect11
-rw-r--r--t/t4100/t-apply-1.patch194
-rw-r--r--t/t4100/t-apply-2.expect5
-rw-r--r--t/t4100/t-apply-2.patch72
-rw-r--r--t/t4100/t-apply-3.expect7
-rw-r--r--t/t4100/t-apply-3.patch567
-rw-r--r--t/t4100/t-apply-4.expect5
-rw-r--r--t/t4100/t-apply-4.patch7
-rw-r--r--t/t4100/t-apply-5.expect19
-rw-r--r--t/t4100/t-apply-5.patch612
-rw-r--r--t/t4100/t-apply-6.expect5
-rw-r--r--t/t4100/t-apply-6.patch101
-rw-r--r--t/t4100/t-apply-7.expect6
-rw-r--r--t/t4100/t-apply-7.patch494
-rwxr-xr-xt/t4101-apply-nonl.sh34
-rwxr-xr-xt/t4102-apply-rename.sh42
-rwxr-xr-xt/t4103-apply-binary.sh115
-rwxr-xr-xt/t4109-apply-multifrag.sh176
-rwxr-xr-xt/t4110-apply-scan.sh101
-rwxr-xr-xt/t4112-apply-renames.sh148
-rwxr-xr-xt/t4113-apply-ending.sh53
-rwxr-xr-xt/t5000-tar-tree.sh98
-rwxr-xr-xt/t5300-pack-object.sh199
-rwxr-xr-xt/t5400-send-pack.sh67
-rwxr-xr-xt/t5500-fetch-pack.sh131
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh36
-rwxr-xr-xt/t5700-clone-reference.sh78
-rwxr-xr-xt/t5710-info-alternate.sh105
-rwxr-xr-xt/t6000lib.sh116
-rwxr-xr-xt/t6002-rev-list-bisect.sh238
-rwxr-xr-xt/t6003-rev-list-topo-order.sh408
-rwxr-xr-xt/t6010-merge-base.sh59
-rwxr-xr-xt/t6020-merge-df.sh25
-rwxr-xr-xt/t6021-merge-criss-cross.sh98
-rwxr-xr-xt/t6022-merge-rename.sh208
-rwxr-xr-xt/t6101-rev-parse-parents.sh33
-rwxr-xr-xt/t7001-mv.sh41
-rwxr-xr-xt/t7101-reset.sh63
-rwxr-xr-xt/t8001-annotate.sh9
-rwxr-xr-xt/t8002-blame.sh9
-rwxr-xr-xt/t9001-send-email.sh41
-rwxr-xr-xt/test-lib.sh217
-rw-r--r--t/test4012.pngbin0 -> 5660 bytes
93 files changed, 10917 insertions, 0 deletions
diff --git a/t/.gitignore b/t/.gitignore
new file mode 100644
index 0000000000..fad67c097b
--- /dev/null
+++ b/t/.gitignore
@@ -0,0 +1 @@
+trash
diff --git a/t/Makefile b/t/Makefile
new file mode 100644
index 0000000000..549598575b
--- /dev/null
+++ b/t/Makefile
@@ -0,0 +1,29 @@
+# Run tests
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+#GIT_TEST_OPTS=--verbose --debug
+SHELL_PATH ?= $(SHELL)
+TAR ?= $(TAR)
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+
+ifdef NO_PYTHON
+ GIT_TEST_OPTS += --no-python
+endif
+
+all: $(T) clean
+
+$(T):
+ @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+clean:
+ rm -fr trash
+
+.PHONY: $(T) clean
+.NOTPARALLEL:
+
diff --git a/t/README b/t/README
new file mode 100644
index 0000000000..ac5a3ac563
--- /dev/null
+++ b/t/README
@@ -0,0 +1,208 @@
+Core GIT Tests
+==============
+
+This directory holds many test scripts for core GIT tools. The
+first part of this short document describes how to run the tests
+and read their output.
+
+When fixing the tools or adding enhancements, you are strongly
+encouraged to add tests in this directory to cover what you are
+trying to fix or enhance. The later part of this short document
+describes how your test scripts should be organized.
+
+
+Running Tests
+-------------
+
+The easiest way to run tests is to say "make". This runs all
+the tests.
+
+ *** t0000-basic.sh ***
+ * ok 1: .git/objects should be empty after git-init-db in an empty repo.
+ * ok 2: .git/objects should have 256 subdirectories.
+ * ok 3: git-update-index without --add should fail adding.
+ ...
+ * ok 23: no diff after checkout and git-update-index --refresh.
+ * passed all 23 test(s)
+ *** t0100-environment-names.sh ***
+ * ok 1: using old names should issue warnings.
+ * ok 2: using old names but having new names should not issue warnings.
+ ...
+
+Or you can run each test individually from command line, like
+this:
+
+ $ sh ./t3001-ls-files-killed.sh
+ * ok 1: git-update-index --add to add various paths.
+ * ok 2: git-ls-files -k to show killed files.
+ * ok 3: validate git-ls-files -k output.
+ * passed all 3 test(s)
+
+You can pass --verbose (or -v), --debug (or -d), and --immediate
+(or -i) command line argument to the test.
+
+--verbose::
+ This makes the test more verbose. Specifically, the
+ command being run and their output if any are also
+ output.
+
+--debug::
+ This may help the person who is developing a new test.
+ It causes the command defined with test_debug to run.
+
+--immediate::
+ This causes the test to immediately exit upon the first
+ failed test.
+
+
+Naming Tests
+------------
+
+The test files are named as:
+
+ tNNNN-commandname-details.sh
+
+where N is a decimal digit.
+
+First digit tells the family:
+
+ 0 - the absolute basics and global stuff
+ 1 - the basic commands concerning database
+ 2 - the basic commands concerning the working tree
+ 3 - the other basic commands (e.g. ls-files)
+ 4 - the diff commands
+ 5 - the pull and exporting commands
+ 6 - the revision tree commands (even e.g. merge-base)
+
+Second digit tells the particular command we are testing.
+
+Third digit (optionally) tells the particular switch or group of switches
+we are testing.
+
+If you create files under t/ directory (i.e. here) that is not
+the top-level test script, never name the file to match the above
+pattern. The Makefile here considers all such files as the
+top-level test script and tries to run all of them. A care is
+especially needed if you are creating a common test library
+file, similar to test-lib.sh, because such a library file may
+not be suitable for standalone execution.
+
+
+Writing Tests
+-------------
+
+The test script is written as a shell script. It should start
+with the standard "#!/bin/sh" with copyright notices, and an
+assignment to variable 'test_description', like this:
+
+ #!/bin/sh
+ #
+ # Copyright (c) 2005 Junio C Hamano
+ #
+
+ test_description='xxx test (option --frotz)
+
+ This test registers the following structure in the cache
+ and tries to run git-ls-files with option --frotz.'
+
+
+Source 'test-lib.sh'
+--------------------
+
+After assigning test_description, the test script should source
+test-lib.sh like this:
+
+ . ./test-lib.sh
+
+This test harness library does the following things:
+
+ - If the script is invoked with command line argument --help
+ (or -h), it shows the test_description and exits.
+
+ - Creates an empty test directory with an empty .git/objects
+ database and chdir(2) into it. This directory is 't/trash'
+ if you must know, but I do not think you care.
+
+ - Defines standard test helper functions for your scripts to
+ use. These functions are designed to make all scripts behave
+ consistently when command line arguments --verbose (or -v),
+ --debug (or -d), and --immediate (or -i) is given.
+
+
+End with test_done
+------------------
+
+Your script will be a sequence of tests, using helper functions
+from the test harness library. At the end of the script, call
+'test_done'.
+
+
+Test harness library
+--------------------
+
+There are a handful helper functions defined in the test harness
+library for your script to use.
+
+ - test_expect_success <message> <script>
+
+ This takes two strings as parameter, and evaluates the
+ <script>. If it yields success, test is considered
+ successful. <message> should state what it is testing.
+
+ Example:
+
+ test_expect_success \
+ 'git-write-tree should be able to write an empty tree.' \
+ 'tree=$(git-write-tree)'
+
+ - test_expect_failure <message> <script>
+
+ This is the opposite of test_expect_success. If <script>
+ yields success, test is considered a failure.
+
+ Example:
+
+ test_expect_failure \
+ 'git-update-index without --add should fail adding.' \
+ 'git-update-index should-be-empty'
+
+ - test_debug <script>
+
+ This takes a single argument, <script>, and evaluates it only
+ when the test script is started with --debug command line
+ argument. This is primarily meant for use during the
+ development of a new test script.
+
+ - test_done
+
+ Your test script must have test_done at the end. Its purpose
+ is to summarize successes and failures in the test script and
+ exit with an appropriate error code.
+
+
+Tips for Writing Tests
+----------------------
+
+As with any programming projects, existing programs are the best
+source of the information. However, do _not_ emulate
+t0000-basic.sh when writing your tests. The test is special in
+that it tries to validate the very core of GIT. For example, it
+knows that there will be 256 subdirectories under .git/objects/,
+and it knows that the object ID of an empty tree is a certain
+40-byte string. This is deliberately done so in t0000-basic.sh
+because the things the very basic core test tries to achieve is
+to serve as a basis for people who are changing the GIT internal
+drastically. For these people, after making certain changes,
+not seeing failures from the basic test _is_ a failure. And
+such drastic changes to the core GIT that even changes these
+otherwise supposedly stable object IDs should be accompanied by
+an update to t0000-basic.sh.
+
+However, other tests that simply rely on basic parts of the core
+GIT working properly should not have that level of intimate
+knowledge of the core GIT internals. If all the test scripts
+hardcoded the object IDs like t0000-basic.sh does, that defeats
+the purpose of t0000-basic.sh, which is to isolate that level of
+validation in one place. Your test also ends up needing
+updating when such a change to the internal happens, so do _not_
+do it and leave the low level of validation to t0000-basic.sh.
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
new file mode 100644
index 0000000000..114938c3ff
--- /dev/null
+++ b/t/annotate-tests.sh
@@ -0,0 +1,121 @@
+# This file isn't used as a test script directly, instead it is
+# sourced from t8001-annotate.sh and t8001-blame.sh.
+
+check_count () {
+ head=
+ case "$1" in -h) head="$2"; shift; shift ;; esac
+ $PROG file $head | perl -e '
+ my %expect = (@ARGV);
+ my %count = ();
+ while (<STDIN>) {
+ if (/^[0-9a-f]+\t\(([^\t]+)\t/) {
+ my $author = $1;
+ for ($author) { s/^\s*//; s/\s*$//; }
+ if (exists $expect{$author}) {
+ $count{$author}++;
+ }
+ }
+ }
+ my $bad = 0;
+ while (my ($author, $count) = each %count) {
+ my $ok;
+ if ($expect{$author} != $count) {
+ $bad = 1;
+ $ok = "bad";
+ }
+ else {
+ $ok = "good";
+ }
+ print STDERR "Author $author (expected $expect{$author}, attributed $count) $ok\n";
+ }
+ exit($bad);
+ ' "$@"
+}
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'echo "1A quick brown fox jumps over the" >file &&
+ echo "lazy dog" >>file &&
+ git add file
+ GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
+
+test_expect_success \
+ 'check all lines blamed on A' \
+ 'check_count A 2'
+
+test_expect_success \
+ 'Setup new lines blamed on B' \
+ 'echo "2A quick brown fox jumps over the" >>file &&
+ echo "lazy dog" >> file &&
+ GIT_AUTHOR_NAME="B" git commit -a -m "Second."'
+
+test_expect_success \
+ 'Two lines blamed on A, two on B' \
+ 'check_count A 2 B 2'
+
+test_expect_success \
+ 'merge-setup part 1' \
+ 'git checkout -b branch1 master &&
+ echo "3A slow green fox jumps into the" >> file &&
+ echo "well." >> file &&
+ GIT_AUTHOR_NAME="B1" git commit -a -m "Branch1-1"'
+
+test_expect_success \
+ 'Two lines blamed on A, two on B, two on B1' \
+ 'check_count A 2 B 2 B1 2'
+
+test_expect_success \
+ 'merge-setup part 2' \
+ 'git checkout -b branch2 master &&
+ sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new &&
+ mv file.new file &&
+ GIT_AUTHOR_NAME="B2" git commit -a -m "Branch2-1"'
+
+test_expect_success \
+ 'Two lines blamed on A, one on B, one on B2' \
+ 'check_count A 2 B 1 B2 1'
+
+test_expect_success \
+ 'merge-setup part 3' \
+ 'git pull . branch1'
+
+test_expect_success \
+ 'Two lines blamed on A, one on B, two on B1, one on B2' \
+ 'check_count A 2 B 1 B1 2 B2 1'
+
+test_expect_success \
+ 'Annotating an old revision works' \
+ 'check_count -h master A 2 B 2'
+
+test_expect_success \
+ 'Annotating an old revision works' \
+ 'check_count -h master^ A 2'
+
+test_expect_success \
+ 'merge-setup part 4' \
+ 'echo "evil merge." >>file &&
+ EDITOR=: VISUAL=: git commit -a --amend'
+
+test_expect_success \
+ 'Two lines blamed on A, one on B, two on B1, one on B2, one on A U Thor' \
+ 'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1'
+
+test_expect_success \
+ 'an incomplete line added' \
+ 'echo "incomplete" | tr -d "\\012" >>file &&
+ GIT_AUTHOR_NAME="C" git commit -a -m "Incomplete"'
+
+test_expect_success \
+ 'With incomplete lines.' \
+ 'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1'
+
+test_expect_success \
+ 'some edit' \
+ 'mv file file1 &&
+ sed -e 1d -e "5s/3A/99/" file1 >file &&
+ rm -f file1 &&
+ GIT_AUTHOR_NAME="D" git commit -a -m "edit"'
+
+test_expect_success \
+ 'some edit' \
+ 'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1'
diff --git a/t/diff-lib.sh b/t/diff-lib.sh
new file mode 100755
index 0000000000..745a1b0311
--- /dev/null
+++ b/t/diff-lib.sh
@@ -0,0 +1,41 @@
+:
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+sanitize_diff_raw='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]* / X X \1# /'
+compare_diff_raw () {
+ # When heuristics are improved, the score numbers would change.
+ # Ignore them while comparing.
+ # Also we do not check SHA1 hash generation in this test, which
+ # is a job for t0000-basic.sh
+
+ sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
+ sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
+ diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+sanitize_diff_raw_z='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*$/ X X \1#/'
+compare_diff_raw_z () {
+ # When heuristics are improved, the score numbers would change.
+ # Ignore them while comparing.
+ # Also we do not check SHA1 hash generation in this test, which
+ # is a job for t0000-basic.sh
+
+ tr '\0' '\012' <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1
+ tr '\0' '\012' <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2
+ diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+compare_diff_patch () {
+ # When heuristics are improved, the score numbers would change.
+ # Ignore them while comparing.
+ sed -e '
+ /^[dis]*imilarity index [0-9]*%$/d
+ /^index [0-9a-f]*\.\.[0-9a-f]/d
+ ' <"$1" >.tmp-1
+ sed -e '
+ /^[dis]*imilarity index [0-9]*%$/d
+ /^index [0-9a-f]*\.\.[0-9a-f]/d
+ ' <"$2" >.tmp-2
+ diff -u .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
diff --git a/t/lib-read-tree-m-3way.sh b/t/lib-read-tree-m-3way.sh
new file mode 100755
index 0000000000..d195603dfa
--- /dev/null
+++ b/t/lib-read-tree-m-3way.sh
@@ -0,0 +1,158 @@
+: Included from t1000-read-tree-m-3way.sh and others
+# Original tree.
+mkdir Z
+for a in N D M
+do
+ for b in N D M
+ do
+ p=$a$b
+ echo This is $p from the original tree. >$p
+ echo This is Z/$p from the original tree. >Z/$p
+ test_expect_success \
+ "adding test file $p and Z/$p" \
+ 'git-update-index --add $p &&
+ git-update-index --add Z/$p'
+ done
+done
+echo This is SS from the original tree. >SS
+test_expect_success \
+ 'adding test file SS' \
+ 'git-update-index --add SS'
+cat >TT <<\EOF
+This is a trivial merge sample text.
+Branch A is expected to upcase this word, here.
+There are some filler lines to avoid diff context
+conflicts here,
+like this one,
+and this one,
+and this one is yet another one of them.
+At the very end, here comes another line, that is
+the word, expected to be upcased by Branch B.
+This concludes the trivial merge sample file.
+EOF
+test_expect_success \
+ 'adding test file TT' \
+ 'git-update-index --add TT'
+test_expect_success \
+ 'prepare initial tree' \
+ 'tree_O=$(git-write-tree)'
+
+################################################################
+# Branch A and B makes the changes according to the above matrix.
+
+################################################################
+# Branch A
+
+to_remove=$(echo D? Z/D?)
+rm -f $to_remove
+test_expect_success \
+ 'change in branch A (removal)' \
+ 'git-update-index --remove $to_remove'
+
+for p in M? Z/M?
+do
+ echo This is modified $p in the branch A. >$p
+ test_expect_success \
+ 'change in branch A (modification)' \
+ "git-update-index $p"
+done
+
+for p in AN AA Z/AN Z/AA
+do
+ echo This is added $p in the branch A. >$p
+ test_expect_success \
+ 'change in branch A (addition)' \
+ "git-update-index --add $p"
+done
+
+echo This is SS from the modified tree. >SS
+echo This is LL from the modified tree. >LL
+test_expect_success \
+ 'change in branch A (addition)' \
+ 'git-update-index --add LL &&
+ git-update-index SS'
+mv TT TT-
+sed -e '/Branch A/s/word/WORD/g' <TT- >TT
+rm -f TT-
+test_expect_success \
+ 'change in branch A (edit)' \
+ 'git-update-index TT'
+
+mkdir DF
+echo Branch A makes a file at DF/DF, creating a directory DF. >DF/DF
+test_expect_success \
+ 'change in branch A (change file to directory)' \
+ 'git-update-index --add DF/DF'
+
+test_expect_success \
+ 'recording branch A tree' \
+ 'tree_A=$(git-write-tree)'
+
+################################################################
+# Branch B
+# Start from O
+
+rm -rf [NDMASLT][NDMASLT] Z DF
+mkdir Z
+test_expect_success \
+ 'reading original tree and checking out' \
+ 'git-read-tree $tree_O &&
+ git-checkout-index -a'
+
+to_remove=$(echo ?D Z/?D)
+rm -f $to_remove
+test_expect_success \
+ 'change in branch B (removal)' \
+ "git-update-index --remove $to_remove"
+
+for p in ?M Z/?M
+do
+ echo This is modified $p in the branch B. >$p
+ test_expect_success \
+ 'change in branch B (modification)' \
+ "git-update-index $p"
+done
+
+for p in NA AA Z/NA Z/AA
+do
+ echo This is added $p in the branch B. >$p
+ test_expect_success \
+ 'change in branch B (addition)' \
+ "git-update-index --add $p"
+done
+echo This is SS from the modified tree. >SS
+echo This is LL from the modified tree. >LL
+test_expect_success \
+ 'change in branch B (addition and modification)' \
+ 'git-update-index --add LL &&
+ git-update-index SS'
+mv TT TT-
+sed -e '/Branch B/s/word/WORD/g' <TT- >TT
+rm -f TT-
+test_expect_success \
+ 'change in branch B (modification)' \
+ 'git-update-index TT'
+
+echo Branch B makes a file at DF. >DF
+test_expect_success \
+ 'change in branch B (addition of a file to conflict with directory)' \
+ 'git-update-index --add DF'
+
+test_expect_success \
+ 'recording branch B tree' \
+ 'tree_B=$(git-write-tree)'
+
+test_expect_success \
+ 'keep contents of 3 trees for easy access' \
+ 'rm -f .git/index &&
+ git-read-tree $tree_O &&
+ mkdir .orig-O &&
+ git-checkout-index --prefix=.orig-O/ -f -q -a &&
+ rm -f .git/index &&
+ git-read-tree $tree_A &&
+ mkdir .orig-A &&
+ git-checkout-index --prefix=.orig-A/ -f -q -a &&
+ rm -f .git/index &&
+ git-read-tree $tree_B &&
+ mkdir .orig-B &&
+ git-checkout-index --prefix=.orig-B/ -f -q -a'
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
new file mode 100755
index 0000000000..2c9bbb59b0
--- /dev/null
+++ b/t/t0000-basic.sh
@@ -0,0 +1,271 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test the very basics part #1.
+
+The rest of the test suite does not check the basic operation of git
+plumbing commands to work very carefully. Their job is to concentrate
+on tricky features that caused bugs in the past to detect regression.
+
+This test runs very basic features, like registering things in cache,
+writing tree, etc.
+
+Note that this test *deliberately* hard-codes many expected object
+IDs. When object ID computation changes, like in the previous case of
+swapping compression and hashing order, the person who is making the
+modification *should* take notice and update the test vectors here.
+'
+
+################################################################
+# It appears that people are getting bitten by not installing
+# 'merge' (usually part of RCS package in binary distributions)
+# or have too old python without subprocess. Check them and error
+# out before running any tests. Also catch the bogosity of trying
+# to run tests without building while we are at it.
+
+../git >/dev/null
+if test $? != 1
+then
+ echo >&2 'You do not seem to have built git yet.'
+ exit 1
+fi
+
+merge >/dev/null 2>/dev/null
+if test $? = 127
+then
+ echo >&2 'You do not seem to have "merge" installed.
+Please check INSTALL document.'
+ exit 1
+fi
+
+. ./test-lib.sh
+
+test "$no_python" || "$PYTHON" -c 'import subprocess' || {
+ echo >&2 'Your python seem to lack "subprocess" module.
+Please check INSTALL document.'
+ exit 1
+}
+
+################################################################
+# init-db has been done in an empty repository.
+# make sure it is empty.
+
+find .git/objects -type f -print >should-be-empty
+test_expect_success \
+ '.git/objects should be empty after git-init-db in an empty repo.' \
+ 'cmp -s /dev/null should-be-empty'
+
+# also it should have 2 subdirectories; no fan-out anymore, pack, and info.
+# 3 is counting "objects" itself
+find .git/objects -type d -print >full-of-directories
+test_expect_success \
+ '.git/objects should have 3 subdirectories.' \
+ 'test $(wc -l < full-of-directories) = 3'
+
+################################################################
+# Basics of the basics
+
+# updating a new file without --add should fail.
+test_expect_failure \
+ 'git-update-index without --add should fail adding.' \
+ 'git-update-index should-be-empty'
+
+# and with --add it should succeed, even if it is empty (it used to fail).
+test_expect_success \
+ 'git-update-index with --add should succeed.' \
+ 'git-update-index --add should-be-empty'
+
+test_expect_success \
+ 'writing tree out with git-write-tree' \
+ 'tree=$(git-write-tree)'
+
+# we know the shape and contents of the tree and know the object ID for it.
+test_expect_success \
+ 'validate object ID of a known tree.' \
+ 'test "$tree" = 7bb943559a305bdd6bdee2cef6e5df2413c3d30a'
+
+# Removing paths.
+rm -f should-be-empty full-of-directories
+test_expect_failure \
+ 'git-update-index without --remove should fail removing.' \
+ 'git-update-index should-be-empty'
+
+test_expect_success \
+ 'git-update-index with --remove should be able to remove.' \
+ 'git-update-index --remove should-be-empty'
+
+# Empty tree can be written with recent write-tree.
+test_expect_success \
+ 'git-write-tree should be able to write an empty tree.' \
+ 'tree=$(git-write-tree)'
+
+test_expect_success \
+ 'validate object ID of a known tree.' \
+ 'test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904'
+
+# Various types of objects
+mkdir path2 path3 path3/subp3
+for p in path0 path2/file2 path3/file3 path3/subp3/file3
+do
+ echo "hello $p" >$p
+ ln -s "hello $p" ${p}sym
+done
+test_expect_success \
+ 'adding various types of objects with git-update-index --add.' \
+ 'find path* ! -type d -print | xargs git-update-index --add'
+
+# Show them and see that matches what we expect.
+test_expect_success \
+ 'showing stage with git-ls-files --stage' \
+ 'git-ls-files --stage >current'
+
+cat >expected <<\EOF
+100644 f87290f8eb2cbbea7857214459a0739927eab154 0 path0
+120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0 path0sym
+100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0 path2/file2
+120000 d8ce161addc5173867a3c3c730924388daedbc38 0 path2/file2sym
+100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0 path3/file3
+120000 8599103969b43aff7e430efea79ca4636466794f 0 path3/file3sym
+100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0 path3/subp3/file3
+120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0 path3/subp3/file3sym
+EOF
+test_expect_success \
+ 'validate git-ls-files output for a known tree.' \
+ 'diff current expected'
+
+test_expect_success \
+ 'writing tree out with git-write-tree.' \
+ 'tree=$(git-write-tree)'
+test_expect_success \
+ 'validate object ID for a known tree.' \
+ 'test "$tree" = 087704a96baf1c2d1c869a8b084481e121c88b5b'
+
+test_expect_success \
+ 'showing tree with git-ls-tree' \
+ 'git-ls-tree $tree >current'
+cat >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
+040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
+EOF
+test_expect_success \
+ 'git-ls-tree output for a known tree.' \
+ 'diff current expected'
+
+# This changed in ls-tree pathspec change -- recursive does
+# not show tree nodes anymore.
+test_expect_success \
+ 'showing tree with git-ls-tree -r' \
+ 'git-ls-tree -r $tree >current'
+cat >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
+120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
+100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
+120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
+100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
+120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
+EOF
+test_expect_success \
+ 'git-ls-tree -r output for a known tree.' \
+ 'diff current expected'
+
+# But with -r -t we can have both.
+test_expect_success \
+ 'showing tree with git-ls-tree -r -t' \
+ 'git-ls-tree -r -t $tree >current'
+cat >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
+100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
+120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
+040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
+100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
+120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
+040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 path3/subp3
+100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
+120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
+EOF
+test_expect_success \
+ 'git-ls-tree -r output for a known tree.' \
+ 'diff current expected'
+
+test_expect_success \
+ 'writing partial tree out with git-write-tree --prefix.' \
+ 'ptree=$(git-write-tree --prefix=path3)'
+test_expect_success \
+ 'validate object ID for a known tree.' \
+ 'test "$ptree" = 21ae8269cacbe57ae09138dcc3a2887f904d02b3'
+
+test_expect_success \
+ 'writing partial tree out with git-write-tree --prefix.' \
+ 'ptree=$(git-write-tree --prefix=path3/subp3)'
+test_expect_success \
+ 'validate object ID for a known tree.' \
+ 'test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2'
+
+################################################################
+rm .git/index
+test_expect_success \
+ 'git-read-tree followed by write-tree should be idempotent.' \
+ 'git-read-tree $tree &&
+ test -f .git/index &&
+ newtree=$(git-write-tree) &&
+ test "$newtree" = "$tree"'
+
+cat >expected <<\EOF
+:100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M path0
+:120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M path0sym
+:100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M path2/file2
+:120000 120000 d8ce161addc5173867a3c3c730924388daedbc38 0000000000000000000000000000000000000000 M path2/file2sym
+:100644 100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0000000000000000000000000000000000000000 M path3/file3
+:120000 120000 8599103969b43aff7e430efea79ca4636466794f 0000000000000000000000000000000000000000 M path3/file3sym
+:100644 100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0000000000000000000000000000000000000000 M path3/subp3/file3
+:120000 120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0000000000000000000000000000000000000000 M path3/subp3/file3sym
+EOF
+test_expect_success \
+ 'validate git-diff-files output for a know cache/work tree state.' \
+ 'git-diff-files >current && diff >/dev/null -b current expected'
+
+test_expect_success \
+ 'git-update-index --refresh should succeed.' \
+ 'git-update-index --refresh'
+
+test_expect_success \
+ 'no diff after checkout and git-update-index --refresh.' \
+ 'git-diff-files >current && cmp -s current /dev/null'
+
+################################################################
+P=087704a96baf1c2d1c869a8b084481e121c88b5b
+test_expect_success \
+ 'git-commit-tree records the correct tree in a commit.' \
+ 'commit0=$(echo NO | git-commit-tree $P) &&
+ tree=$(git show --pretty=raw $commit0 |
+ sed -n -e "s/^tree //p" -e "/^author /q") &&
+ test "z$tree" = "z$P"'
+
+test_expect_success \
+ 'git-commit-tree records the correct parent in a commit.' \
+ 'commit1=$(echo NO | git-commit-tree $P -p $commit0) &&
+ parent=$(git show --pretty=raw $commit1 |
+ sed -n -e "s/^parent //p" -e "/^author /q") &&
+ test "z$commit0" = "z$parent"'
+
+test_expect_success \
+ 'git-commit-tree omits duplicated parent in a commit.' \
+ 'commit2=$(echo NO | git-commit-tree $P -p $commit0 -p $commit0) &&
+ parent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ sort -u) &&
+ test "z$commit0" = "z$parent" &&
+ numparent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ wc -l) &&
+ test $numparent = 1'
+
+test_done
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
new file mode 100755
index 0000000000..e45a9e40e4
--- /dev/null
+++ b/t/t0010-racy-git.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='racy GIT'
+
+. ./test-lib.sh
+
+# This test can give false success if your machine is sufficiently
+# slow or your trial happened to happen on second boundary.
+
+for trial in 0 1 2 3 4
+do
+ rm -f .git/index
+ echo frotz >infocom
+ git update-index --add infocom
+ echo xyzzy >infocom
+
+ files=`git diff-files -p`
+ test_expect_success \
+ "Racy GIT trial #$trial part A" \
+ 'test "" != "$files"'
+
+ sleep 1
+ echo xyzzy >cornerstone
+ git update-index --add cornerstone
+
+ files=`git diff-files -p`
+ test_expect_success \
+ "Racy GIT trial #$trial part B" \
+ 'test "" != "$files"'
+
+done
+
+test_done
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
new file mode 100755
index 0000000000..d0af8c3d52
--- /dev/null
+++ b/t/t1000-read-tree-m-3way.sh
@@ -0,0 +1,514 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Three way merge with read-tree -m
+
+This test tries three-way merge with read-tree -m
+
+There is one ancestor (called O for Original) and two branches A
+and B derived from it. We want to do a 3-way merge between A and
+B, using O as the common ancestor.
+
+ merge A O B
+
+Decisions are made by comparing contents of O, A and B pathname
+by pathname. The result is determined by the following guiding
+principle:
+
+ - If only A does something to it and B does not touch it, take
+ whatever A does.
+
+ - If only B does something to it and A does not touch it, take
+ whatever B does.
+
+ - If both A and B does something but in the same way, take
+ whatever they do.
+
+ - If A and B does something but different things, we need a
+ 3-way merge:
+
+ - We cannot do anything about the following cases:
+
+ * O does not have it. A and B both must be adding to the
+ same path independently.
+
+ * A deletes it. B must be modifying.
+
+ - Otherwise, A and B are modifying. Run 3-way merge.
+
+First, the case matrix.
+
+ - Vertical axis is for A'\''s actions.
+ - Horizontal axis is for B'\''s actions.
+
+.----------------------------------------------------------------.
+| A B | No Action | Delete | Modify | Add |
+|------------+------------+------------+------------+------------|
+| No Action | | | | |
+| | select O | delete | select B | select B |
+| | | | | |
+|------------+------------+------------+------------+------------|
+| Delete | | | ********** | can |
+| | delete | delete | merge | not |
+| | | | | happen |
+|------------+------------+------------+------------+------------|
+| Modify | | ********** | ?????????? | can |
+| | select A | merge | select A=B | not |
+| | | | merge | happen |
+|------------+------------+------------+------------+------------|
+| Add | | can | can | ?????????? |
+| | select A | not | not | select A=B |
+| | | happen | happen | merge |
+.----------------------------------------------------------------.
+
+In addition:
+
+ SS: a special case of MM, where A and B makes the same modification.
+ LL: a special case of AA, where A and B creates the same file.
+ TT: a special case of MM, where A and B makes mergeable changes.
+ DF: a special case, where A makes a directory and B makes a file.
+
+'
+. ./test-lib.sh
+. ../lib-read-tree-m-3way.sh
+
+################################################################
+# Trivial "majority when 3 stages exist" merge plus #2ALT, #3ALT
+# and #5ALT trivial merges.
+
+cat >expected <<\EOF
+100644 X 2 AA
+100644 X 3 AA
+100644 X 0 AN
+100644 X 1 DD
+100644 X 3 DF
+100644 X 2 DF/DF
+100644 X 1 DM
+100644 X 3 DM
+100644 X 1 DN
+100644 X 3 DN
+100644 X 0 LL
+100644 X 1 MD
+100644 X 2 MD
+100644 X 1 MM
+100644 X 2 MM
+100644 X 3 MM
+100644 X 0 MN
+100644 X 0 NA
+100644 X 1 ND
+100644 X 2 ND
+100644 X 0 NM
+100644 X 0 NN
+100644 X 0 SS
+100644 X 1 TT
+100644 X 2 TT
+100644 X 3 TT
+100644 X 2 Z/AA
+100644 X 3 Z/AA
+100644 X 0 Z/AN
+100644 X 1 Z/DD
+100644 X 1 Z/DM
+100644 X 3 Z/DM
+100644 X 1 Z/DN
+100644 X 3 Z/DN
+100644 X 1 Z/MD
+100644 X 2 Z/MD
+100644 X 1 Z/MM
+100644 X 2 Z/MM
+100644 X 3 Z/MM
+100644 X 0 Z/MN
+100644 X 0 Z/NA
+100644 X 1 Z/ND
+100644 X 2 Z/ND
+100644 X 0 Z/NM
+100644 X 0 Z/NN
+EOF
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+
+check_result () {
+ git-ls-files --stage | sed -e 's/ '"$_x40"' / X /' >current &&
+ diff -u expected current
+}
+
+# This is done on an empty work directory, which is the normal
+# merge person behaviour.
+test_expect_success \
+ '3-way merge with git-read-tree -m, empty cache' \
+ "rm -fr [NDMALTS][NDMALTSF] Z &&
+ rm .git/index &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+# This starts out with the first head, which is the normal
+# patch submitter behaviour.
+test_expect_success \
+ '3-way merge with git-read-tree -m, match H' \
+ "rm -fr [NDMALTS][NDMALTSF] Z &&
+ rm .git/index &&
+ git-read-tree $tree_A &&
+ git-checkout-index -f -u -a &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+: <<\END_OF_CASE_TABLE
+
+We have so far tested only empty index and clean-and-matching-A index
+case which are trivial. Make sure index requirements are also
+checked.
+
+"git-read-tree -m O A B"
+
+ O A B result index requirements
+-------------------------------------------------------------------
+ 1 missing missing missing - must not exist.
+ ------------------------------------------------------------------
+ 2 missing missing exists take B* must match B, if exists.
+ ------------------------------------------------------------------
+ 3 missing exists missing take A* must match A, if exists.
+ ------------------------------------------------------------------
+ 4 missing exists A!=B no merge must match A and be
+ up-to-date, if exists.
+ ------------------------------------------------------------------
+ 5 missing exists A==B take A must match A, if exists.
+ ------------------------------------------------------------------
+ 6 exists missing missing remove must not exist.
+ ------------------------------------------------------------------
+ 7 exists missing O!=B no merge must not exist.
+ ------------------------------------------------------------------
+ 8 exists missing O==B remove must not exist.
+ ------------------------------------------------------------------
+ 9 exists O!=A missing no merge must match A and be
+ up-to-date, if exists.
+ ------------------------------------------------------------------
+ 10 exists O==A missing remove ditto
+ ------------------------------------------------------------------
+ 11 exists O!=A O!=B no merge must match A and be
+ A!=B up-to-date, if exists.
+ ------------------------------------------------------------------
+ 12 exists O!=A O!=B take A must match A, if exists.
+ A==B
+ ------------------------------------------------------------------
+ 13 exists O!=A O==B take A must match A, if exists.
+ ------------------------------------------------------------------
+ 14 exists O==A O!=B take B if exists, must either (1)
+ match A and be up-to-date,
+ or (2) match B.
+ ------------------------------------------------------------------
+ 15 exists O==A O==B take B must match A if exists.
+ ------------------------------------------------------------------
+ 16 exists O==A O==B barf must match A if exists.
+ *multi* in one in another
+-------------------------------------------------------------------
+
+Note: we need to be careful in case 2 and 3. The tree A may contain
+DF (file) when tree B require DF to be a directory by having DF/DF
+(file).
+
+END_OF_CASE_TABLE
+
+test_expect_failure \
+ '1 - must not have an entry not in A.' \
+ "rm -f .git/index XX &&
+ echo XX >XX &&
+ git-update-index --add XX &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '2 - must match B in !O && !A && B case.' \
+ "rm -f .git/index NA &&
+ cp .orig-B/NA NA &&
+ git-update-index --add NA &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '2 - matching B alone is OK in !O && !A && B case.' \
+ "rm -f .git/index NA &&
+ cp .orig-B/NA NA &&
+ git-update-index --add NA &&
+ echo extra >>NA &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '3 - must match A in !O && A && !B case.' \
+ "rm -f .git/index AN &&
+ cp .orig-A/AN AN &&
+ git-update-index --add AN &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '3 - matching A alone is OK in !O && A && !B case.' \
+ "rm -f .git/index AN &&
+ cp .orig-A/AN AN &&
+ git-update-index --add AN &&
+ echo extra >>AN &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_failure \
+ '3 (fail) - must match A in !O && A && !B case.' \
+ "rm -f .git/index AN &&
+ cp .orig-A/AN AN &&
+ echo extra >>AN &&
+ git-update-index --add AN &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '4 - must match and be up-to-date in !O && A && B && A!=B case.' \
+ "rm -f .git/index AA &&
+ cp .orig-A/AA AA &&
+ git-update-index --add AA &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_failure \
+ '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' \
+ "rm -f .git/index AA &&
+ cp .orig-A/AA AA &&
+ git-update-index --add AA &&
+ echo extra >>AA &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_failure \
+ '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' \
+ "rm -f .git/index AA &&
+ cp .orig-A/AA AA &&
+ echo extra >>AA &&
+ git-update-index --add AA &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '5 - must match in !O && A && B && A==B case.' \
+ "rm -f .git/index LL &&
+ cp .orig-A/LL LL &&
+ git-update-index --add LL &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '5 - must match in !O && A && B && A==B case.' \
+ "rm -f .git/index LL &&
+ cp .orig-A/LL LL &&
+ git-update-index --add LL &&
+ echo extra >>LL &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_failure \
+ '5 (fail) - must match A in !O && A && B && A==B case.' \
+ "rm -f .git/index LL &&
+ cp .orig-A/LL LL &&
+ echo extra >>LL &&
+ git-update-index --add LL &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_failure \
+ '6 - must not exist in O && !A && !B case' \
+ "rm -f .git/index DD &&
+ echo DD >DD
+ git-update-index --add DD &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_failure \
+ '7 - must not exist in O && !A && B && O!=B case' \
+ "rm -f .git/index DM &&
+ cp .orig-B/DM DM &&
+ git-update-index --add DM &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_failure \
+ '8 - must not exist in O && !A && B && O==B case' \
+ "rm -f .git/index DN &&
+ cp .orig-B/DN DN &&
+ git-update-index --add DN &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '9 - must match and be up-to-date in O && A && !B && O!=A case' \
+ "rm -f .git/index MD &&
+ cp .orig-A/MD MD &&
+ git-update-index --add MD &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_failure \
+ '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' \
+ "rm -f .git/index MD &&
+ cp .orig-A/MD MD &&
+ git-update-index --add MD &&
+ echo extra >>MD &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_failure \
+ '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' \
+ "rm -f .git/index MD &&
+ cp .orig-A/MD MD &&
+ echo extra >>MD &&
+ git-update-index --add MD &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '10 - must match and be up-to-date in O && A && !B && O==A case' \
+ "rm -f .git/index ND &&
+ cp .orig-A/ND ND &&
+ git-update-index --add ND &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_failure \
+ '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' \
+ "rm -f .git/index ND &&
+ cp .orig-A/ND ND &&
+ git-update-index --add ND &&
+ echo extra >>ND &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_failure \
+ '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' \
+ "rm -f .git/index ND &&
+ cp .orig-A/ND ND &&
+ echo extra >>ND &&
+ git-update-index --add ND &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '11 - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
+ "rm -f .git/index MM &&
+ cp .orig-A/MM MM &&
+ git-update-index --add MM &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_failure \
+ '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
+ "rm -f .git/index MM &&
+ cp .orig-A/MM MM &&
+ git-update-index --add MM &&
+ echo extra >>MM &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_failure \
+ '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
+ "rm -f .git/index MM &&
+ cp .orig-A/MM MM &&
+ echo extra >>MM &&
+ git-update-index --add MM &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '12 - must match A in O && A && B && O!=A && A==B case' \
+ "rm -f .git/index SS &&
+ cp .orig-A/SS SS &&
+ git-update-index --add SS &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '12 - must match A in O && A && B && O!=A && A==B case' \
+ "rm -f .git/index SS &&
+ cp .orig-A/SS SS &&
+ git-update-index --add SS &&
+ echo extra >>SS &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_failure \
+ '12 (fail) - must match A in O && A && B && O!=A && A==B case' \
+ "rm -f .git/index SS &&
+ cp .orig-A/SS SS &&
+ echo extra >>SS &&
+ git-update-index --add SS &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '13 - must match A in O && A && B && O!=A && O==B case' \
+ "rm -f .git/index MN &&
+ cp .orig-A/MN MN &&
+ git-update-index --add MN &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '13 - must match A in O && A && B && O!=A && O==B case' \
+ "rm -f .git/index MN &&
+ cp .orig-A/MN MN &&
+ git-update-index --add MN &&
+ echo extra >>MN &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '14 - must match and be up-to-date in O && A && B && O==A && O!=B case' \
+ "rm -f .git/index NM &&
+ cp .orig-A/NM NM &&
+ git-update-index --add NM &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '14 - may match B in O && A && B && O==A && O!=B case' \
+ "rm -f .git/index NM &&
+ cp .orig-B/NM NM &&
+ git-update-index --add NM &&
+ echo extra >>NM &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_failure \
+ '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' \
+ "rm -f .git/index NM &&
+ cp .orig-A/NM NM &&
+ git-update-index --add NM &&
+ echo extra >>NM &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_failure \
+ '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' \
+ "rm -f .git/index NM &&
+ cp .orig-A/NM NM &&
+ echo extra >>NM &&
+ git-update-index --add NM &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '15 - must match A in O && A && B && O==A && O==B case' \
+ "rm -f .git/index NN &&
+ cp .orig-A/NN NN &&
+ git-update-index --add NN &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '15 - must match A in O && A && B && O==A && O==B case' \
+ "rm -f .git/index NN &&
+ cp .orig-A/NN NN &&
+ git-update-index --add NN &&
+ echo extra >>NN &&
+ git-read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_failure \
+ '15 (fail) - must match A in O && A && B && O==A && O==B case' \
+ "rm -f .git/index NN &&
+ cp .orig-A/NN NN &&
+ echo extra >>NN &&
+ git-update-index --add NN &&
+ git-read-tree -m $tree_O $tree_A $tree_B"
+
+# #16
+test_expect_success \
+ '16 - A matches in one and B matches in another.' \
+ 'rm -f .git/index F16 &&
+ echo F16 >F16 &&
+ git-update-index --add F16 &&
+ tree0=`git-write-tree` &&
+ echo E16 >F16 &&
+ git-update-index F16 &&
+ tree1=`git-write-tree` &&
+ git-read-tree -m $tree0 $tree1 $tree1 $tree0 &&
+ git-ls-files --stage'
+
+test_done
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
new file mode 100755
index 0000000000..75e4c9a886
--- /dev/null
+++ b/t/t1001-read-tree-m-2way.sh
@@ -0,0 +1,344 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Two way merge with read-tree -m $H $M
+
+This test tries two-way merge (aka fast forward with carry forward).
+
+There is the head (called H) and another commit (called M), which is
+simply ahead of H. The index and the work tree contains a state that
+is derived from H, but may also have local changes. This test checks
+all the combinations described in the two-tree merge "carry forward"
+rules, found in <Documentation/git-read-tree.txt>.
+
+In the test, these paths are used:
+ bozbar - in H, stays in M, modified from bozbar to gnusto
+ frotz - not in H added in M
+ nitfol - in H, stays in M unmodified
+ rezrov - in H, deleted in M
+ yomin - not in H nor M
+'
+. ./test-lib.sh
+
+read_tree_twoway () {
+ git-read-tree -m "$1" "$2" && git-ls-files --stage
+}
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+compare_change () {
+ sed -n >current \
+ -e '/^--- /d; /^+++ /d; /^@@ /d;' \
+ -e 's/^\([-+][0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /p' \
+ "$1"
+ diff -u expected current
+}
+
+check_cache_at () {
+ clean_if_empty=`git-diff-files -- "$1"`
+ case "$clean_if_empty" in
+ '') echo "$1: clean" ;;
+ ?*) echo "$1: dirty" ;;
+ esac
+ case "$2,$clean_if_empty" in
+ clean,) : ;;
+ clean,?*) false ;;
+ dirty,) false ;;
+ dirty,?*) : ;;
+ esac
+}
+
+cat >bozbar-old <<\EOF
+This is a sample file used in two-way fast forward merge
+tests. Its second line ends with a magic word bozbar
+which will be modified by the merged head to gnusto.
+It has some extra lines so that external tools can
+successfully merge independent changes made to later
+lines (such as this one), avoiding line conflicts.
+EOF
+
+sed -e 's/bozbar/gnusto (earlier bozbar)/' bozbar-old >bozbar-new
+
+test_expect_success \
+ setup \
+ 'echo frotz >frotz &&
+ echo nitfol >nitfol &&
+ cat bozbar-old >bozbar &&
+ echo rezrov >rezrov &&
+ echo yomin >yomin &&
+ git-update-index --add nitfol bozbar rezrov &&
+ treeH=`git-write-tree` &&
+ echo treeH $treeH &&
+ git-ls-tree $treeH &&
+
+ cat bozbar-new >bozbar &&
+ git-update-index --add frotz bozbar --force-remove rezrov &&
+ git-ls-files --stage >M.out &&
+ treeM=`git-write-tree` &&
+ echo treeM $treeM &&
+ git-ls-tree $treeM &&
+ git-diff-tree $treeH $treeM'
+
+test_expect_success \
+ '1, 2, 3 - no carry forward' \
+ 'rm -f .git/index &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >1-3.out &&
+ diff -u M.out 1-3.out &&
+ check_cache_at bozbar dirty &&
+ check_cache_at frotz dirty &&
+ check_cache_at nitfol dirty'
+
+echo '+100644 X 0 yomin' >expected
+
+test_expect_success \
+ '4 - carry forward local addition.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ git-update-index --add yomin &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >4.out || return 1
+ diff -u M.out 4.out >4diff.out
+ compare_change 4diff.out expected &&
+ check_cache_at yomin clean'
+
+test_expect_success \
+ '5 - carry forward local addition.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo yomin >yomin &&
+ git-update-index --add yomin &&
+ echo yomin yomin >yomin &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >5.out || return 1
+ diff -u M.out 5.out >5diff.out
+ compare_change 5diff.out expected &&
+ check_cache_at yomin dirty'
+
+test_expect_success \
+ '6 - local addition already has the same.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ git-update-index --add frotz &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >6.out &&
+ diff -u M.out 6.out &&
+ check_cache_at frotz clean'
+
+test_expect_success \
+ '7 - local addition already has the same.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo frotz >frotz &&
+ git-update-index --add frotz &&
+ echo frotz frotz >frotz &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >7.out &&
+ diff -u M.out 7.out &&
+ check_cache_at frotz dirty'
+
+test_expect_success \
+ '8 - conflicting addition.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo frotz frotz >frotz &&
+ git-update-index --add frotz &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '9 - conflicting addition.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo frotz frotz >frotz &&
+ git-update-index --add frotz &&
+ echo frotz >frotz &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '10 - path removed.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo rezrov >rezrov &&
+ git-update-index --add rezrov &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >10.out &&
+ diff -u M.out 10.out'
+
+test_expect_success \
+ '11 - dirty path removed.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo rezrov >rezrov &&
+ git-update-index --add rezrov &&
+ echo rezrov rezrov >rezrov &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '12 - unmatching local changes being removed.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo rezrov rezrov >rezrov &&
+ git-update-index --add rezrov &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '13 - unmatching local changes being removed.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo rezrov rezrov >rezrov &&
+ git-update-index --add rezrov &&
+ echo rezrov >rezrov &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+cat >expected <<EOF
+-100644 X 0 nitfol
++100644 X 0 nitfol
+EOF
+
+test_expect_success \
+ '14 - unchanged in two heads.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo nitfol nitfol >nitfol &&
+ git-update-index --add nitfol &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >14.out || return 1
+ diff -u M.out 14.out >14diff.out
+ compare_change 14diff.out expected &&
+ check_cache_at nitfol clean'
+
+test_expect_success \
+ '15 - unchanged in two heads.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo nitfol nitfol >nitfol &&
+ git-update-index --add nitfol &&
+ echo nitfol nitfol nitfol >nitfol &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >15.out || return 1
+ diff -u M.out 15.out >15diff.out
+ compare_change 15diff.out expected &&
+ check_cache_at nitfol dirty'
+
+test_expect_success \
+ '16 - conflicting local change.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo bozbar bozbar >bozbar &&
+ git-update-index --add bozbar &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '17 - conflicting local change.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ echo bozbar bozbar >bozbar &&
+ git-update-index --add bozbar &&
+ echo bozbar bozbar bozbar >bozbar &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '18 - local change already having a good result.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ cat bozbar-new >bozbar &&
+ git-update-index --add bozbar &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >18.out &&
+ diff -u M.out 18.out &&
+ check_cache_at bozbar clean'
+
+test_expect_success \
+ '19 - local change already having a good result, further modified.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ cat bozbar-new >bozbar &&
+ git-update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >19.out &&
+ diff -u M.out 19.out &&
+ check_cache_at bozbar dirty'
+
+test_expect_success \
+ '20 - no local change, use new tree.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ cat bozbar-old >bozbar &&
+ git-update-index --add bozbar &&
+ read_tree_twoway $treeH $treeM &&
+ git-ls-files --stage >20.out &&
+ diff -u M.out 20.out &&
+ check_cache_at bozbar dirty'
+
+test_expect_success \
+ '21 - no local change, dirty cache.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ cat bozbar-old >bozbar &&
+ git-update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+# This fails with straight two-way fast forward.
+test_expect_success \
+ '22 - local change cache updated.' \
+ 'rm -f .git/index &&
+ git-read-tree $treeH &&
+ git-checkout-index -u -f -q -a &&
+ sed -e "s/such as/SUCH AS/" bozbar-old >bozbar &&
+ git-update-index --add bozbar &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+# Also make sure we did not break DF vs DF/DF case.
+test_expect_success \
+ 'DF vs DF/DF case setup.' \
+ 'rm -f .git/index &&
+ echo DF >DF &&
+ git-update-index --add DF &&
+ treeDF=`git-write-tree` &&
+ echo treeDF $treeDF &&
+ git-ls-tree $treeDF &&
+
+ rm -f DF &&
+ mkdir DF &&
+ echo DF/DF >DF/DF &&
+ git-update-index --add --remove DF DF/DF &&
+ treeDFDF=`git-write-tree` &&
+ echo treeDFDF $treeDFDF &&
+ git-ls-tree $treeDFDF &&
+ git-ls-files --stage >DFDF.out'
+
+test_expect_success \
+ 'DF vs DF/DF case test.' \
+ 'rm -f .git/index &&
+ rm -fr DF &&
+ echo DF >DF &&
+ git-update-index --add DF &&
+ read_tree_twoway $treeDF $treeDFDF &&
+ git-ls-files --stage >DFDFcheck.out &&
+ diff -u DFDF.out DFDFcheck.out &&
+ check_cache_at DF/DF dirty &&
+ :'
+
+test_done
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
new file mode 100755
index 0000000000..da3c81357b
--- /dev/null
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -0,0 +1,344 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Two way merge with read-tree -m -u $H $M
+
+This is identical to t1001, but uses -u to update the work tree as well.
+
+'
+. ./test-lib.sh
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+compare_change () {
+ sed >current \
+ -e '/^--- /d; /^+++ /d; /^@@ /d;' \
+ -e 's/^\(.[0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /' "$1"
+ diff -u expected current
+}
+
+check_cache_at () {
+ clean_if_empty=`git-diff-files -- "$1"`
+ case "$clean_if_empty" in
+ '') echo "$1: clean" ;;
+ ?*) echo "$1: dirty" ;;
+ esac
+ case "$2,$clean_if_empty" in
+ clean,) : ;;
+ clean,?*) false ;;
+ dirty,) false ;;
+ dirty,?*) : ;;
+ esac
+}
+
+test_expect_success \
+ setup \
+ 'echo frotz >frotz &&
+ echo nitfol >nitfol &&
+ echo bozbar >bozbar &&
+ echo rezrov >rezrov &&
+ git-update-index --add nitfol bozbar rezrov &&
+ treeH=`git-write-tree` &&
+ echo treeH $treeH &&
+ git-ls-tree $treeH &&
+
+ echo gnusto >bozbar &&
+ git-update-index --add frotz bozbar --force-remove rezrov &&
+ git-ls-files --stage >M.out &&
+ treeM=`git-write-tree` &&
+ echo treeM $treeM &&
+ git-ls-tree $treeM &&
+ sum bozbar frotz nitfol >M.sum &&
+ git-diff-tree $treeH $treeM'
+
+test_expect_success \
+ '1, 2, 3 - no carry forward' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >1-3.out &&
+ cmp M.out 1-3.out &&
+ sum bozbar frotz nitfol >actual3.sum &&
+ cmp M.sum actual3.sum &&
+ check_cache_at bozbar clean &&
+ check_cache_at frotz clean &&
+ check_cache_at nitfol clean'
+
+test_expect_success \
+ '4 - carry forward local addition.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo "+100644 X 0 yomin" >expected &&
+ echo yomin >yomin &&
+ git-update-index --add yomin &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >4.out || return 1
+ diff -U0 M.out 4.out >4diff.out
+ compare_change 4diff.out expected &&
+ check_cache_at yomin clean &&
+ sum bozbar frotz nitfol >actual4.sum &&
+ cmp M.sum actual4.sum &&
+ echo yomin >yomin1 &&
+ diff yomin yomin1 &&
+ rm -f yomin1'
+
+test_expect_success \
+ '5 - carry forward local addition.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ git-read-tree -m -u $treeH &&
+ echo yomin >yomin &&
+ git-update-index --add yomin &&
+ echo yomin yomin >yomin &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >5.out || return 1
+ diff -U0 M.out 5.out >5diff.out
+ compare_change 5diff.out expected &&
+ check_cache_at yomin dirty &&
+ sum bozbar frotz nitfol >actual5.sum &&
+ cmp M.sum actual5.sum &&
+ : dirty index should have prevented -u from checking it out. &&
+ echo yomin yomin >yomin1 &&
+ diff yomin yomin1 &&
+ rm -f yomin1'
+
+test_expect_success \
+ '6 - local addition already has the same.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo frotz >frotz &&
+ git-update-index --add frotz &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >6.out &&
+ diff -U0 M.out 6.out &&
+ check_cache_at frotz clean &&
+ sum bozbar frotz nitfol >actual3.sum &&
+ cmp M.sum actual3.sum &&
+ echo frotz >frotz1 &&
+ diff frotz frotz1 &&
+ rm -f frotz1'
+
+test_expect_success \
+ '7 - local addition already has the same.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo frotz >frotz &&
+ git-update-index --add frotz &&
+ echo frotz frotz >frotz &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >7.out &&
+ diff -U0 M.out 7.out &&
+ check_cache_at frotz dirty &&
+ sum bozbar frotz nitfol >actual7.sum &&
+ if cmp M.sum actual7.sum; then false; else :; fi &&
+ : dirty index should have prevented -u from checking it out. &&
+ echo frotz frotz >frotz1 &&
+ diff frotz frotz1 &&
+ rm -f frotz1'
+
+test_expect_success \
+ '8 - conflicting addition.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo frotz frotz >frotz &&
+ git-update-index --add frotz &&
+ if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '9 - conflicting addition.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo frotz frotz >frotz &&
+ git-update-index --add frotz &&
+ echo frotz >frotz &&
+ if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '10 - path removed.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo rezrov >rezrov &&
+ git-update-index --add rezrov &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >10.out &&
+ cmp M.out 10.out &&
+ sum bozbar frotz nitfol >actual10.sum &&
+ cmp M.sum actual10.sum'
+
+test_expect_success \
+ '11 - dirty path removed.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo rezrov >rezrov &&
+ git-update-index --add rezrov &&
+ echo rezrov rezrov >rezrov &&
+ if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '12 - unmatching local changes being removed.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo rezrov rezrov >rezrov &&
+ git-update-index --add rezrov &&
+ if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '13 - unmatching local changes being removed.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo rezrov rezrov >rezrov &&
+ git-update-index --add rezrov &&
+ echo rezrov >rezrov &&
+ if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+cat >expected <<EOF
+-100644 X 0 nitfol
++100644 X 0 nitfol
+EOF
+
+test_expect_success \
+ '14 - unchanged in two heads.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo nitfol nitfol >nitfol &&
+ git-update-index --add nitfol &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >14.out || return 1
+ diff -U0 M.out 14.out >14diff.out
+ compare_change 14diff.out expected &&
+ sum bozbar frotz >actual14.sum &&
+ grep -v nitfol M.sum > expected14.sum &&
+ cmp expected14.sum actual14.sum &&
+ sum bozbar frotz nitfol >actual14a.sum &&
+ if cmp M.sum actual14a.sum; then false; else :; fi &&
+ check_cache_at nitfol clean &&
+ echo nitfol nitfol >nitfol1 &&
+ diff nitfol nitfol1 &&
+ rm -f nitfol1'
+
+test_expect_success \
+ '15 - unchanged in two heads.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo nitfol nitfol >nitfol &&
+ git-update-index --add nitfol &&
+ echo nitfol nitfol nitfol >nitfol &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >15.out || return 1
+ diff -U0 M.out 15.out >15diff.out
+ compare_change 15diff.out expected &&
+ check_cache_at nitfol dirty &&
+ sum bozbar frotz >actual15.sum &&
+ grep -v nitfol M.sum > expected15.sum &&
+ cmp expected15.sum actual15.sum &&
+ sum bozbar frotz nitfol >actual15a.sum &&
+ if cmp M.sum actual15a.sum; then false; else :; fi &&
+ echo nitfol nitfol nitfol >nitfol1 &&
+ diff nitfol nitfol1 &&
+ rm -f nitfol1'
+
+test_expect_success \
+ '16 - conflicting local change.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo bozbar bozbar >bozbar &&
+ git-update-index --add bozbar &&
+ if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '17 - conflicting local change.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo bozbar bozbar >bozbar &&
+ git-update-index --add bozbar &&
+ echo bozbar bozbar bozbar >bozbar &&
+ if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '18 - local change already having a good result.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo gnusto >bozbar &&
+ git-update-index --add bozbar &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >18.out &&
+ diff -U0 M.out 18.out &&
+ check_cache_at bozbar clean &&
+ sum bozbar frotz nitfol >actual18.sum &&
+ cmp M.sum actual18.sum'
+
+test_expect_success \
+ '19 - local change already having a good result, further modified.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo gnusto >bozbar &&
+ git-update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >19.out &&
+ diff -U0 M.out 19.out &&
+ check_cache_at bozbar dirty &&
+ sum frotz nitfol >actual19.sum &&
+ grep -v bozbar M.sum > expected19.sum &&
+ cmp expected19.sum actual19.sum &&
+ sum bozbar frotz nitfol >actual19a.sum &&
+ if cmp M.sum actual19a.sum; then false; else :; fi &&
+ echo gnusto gnusto >bozbar1 &&
+ diff bozbar bozbar1 &&
+ rm -f bozbar1'
+
+test_expect_success \
+ '20 - no local change, use new tree.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo bozbar >bozbar &&
+ git-update-index --add bozbar &&
+ git-read-tree -m -u $treeH $treeM &&
+ git-ls-files --stage >20.out &&
+ diff -U0 M.out 20.out &&
+ check_cache_at bozbar clean &&
+ sum bozbar frotz nitfol >actual20.sum &&
+ cmp M.sum actual20.sum'
+
+test_expect_success \
+ '21 - no local change, dirty cache.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git-read-tree --reset -u $treeH &&
+ echo bozbar >bozbar &&
+ git-update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ if git-read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+# Also make sure we did not break DF vs DF/DF case.
+test_expect_success \
+ 'DF vs DF/DF case setup.' \
+ 'rm -f .git/index
+ echo DF >DF &&
+ git-update-index --add DF &&
+ treeDF=`git-write-tree` &&
+ echo treeDF $treeDF &&
+ git-ls-tree $treeDF &&
+
+ rm -f DF &&
+ mkdir DF &&
+ echo DF/DF >DF/DF &&
+ git-update-index --add --remove DF DF/DF &&
+ treeDFDF=`git-write-tree` &&
+ echo treeDFDF $treeDFDF &&
+ git-ls-tree $treeDFDF &&
+ git-ls-files --stage >DFDF.out'
+
+test_expect_success \
+ 'DF vs DF/DF case test.' \
+ 'rm -f .git/index &&
+ rm -fr DF &&
+ echo DF >DF &&
+ git-update-index --add DF &&
+ git-read-tree -m -u $treeDF $treeDFDF &&
+ git-ls-files --stage >DFDFcheck.out &&
+ diff -U0 DFDF.out DFDFcheck.out &&
+ check_cache_at DF/DF clean'
+
+test_done
diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh
new file mode 100755
index 0000000000..19a0ed4d20
--- /dev/null
+++ b/t/t1100-commit-tree-options.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# Copyright (C) 2005 Rene Scharfe
+#
+
+test_description='git-commit-tree options test
+
+This test checks that git-commit-tree can create a specific commit
+object by defining all environment variables that it understands.
+'
+
+. ./test-lib.sh
+
+cat >expected <<EOF
+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
+author Author Name <author@email> 1117148400 +0000
+committer Committer Name <committer@email> 1117150200 +0000
+
+comment text
+EOF
+
+test_expect_success \
+ 'test preparation: write empty tree' \
+ 'git-write-tree >treeid'
+
+test_expect_success \
+ 'construct commit' \
+ 'echo comment text |
+ GIT_AUTHOR_NAME="Author Name" \
+ GIT_AUTHOR_EMAIL="author@email" \
+ GIT_AUTHOR_DATE="2005-05-26 23:00" \
+ GIT_COMMITTER_NAME="Committer Name" \
+ GIT_COMMITTER_EMAIL="committer@email" \
+ GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ TZ=GMT git-commit-tree `cat treeid` >commitid 2>/dev/null'
+
+test_expect_success \
+ 'read commit' \
+ 'git-cat-file commit `cat commitid` >commit'
+
+test_expect_success \
+ 'compare commit' \
+ 'diff expected commit'
+
+test_done
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
new file mode 100755
index 0000000000..c7db20e7f3
--- /dev/null
+++ b/t/t1200-tutorial.sh
@@ -0,0 +1,162 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test git-rev-parse with different parent options'
+
+. ./test-lib.sh
+
+echo "Hello World" > hello
+echo "Silly example" > example
+
+git-update-index --add hello example
+
+test_expect_success 'blob' "test blob = \"$(git-cat-file -t 557db03)\""
+
+test_expect_success 'blob 557db03' "test \"Hello World\" = \"$(git-cat-file blob 557db03)\""
+
+echo "It's a new day for git" >>hello
+cat > diff.expect << EOF
+diff --git a/hello b/hello
+index 557db03..263414f 100644
+--- a/hello
++++ b/hello
+@@ -1 +1,2 @@
+ Hello World
++It's a new day for git
+EOF
+git-diff-files -p > diff.output
+test_expect_success 'git-diff-files -p' 'cmp diff.expect diff.output'
+git diff > diff.output
+test_expect_success 'git diff' 'cmp diff.expect diff.output'
+
+tree=$(git-write-tree 2>/dev/null)
+
+test_expect_success 'tree' "test 8988da15d077d4829fc51d8544c097def6644dbb = $tree"
+
+output="$(echo "Initial commit" | git-commit-tree $(git-write-tree) 2>&1 > .git/refs/heads/master)"
+
+test_expect_success 'commit' "test 'Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb' = \"$output\""
+
+git-diff-index -p HEAD > diff.output
+test_expect_success 'git-diff-index -p HEAD' 'cmp diff.expect diff.output'
+
+git diff HEAD > diff.output
+test_expect_success 'git diff HEAD' 'cmp diff.expect diff.output'
+
+#rm hello
+#test_expect_success 'git-read-tree --reset HEAD' "git-read-tree --reset HEAD ; test \"hello: needs update\" = \"$(git-update-index --refresh)\""
+
+cat > whatchanged.expect << EOF
+commit VARIABLE
+Author: VARIABLE
+Date: VARIABLE
+
+ Initial commit
+
+diff --git a/example b/example
+new file mode 100644
+index 0000000..f24c74a
+--- /dev/null
++++ b/example
+@@ -0,0 +1 @@
++Silly example
+diff --git a/hello b/hello
+new file mode 100644
+index 0000000..557db03
+--- /dev/null
++++ b/hello
+@@ -0,0 +1 @@
++Hello World
+EOF
+
+git-whatchanged -p --root | \
+ sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
+ -e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
+> whatchanged.output
+test_expect_success 'git-whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'
+
+git tag my-first-tag
+test_expect_success 'git tag my-first-tag' 'cmp .git/refs/heads/master .git/refs/tags/my-first-tag'
+
+# TODO: test git-clone
+
+git checkout -b mybranch
+test_expect_success 'git checkout -b mybranch' 'cmp .git/refs/heads/master .git/refs/heads/mybranch'
+
+cat > branch.expect <<EOF
+ master
+* mybranch
+EOF
+
+git branch > branch.output
+test_expect_success 'git branch' 'cmp branch.expect branch.output'
+
+git checkout mybranch
+echo "Work, work, work" >>hello
+git commit -m 'Some work.' -i hello
+
+git checkout master
+
+echo "Play, play, play" >>hello
+echo "Lots of fun" >>example
+git commit -m 'Some fun.' -i hello example
+
+test_expect_failure 'git resolve now fails' 'git resolve HEAD mybranch "Merge work in mybranch"'
+
+cat > hello << EOF
+Hello World
+It's a new day for git
+Play, play, play
+Work, work, work
+EOF
+
+git commit -m 'Merged "mybranch" changes.' -i hello
+
+test_done
+
+cat > show-branch.expect << EOF
+* [master] Merged "mybranch" changes.
+ ! [mybranch] Some work.
+--
+- [master] Merged "mybranch" changes.
+*+ [mybranch] Some work.
+EOF
+
+git show-branch --topo-order master mybranch > show-branch.output
+test_expect_success 'git show-branch' 'cmp show-branch.expect show-branch.output'
+
+git checkout mybranch
+
+cat > resolve.expect << EOF
+Updating from VARIABLE to VARIABLE
+ example | 1 +
+ hello | 1 +
+ 2 files changed, 2 insertions(+), 0 deletions(-)
+EOF
+
+git resolve HEAD master "Merge upstream changes." | \
+ sed -e "1s/[0-9a-f]\{40\}/VARIABLE/g" > resolve.output
+test_expect_success 'git resolve' 'cmp resolve.expect resolve.output'
+
+cat > show-branch2.expect << EOF
+! [master] Merged "mybranch" changes.
+ * [mybranch] Merged "mybranch" changes.
+--
+-- [master] Merged "mybranch" changes.
+EOF
+
+git show-branch --topo-order master mybranch > show-branch2.output
+test_expect_success 'git show-branch' 'cmp show-branch2.expect show-branch2.output'
+
+# TODO: test git fetch
+
+# TODO: test git push
+
+test_expect_success 'git repack' 'git repack'
+test_expect_success 'git prune-packed' 'git prune-packed'
+test_expect_failure '-> only packed objects' 'find -type f .git/objects/[0-9a-f][0-9a-f]'
+
+test_done
+
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
new file mode 100755
index 0000000000..8260d57b63
--- /dev/null
+++ b/t/t1300-repo-config.sh
@@ -0,0 +1,313 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test git-repo-config in different settings'
+
+. ./test-lib.sh
+
+test -f .git/config && rm .git/config
+
+git-repo-config core.penguin "little blue"
+
+cat > expect << EOF
+[core]
+ penguin = little blue
+EOF
+
+test_expect_success 'initial' 'cmp .git/config expect'
+
+git-repo-config Core.Movie BadPhysics
+
+cat > expect << EOF
+[core]
+ penguin = little blue
+ Movie = BadPhysics
+EOF
+
+test_expect_success 'mixed case' 'cmp .git/config expect'
+
+git-repo-config Cores.WhatEver Second
+
+cat > expect << EOF
+[core]
+ penguin = little blue
+ Movie = BadPhysics
+[Cores]
+ WhatEver = Second
+EOF
+
+test_expect_success 'similar section' 'cmp .git/config expect'
+
+git-repo-config CORE.UPPERCASE true
+
+cat > expect << EOF
+[core]
+ penguin = little blue
+ Movie = BadPhysics
+ UPPERCASE = true
+[Cores]
+ WhatEver = Second
+EOF
+
+test_expect_success 'similar section' 'cmp .git/config expect'
+
+test_expect_success 'replace with non-match' \
+ 'git-repo-config core.penguin kingpin !blue'
+
+test_expect_success 'replace with non-match (actually matching)' \
+ 'git-repo-config core.penguin "very blue" !kingpin'
+
+cat > expect << EOF
+[core]
+ penguin = very blue
+ Movie = BadPhysics
+ UPPERCASE = true
+ penguin = kingpin
+[Cores]
+ WhatEver = Second
+EOF
+
+test_expect_success 'non-match result' 'cmp .git/config expect'
+
+cat > .git/config << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha ="beta" # last silly comment
+haha = hello
+ haha = bello
+[nextSection] noNewline = ouch
+EOF
+
+cp .git/config .git/config2
+
+test_expect_success 'multiple unset' \
+ 'git-repo-config --unset-all beta.haha'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'multiple unset is correct' 'cmp .git/config expect'
+
+mv .git/config2 .git/config
+
+test_expect_success '--replace-all' \
+ 'git-repo-config --replace-all beta.haha gamma'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha = gamma
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'all replaced' 'cmp .git/config expect'
+
+git-repo-config beta.haha alpha
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha = alpha
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'really mean test' 'cmp .git/config expect'
+
+git-repo-config nextsection.nonewline wow
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha = alpha
+[nextSection]
+ nonewline = wow
+EOF
+
+test_expect_success 'really really mean test' 'cmp .git/config expect'
+
+test_expect_success 'get value' 'test alpha = $(git-repo-config beta.haha)'
+git-repo-config --unset beta.haha
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ nonewline = wow
+EOF
+
+test_expect_success 'unset' 'cmp .git/config expect'
+
+git-repo-config nextsection.NoNewLine "wow2 for me" "for me$"
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ nonewline = wow
+ NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar' 'cmp .git/config expect'
+
+test_expect_success 'non-match' \
+ 'git-repo-config --get nextsection.nonewline !for'
+
+test_expect_success 'non-match value' \
+ 'test wow = $(git-repo-config --get nextsection.nonewline !for)'
+
+test_expect_failure 'ambiguous get' \
+ 'git-repo-config --get nextsection.nonewline'
+
+test_expect_success 'get multivar' \
+ 'git-repo-config --get-all nextsection.nonewline'
+
+git-repo-config nextsection.nonewline "wow3" "wow$"
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ nonewline = wow3
+ NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar replace' 'cmp .git/config expect'
+
+test_expect_failure 'ambiguous value' 'git-repo-config nextsection.nonewline'
+
+test_expect_failure 'ambiguous unset' \
+ 'git-repo-config --unset nextsection.nonewline'
+
+test_expect_failure 'invalid unset' \
+ 'git-repo-config --unset somesection.nonewline'
+
+git-repo-config --unset nextsection.nonewline "wow3$"
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar unset' 'cmp .git/config expect'
+
+test_expect_failure 'invalid key' 'git-repo-config inval.2key blabla'
+
+test_expect_success 'correct key' 'git-repo-config 123456.a123 987'
+
+test_expect_success 'hierarchical section' \
+ 'git-repo-config Version.1.2.3eX.Alpha beta'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ NoNewLine = wow2 for me
+[123456]
+ a123 = 987
+[Version "1.2.3eX"]
+ Alpha = beta
+EOF
+
+test_expect_success 'hierarchical section value' 'cmp .git/config expect'
+
+cat > expect << EOF
+beta.noindent=sillyValue
+nextsection.nonewline=wow2 for me
+123456.a123=987
+version.1.2.3eX.alpha=beta
+EOF
+
+test_expect_success 'working --list' \
+ 'git-repo-config --list > output && cmp output expect'
+
+cat > expect << EOF
+beta.noindent sillyValue
+nextsection.nonewline wow2 for me
+EOF
+
+test_expect_success '--get-regexp' \
+ 'git-repo-config --get-regexp in > output && cmp output expect'
+
+cat > .git/config << EOF
+[novalue]
+ variable
+EOF
+
+test_expect_success 'get variable with no value' \
+ 'git-repo-config --get novalue.variable ^$'
+
+git-repo-config > output 2>&1
+
+test_expect_success 'no arguments, but no crash' \
+ "test $? = 129 && grep usage output"
+
+cat > .git/config << EOF
+[a.b]
+ c = d
+EOF
+
+git-repo-config a.x y
+
+cat > expect << EOF
+[a.b]
+ c = d
+[a]
+ x = y
+EOF
+
+test_expect_success 'new section is partial match of another' 'cmp .git/config expect'
+
+git-repo-config b.x y
+git-repo-config a.b c
+
+cat > expect << EOF
+[a.b]
+ c = d
+[a]
+ x = y
+ b = c
+[b]
+ x = y
+EOF
+
+test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
+
+test_done
+
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
new file mode 100755
index 0000000000..df3e993365
--- /dev/null
+++ b/t/t1400-update-ref.sh
@@ -0,0 +1,213 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='Test git-update-ref and basic ref logging'
+. ./test-lib.sh
+
+Z=0000000000000000000000000000000000000000
+A=1111111111111111111111111111111111111111
+B=2222222222222222222222222222222222222222
+C=3333333333333333333333333333333333333333
+D=4444444444444444444444444444444444444444
+E=5555555555555555555555555555555555555555
+F=6666666666666666666666666666666666666666
+m=refs/heads/master
+
+test_expect_success \
+ "create $m" \
+ 'git-update-ref $m $A &&
+ test $A = $(cat .git/$m)'
+test_expect_success \
+ "create $m" \
+ 'git-update-ref $m $B $A &&
+ test $B = $(cat .git/$m)'
+rm -f .git/$m
+
+test_expect_success \
+ "create $m (by HEAD)" \
+ 'git-update-ref HEAD $A &&
+ test $A = $(cat .git/$m)'
+test_expect_success \
+ "create $m (by HEAD)" \
+ 'git-update-ref HEAD $B $A &&
+ test $B = $(cat .git/$m)'
+rm -f .git/$m
+
+test_expect_failure \
+ '(not) create HEAD with old sha1' \
+ 'git-update-ref HEAD $A $B'
+test_expect_failure \
+ "(not) prior created .git/$m" \
+ 'test -f .git/$m'
+rm -f .git/$m
+
+test_expect_success \
+ "create HEAD" \
+ 'git-update-ref HEAD $A'
+test_expect_failure \
+ '(not) change HEAD with wrong SHA1' \
+ 'git-update-ref HEAD $B $Z'
+test_expect_failure \
+ "(not) changed .git/$m" \
+ 'test $B = $(cat .git/$m)'
+rm -f .git/$m
+
+mkdir -p .git/logs/refs/heads
+touch .git/logs/refs/heads/master
+test_expect_success \
+ "create $m (logged by touch)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ git-update-ref HEAD $A -m "Initial Creation" &&
+ test $A = $(cat .git/$m)'
+test_expect_success \
+ "update $m (logged by touch)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:31" \
+ git-update-ref HEAD $B $A -m "Switch" &&
+ test $B = $(cat .git/$m)'
+test_expect_success \
+ "set $m (logged by touch)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:41" \
+ git-update-ref HEAD $A &&
+ test $A = $(cat .git/$m)'
+
+cat >expect <<EOF
+$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch
+$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
+EOF
+test_expect_success \
+ "verifying $m's log" \
+ 'diff expect .git/logs/$m'
+rm -rf .git/$m .git/logs expect
+
+test_expect_success \
+ 'enable core.logAllRefUpdates' \
+ 'git-repo-config core.logAllRefUpdates true &&
+ test true = $(git-repo-config --bool --get core.logAllRefUpdates)'
+
+test_expect_success \
+ "create $m (logged by config)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:32" \
+ git-update-ref HEAD $A -m "Initial Creation" &&
+ test $A = $(cat .git/$m)'
+test_expect_success \
+ "update $m (logged by config)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:33" \
+ git-update-ref HEAD $B $A -m "Switch" &&
+ test $B = $(cat .git/$m)'
+test_expect_success \
+ "set $m (logged by config)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:43" \
+ git-update-ref HEAD $A &&
+ test $A = $(cat .git/$m)'
+
+cat >expect <<EOF
+$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 +0000 Initial Creation
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000 Switch
+$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
+EOF
+test_expect_success \
+ "verifying $m's log" \
+ 'diff expect .git/logs/$m'
+rm -f .git/$m .git/logs/$m expect
+
+git-update-ref $m $D
+cat >.git/logs/$m <<EOF
+$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
+$F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+$Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
+EOF
+
+ed="Thu, 26 May 2005 18:32:00 -0500"
+gd="Thu, 26 May 2005 18:33:00 -0500"
+ld="Thu, 26 May 2005 18:43:00 -0500"
+test_expect_success \
+ 'Query "master@{May 25 2005}" (before history)' \
+ 'rm -f o e
+ git-rev-parse --verify "master@{May 25 2005}" >o 2>e &&
+ test $C = $(cat o) &&
+ test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"'
+test_expect_success \
+ "Query master@{2005-05-25} (before history)" \
+ 'rm -f o e
+ git-rev-parse --verify master@{2005-05-25} >o 2>e &&
+ test $C = $(cat o) &&
+ echo test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \
+ 'rm -f o e
+ git-rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
+ test $C = $(cat o) &&
+ test "warning: Log .git/logs/$m only goes back to $ed." = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
+ 'rm -f o e
+ git-rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
+ test $A = $(cat o) &&
+ test "" = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
+ 'rm -f o e
+ git-rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
+ test $B = $(cat o) &&
+ test "warning: Log .git/logs/$m has gap after $gd." = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
+ 'rm -f o e
+ git-rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
+ test $Z = $(cat o) &&
+ test "" = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
+ 'rm -f o e
+ git-rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
+ test $E = $(cat o) &&
+ test "" = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{2005-05-28}" (past end of history)' \
+ 'rm -f o e
+ git-rev-parse --verify "master@{2005-05-28}" >o 2>e &&
+ test $D = $(cat o) &&
+ test "warning: Log .git/logs/$m unexpectedly ended on $ld." = "$(cat e)"'
+
+
+rm -f .git/$m .git/logs/$m expect
+
+test_expect_success \
+ 'creating initial files' \
+ 'echo TEST >F &&
+ git-add F &&
+ GIT_AUTHOR_DATE="2005-05-26 23:30" \
+ GIT_COMMITTER_DATE="2005-05-26 23:30" git-commit -m add -a &&
+ h_TEST=$(git-rev-parse --verify HEAD)
+ echo The other day this did not work. >M &&
+ echo And then Bob told me how to fix it. >>M &&
+ echo OTHER >F &&
+ GIT_AUTHOR_DATE="2005-05-26 23:41" \
+ GIT_COMMITTER_DATE="2005-05-26 23:41" git-commit -F M -a &&
+ h_OTHER=$(git-rev-parse --verify HEAD)
+ rm -f M'
+
+cat >expect <<EOF
+$Z $h_TEST $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 commit: add
+$h_TEST $h_OTHER $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000 commit: The other day this did not work.
+EOF
+test_expect_success \
+ 'git-commit logged updates' \
+ 'diff expect .git/logs/$m'
+unset h_TEST h_OTHER
+
+test_expect_success \
+ 'git-cat-file blob master:F (expect OTHER)' \
+ 'test OTHER = $(git-cat-file blob master:F)'
+test_expect_success \
+ 'git-cat-file blob master@{2005-05-26 23:30}:F (expect TEST)' \
+ 'test TEST = $(git-cat-file blob "master@{2005-05-26 23:30}:F")'
+test_expect_success \
+ 'git-cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' \
+ 'test OTHER = $(git-cat-file blob "master@{2005-05-26 23:42}:F")'
+
+test_done
diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh
new file mode 100755
index 0000000000..03ea4dece4
--- /dev/null
+++ b/t/t2000-checkout-cache-clash.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-checkout-index test.
+
+This test registers the following filesystem structure in the
+cache:
+
+ path0 - a file
+ path1/file1 - a file in a directory
+
+And then tries to checkout in a work tree that has the following:
+
+ path0/file0 - a file in a directory
+ path1 - a file
+
+The git-checkout-index command should fail when attempting to checkout
+path0, finding it is occupied by a directory, and path1/file1, finding
+path1 is occupied by a non-directory. With "-f" flag, it should remove
+the conflicting paths and succeed.
+'
+. ./test-lib.sh
+
+date >path0
+mkdir path1
+date >path1/file1
+
+test_expect_success \
+ 'git-update-index --add various paths.' \
+ 'git-update-index --add path0 path1/file1'
+
+rm -fr path0 path1
+mkdir path0
+date >path0/file0
+date >path1
+
+test_expect_failure \
+ 'git-checkout-index without -f should fail on conflicting work tree.' \
+ 'git-checkout-index -a'
+
+test_expect_success \
+ 'git-checkout-index with -f should succeed.' \
+ 'git-checkout-index -f -a'
+
+test_expect_success \
+ 'git-checkout-index conflicting paths.' \
+ 'test -f path0 && test -d path1 && test -f path1/file1'
+
+test_done
+
+
diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh
new file mode 100755
index 0000000000..0dcab8f1de
--- /dev/null
+++ b/t/t2001-checkout-cache-clash.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-checkout-index test.
+
+This test registers the following filesystem structure in the cache:
+
+ path0/file0 - a file in a directory
+ path1/file1 - a file in a directory
+
+and attempts to check it out when the work tree has:
+
+ path0/file0 - a file in a directory
+ path1 - a symlink pointing at "path0"
+
+Checkout cache should fail to extract path1/file1 because the leading
+path path1 is occupied by a non-directory. With "-f" it should remove
+the symlink path1 and create directory path1 and file path1/file1.
+'
+. ./test-lib.sh
+
+show_files() {
+ # show filesystem files, just [-dl] for type and name
+ find path? -ls |
+ sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /'
+ # what's in the cache, just mode and name
+ git-ls-files --stage |
+ sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /'
+ # what's in the tree, just mode and name.
+ git-ls-tree -r "$1" |
+ sed -e 's/^\([0-9]*\) [^ ]* [0-9a-f]* /tr: \1 /'
+}
+
+mkdir path0
+date >path0/file0
+test_expect_success \
+ 'git-update-index --add path0/file0' \
+ 'git-update-index --add path0/file0'
+test_expect_success \
+ 'writing tree out with git-write-tree' \
+ 'tree1=$(git-write-tree)'
+test_debug 'show_files $tree1'
+
+mkdir path1
+date >path1/file1
+test_expect_success \
+ 'git-update-index --add path1/file1' \
+ 'git-update-index --add path1/file1'
+test_expect_success \
+ 'writing tree out with git-write-tree' \
+ 'tree2=$(git-write-tree)'
+test_debug 'show_files $tree2'
+
+rm -fr path1
+test_expect_success \
+ 'read previously written tree and checkout.' \
+ 'git-read-tree -m $tree1 && git-checkout-index -f -a'
+test_debug 'show_files $tree1'
+
+ln -s path0 path1
+test_expect_success \
+ 'git-update-index --add a symlink.' \
+ 'git-update-index --add path1'
+test_expect_success \
+ 'writing tree out with git-write-tree' \
+ 'tree3=$(git-write-tree)'
+test_debug 'show_files $tree3'
+
+# Morten says "Got that?" here.
+# Test begins.
+
+test_expect_success \
+ 'read previously written tree and checkout.' \
+ 'git-read-tree $tree2 && git-checkout-index -f -a'
+test_debug 'show_files $tree2'
+
+test_expect_success \
+ 'checking out conflicting path with -f' \
+ 'test ! -h path0 && test -d path0 &&
+ test ! -h path1 && test -d path1 &&
+ test ! -h path0/file0 && test -f path0/file0 &&
+ test ! -h path1/file1 && test -f path1/file1'
+
+test_done
+
diff --git a/t/t2002-checkout-cache-u.sh b/t/t2002-checkout-cache-u.sh
new file mode 100755
index 0000000000..4352ddb1cb
--- /dev/null
+++ b/t/t2002-checkout-cache-u.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-checkout-index -u test.
+
+With -u flag, git-checkout-index internally runs the equivalent of
+git-update-index --refresh on the checked out entry.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'preparation' '
+echo frotz >path0 &&
+git-update-index --add path0 &&
+t=$(git-write-tree)'
+
+test_expect_failure \
+'without -u, git-checkout-index smudges stat information.' '
+rm -f path0 &&
+git-read-tree $t &&
+git-checkout-index -f -a &&
+git-diff-files | diff - /dev/null'
+
+test_expect_success \
+'with -u, git-checkout-index picks up stat information from new files.' '
+rm -f path0 &&
+git-read-tree $t &&
+git-checkout-index -u -f -a &&
+git-diff-files | diff - /dev/null'
+
+test_done
diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh
new file mode 100755
index 0000000000..f9bc90aee4
--- /dev/null
+++ b/t/t2003-checkout-cache-mkdir.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-checkout-index --prefix test.
+
+This test makes sure that --prefix option works as advertised, and
+also verifies that such leading path may contain symlinks, unlike
+the GIT controlled paths.
+'
+
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'mkdir path1 &&
+ echo frotz >path0 &&
+ echo rezrov >path1/file1 &&
+ git-update-index --add path0 path1/file1'
+
+test_expect_success \
+ 'have symlink in place where dir is expected.' \
+ 'rm -fr path0 path1 &&
+ mkdir path2 &&
+ ln -s path2 path1 &&
+ git-checkout-index -f -a &&
+ test ! -h path1 && test -d path1 &&
+ test -f path1/file1 && test ! -f path2/file1'
+
+test_expect_success \
+ 'use --prefix=path2/' \
+ 'rm -fr path0 path1 path2 &&
+ mkdir path2 &&
+ git-checkout-index --prefix=path2/ -f -a &&
+ test -f path2/path0 &&
+ test -f path2/path1/file1 &&
+ test ! -f path0 &&
+ test ! -f path1/file1'
+
+test_expect_success \
+ 'use --prefix=tmp-' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ git-checkout-index --prefix=tmp- -f -a &&
+ test -f tmp-path0 &&
+ test -f tmp-path1/file1 &&
+ test ! -f path0 &&
+ test ! -f path1/file1'
+
+test_expect_success \
+ 'use --prefix=tmp- but with a conflicting file and dir' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ echo nitfol >tmp-path1 &&
+ mkdir tmp-path0 &&
+ git-checkout-index --prefix=tmp- -f -a &&
+ test -f tmp-path0 &&
+ test -f tmp-path1/file1 &&
+ test ! -f path0 &&
+ test ! -f path1/file1'
+
+# Linus fix #1
+test_expect_success \
+ 'use --prefix=tmp/orary/ where tmp is a symlink' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ mkdir tmp1 tmp1/orary &&
+ ln -s tmp1 tmp &&
+ git-checkout-index --prefix=tmp/orary/ -f -a &&
+ test -d tmp1/orary &&
+ test -f tmp1/orary/path0 &&
+ test -f tmp1/orary/path1/file1 &&
+ test -h tmp'
+
+# Linus fix #2
+test_expect_success \
+ 'use --prefix=tmp/orary- where tmp is a symlink' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ mkdir tmp1 &&
+ ln -s tmp1 tmp &&
+ git-checkout-index --prefix=tmp/orary- -f -a &&
+ test -f tmp1/orary-path0 &&
+ test -f tmp1/orary-path1/file1 &&
+ test -h tmp'
+
+# Linus fix #3
+test_expect_success \
+ 'use --prefix=tmp- where tmp-path1 is a symlink' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ mkdir tmp1 &&
+ ln -s tmp1 tmp-path1 &&
+ git-checkout-index --prefix=tmp- -f -a &&
+ test -f tmp-path0 &&
+ test ! -h tmp-path1 &&
+ test -d tmp-path1 &&
+ test -f tmp-path1/file1'
+
+test_done
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
new file mode 100755
index 0000000000..c100959cad
--- /dev/null
+++ b/t/t2004-checkout-cache-temp.sh
@@ -0,0 +1,212 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='git-checkout-index --temp test.
+
+With --temp flag, git-checkout-index writes to temporary merge files
+rather than the tracked path.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'preparation' '
+mkdir asubdir &&
+echo tree1path0 >path0 &&
+echo tree1path1 >path1 &&
+echo tree1path3 >path3 &&
+echo tree1path4 >path4 &&
+echo tree1asubdir/path5 >asubdir/path5 &&
+git-update-index --add path0 path1 path3 path4 asubdir/path5 &&
+t1=$(git-write-tree) &&
+rm -f path* .merge_* out .git/index &&
+echo tree2path0 >path0 &&
+echo tree2path1 >path1 &&
+echo tree2path2 >path2 &&
+echo tree2path4 >path4 &&
+git-update-index --add path0 path1 path2 path4 &&
+t2=$(git-write-tree) &&
+rm -f path* .merge_* out .git/index &&
+echo tree2path0 >path0 &&
+echo tree3path1 >path1 &&
+echo tree3path2 >path2 &&
+echo tree3path3 >path3 &&
+git-update-index --add path0 path1 path2 path3 &&
+t3=$(git-write-tree)'
+
+test_expect_success \
+'checkout one stage 0 to temporary file' '
+rm -f path* .merge_* out .git/index &&
+git-read-tree $t1 &&
+git-checkout-index --temp -- path1 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path1 &&
+p=$(cut "-d " -f1 out) &&
+test -f $p &&
+test $(cat $p) = tree1path1'
+
+test_expect_success \
+'checkout all stage 0 to temporary files' '
+rm -f path* .merge_* out .git/index &&
+git-read-tree $t1 &&
+git-checkout-index -a --temp >out &&
+test $(wc -l <out) = 5 &&
+for f in path0 path1 path3 path4 asubdir/path5
+do
+ test $(grep $f out | cut "-d " -f2) = $f &&
+ p=$(grep $f out | cut "-d " -f1) &&
+ test -f $p &&
+ test $(cat $p) = tree1$f
+done'
+
+test_expect_success \
+'prepare 3-way merge' '
+rm -f path* .merge_* out .git/index &&
+git-read-tree -m $t1 $t2 $t3'
+
+test_expect_success \
+'checkout one stage 2 to temporary file' '
+rm -f path* .merge_* out &&
+git-checkout-index --stage=2 --temp -- path1 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path1 &&
+p=$(cut "-d " -f1 out) &&
+test -f $p &&
+test $(cat $p) = tree2path1'
+
+test_expect_success \
+'checkout all stage 2 to temporary files' '
+rm -f path* .merge_* out &&
+git-checkout-index --all --stage=2 --temp >out &&
+test $(wc -l <out) = 3 &&
+for f in path1 path2 path4
+do
+ test $(grep $f out | cut "-d " -f2) = $f &&
+ p=$(grep $f out | cut "-d " -f1) &&
+ test -f $p &&
+ test $(cat $p) = tree2$f
+done'
+
+test_expect_success \
+'checkout all stages/one file to nothing' '
+rm -f path* .merge_* out &&
+git-checkout-index --stage=all --temp -- path0 >out &&
+test $(wc -l <out) = 0'
+
+test_expect_success \
+'checkout all stages/one file to temporary files' '
+rm -f path* .merge_* out &&
+git-checkout-index --stage=all --temp -- path1 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path1 &&
+cut "-d " -f1 out | (read s1 s2 s3 &&
+test -f $s1 &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s1) = tree1path1 &&
+test $(cat $s2) = tree2path1 &&
+test $(cat $s3) = tree3path1)'
+
+test_expect_success \
+'checkout some stages/one file to temporary files' '
+rm -f path* .merge_* out &&
+git-checkout-index --stage=all --temp -- path2 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path2 &&
+cut "-d " -f1 out | (read s1 s2 s3 &&
+test $s1 = . &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s2) = tree2path2 &&
+test $(cat $s3) = tree3path2)'
+
+test_expect_success \
+'checkout all stages/all files to temporary files' '
+rm -f path* .merge_* out &&
+git-checkout-index -a --stage=all --temp >out &&
+test $(wc -l <out) = 5'
+
+test_expect_success \
+'-- path0: no entry' '
+test x$(grep path0 out | cut "-d " -f2) = x'
+
+test_expect_success \
+'-- path1: all 3 stages' '
+test $(grep path1 out | cut "-d " -f2) = path1 &&
+grep path1 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s1) = tree1path1 &&
+test $(cat $s2) = tree2path1 &&
+test $(cat $s3) = tree3path1)'
+
+test_expect_success \
+'-- path2: no stage 1, have stage 2 and 3' '
+test $(grep path2 out | cut "-d " -f2) = path2 &&
+grep path2 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test $s1 = . &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s2) = tree2path2 &&
+test $(cat $s3) = tree3path2)'
+
+test_expect_success \
+'-- path3: no stage 2, have stage 1 and 3' '
+test $(grep path3 out | cut "-d " -f2) = path3 &&
+grep path3 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test $s2 = . &&
+test -f $s3 &&
+test $(cat $s1) = tree1path3 &&
+test $(cat $s3) = tree3path3)'
+
+test_expect_success \
+'-- path4: no stage 3, have stage 1 and 3' '
+test $(grep path4 out | cut "-d " -f2) = path4 &&
+grep path4 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test -f $s2 &&
+test $s3 = . &&
+test $(cat $s1) = tree1path4 &&
+test $(cat $s2) = tree2path4)'
+
+test_expect_success \
+'-- asubdir/path5: no stage 2 and 3 have stage 1' '
+test $(grep asubdir/path5 out | cut "-d " -f2) = asubdir/path5 &&
+grep asubdir/path5 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test $s2 = . &&
+test $s3 = . &&
+test $(cat $s1) = tree1asubdir/path5)'
+
+test_expect_success \
+'checkout --temp within subdir' '
+(cd asubdir &&
+ git-checkout-index -a --stage=all >out &&
+ test $(wc -l <out) = 1 &&
+ test $(grep path5 out | cut "-d " -f2) = path5 &&
+ grep path5 out | cut "-d " -f1 | (read s1 s2 s3 &&
+ test -f ../$s1 &&
+ test $s2 = . &&
+ test $s3 = . &&
+ test $(cat ../$s1) = tree1asubdir/path5)
+)'
+
+test_expect_success \
+'checkout --temp symlink' '
+rm -f path* .merge_* out .git/index &&
+ln -s b a &&
+git-update-index --add a &&
+t4=$(git-write-tree) &&
+rm -f .git/index &&
+git-read-tree $t4 &&
+git-checkout-index --temp -a >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = a &&
+p=$(cut "-d " -f1 out) &&
+test -f $p &&
+test $(cat $p) = b'
+
+test_done
diff --git a/t/t2100-update-cache-badpath.sh b/t/t2100-update-cache-badpath.sh
new file mode 100755
index 0000000000..5bc0a3bed3
--- /dev/null
+++ b/t/t2100-update-cache-badpath.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-update-index nonsense-path test.
+
+This test creates the following structure in the cache:
+
+ path0 - a file
+ path1 - a symlink
+ path2/file2 - a file in a directory
+ path3/file3 - a file in a directory
+
+and tries to git-update-index --add the following:
+
+ path0/file0 - a file in a directory
+ path1/file1 - a file in a directory
+ path2 - a file
+ path3 - a symlink
+
+All of the attempts should fail.
+'
+
+. ./test-lib.sh
+
+mkdir path2 path3
+date >path0
+ln -s xyzzy path1
+date >path2/file2
+date >path3/file3
+
+test_expect_success \
+ 'git-update-index --add to add various paths.' \
+ 'git-update-index --add -- path0 path1 path2/file2 path3/file3'
+
+rm -fr path?
+
+mkdir path0 path1
+date >path2
+ln -s frotz path3
+date >path0/file0
+date >path1/file1
+
+for p in path0/file0 path1/file1 path2 path3
+do
+ test_expect_failure \
+ "git-update-index to add conflicting path $p should fail." \
+ "git-update-index --add -- $p"
+done
+test_done
diff --git a/t/t2101-update-index-reupdate.sh b/t/t2101-update-index-reupdate.sh
new file mode 100755
index 0000000000..a78ea7f0b0
--- /dev/null
+++ b/t/t2101-update-index-reupdate.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git-update-index --again test.
+'
+
+. ./test-lib.sh
+
+cat > expected <<\EOF
+100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0 file1
+100644 9db8893856a8a02eaa73470054b7c1c5a7c82e47 0 file2
+EOF
+test_expect_success 'update-index --add' \
+ 'echo hello world >file1 &&
+ echo goodbye people >file2 &&
+ git-update-index --add file1 file2 &&
+ git-ls-files -s >current &&
+ cmp current expected'
+
+test_expect_success 'update-index --again' \
+ 'rm -f file1 &&
+ echo hello everybody >file2 &&
+ if git-update-index --again
+ then
+ echo should have refused to remove file1
+ exit 1
+ else
+ echo happy - failed as expected
+ fi &&
+ git-ls-files -s >current &&
+ cmp current expected'
+
+cat > expected <<\EOF
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF
+test_expect_success 'update-index --remove --again' \
+ 'git-update-index --remove --again &&
+ git-ls-files -s >current &&
+ cmp current expected'
+
+test_expect_success 'first commit' 'git-commit -m initial'
+
+cat > expected <<\EOF
+100644 53ab446c3f4e42ce9bb728a0ccb283a101be4979 0 dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF
+test_expect_success 'update-index again' \
+ 'mkdir -p dir1 &&
+ echo hello world >dir1/file3 &&
+ echo goodbye people >file2 &&
+ git-update-index --add file2 dir1/file3 &&
+ echo hello everybody >file2
+ echo happy >dir1/file3 &&
+ git-update-index --again &&
+ git-ls-files -s >current &&
+ cmp current expected'
+
+cat > expected <<\EOF
+100644 d7fb3f695f06c759dbf3ab00046e7cc2da22d10f 0 dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF
+test_expect_success 'update-index --update from subdir' \
+ 'echo not so happy >file2 &&
+ cd dir1 &&
+ cat ../file2 >file3 &&
+ git-update-index --again &&
+ cd .. &&
+ git-ls-files -s >current &&
+ cmp current expected'
+
+cat > expected <<\EOF
+100644 594fb5bb1759d90998e2bf2a38261ae8e243c760 0 dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF
+test_expect_success 'update-index --update with pathspec' \
+ 'echo very happy >file2 &&
+ cat file2 >dir1/file3 &&
+ git-update-index --again dir1/ &&
+ git-ls-files -s >current &&
+ cmp current expected'
+
+test_done
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
new file mode 100755
index 0000000000..adcbe03d56
--- /dev/null
+++ b/t/t3000-ls-files-others.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-ls-files test (--others should pick up symlinks).
+
+This test runs git-ls-files --others with the following on the
+filesystem.
+
+ path0 - a file
+ path1 - a symlink
+ path2/file2 - a file in a directory
+ path3-junk - a file to confuse things
+ path3/file3 - a file in a directory
+'
+. ./test-lib.sh
+
+date >path0
+ln -s xyzzy path1
+mkdir path2 path3
+date >path2/file2
+date >path2-junk
+date >path3/file3
+date >path3-junk
+git-update-index --add path3-junk path3/file3
+
+cat >expected1 <<EOF
+expected1
+expected2
+output
+path0
+path1
+path2-junk
+path2/file2
+EOF
+sed -e 's|path2/file2|path2/|' <expected1 >expected2
+
+test_expect_success \
+ 'git-ls-files --others to show output.' \
+ 'git-ls-files --others >output'
+
+test_expect_success \
+ 'git-ls-files --others should pick up symlinks.' \
+ 'diff output expected1'
+
+test_expect_success \
+ 'git-ls-files --others --directory to show output.' \
+ 'git-ls-files --others --directory >output'
+
+
+test_expect_success \
+ 'git-ls-files --others --directory should not get confused.' \
+ 'diff output expected2'
+
+test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
new file mode 100755
index 0000000000..6979b7c1c0
--- /dev/null
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-ls-files --others --exclude
+
+This test runs git-ls-files --others and tests --exclude patterns.
+'
+
+. ./test-lib.sh
+
+rm -fr one three
+for dir in . one one/two three
+do
+ mkdir -p $dir &&
+ for i in 1 2 3 4 5 6 7 8
+ do
+ >$dir/a.$i
+ done
+done
+
+cat >expect <<EOF
+a.2
+a.4
+a.5
+a.8
+one/a.3
+one/a.4
+one/a.5
+one/a.7
+one/two/a.2
+one/two/a.3
+one/two/a.5
+one/two/a.7
+one/two/a.8
+three/a.2
+three/a.3
+three/a.4
+three/a.5
+three/a.8
+EOF
+
+echo '.gitignore
+output
+expect
+.gitignore
+*.7
+!*.8' >.git/ignore
+
+echo '*.1
+/*.3
+!*.6' >.gitignore
+echo '*.2
+two/*.4
+!*.7
+*.8' >one/.gitignore
+echo '!*.2
+!*.8' >one/two/.gitignore
+
+test_expect_success \
+ 'git-ls-files --others with various exclude options.' \
+ 'git-ls-files --others \
+ --exclude=\*.6 \
+ --exclude-per-directory=.gitignore \
+ --exclude-from=.git/ignore \
+ >output &&
+ diff -u expect output'
+
+# Test \r\n (MSDOS-like systems)
+printf '*.1\r\n/*.3\r\n!*.6\r\n' >.gitignore
+
+test_expect_success \
+ 'git-ls-files --others with \r\n line endings.' \
+ 'git-ls-files --others \
+ --exclude=\*.6 \
+ --exclude-per-directory=.gitignore \
+ --exclude-from=.git/ignore \
+ >output &&
+ diff -u expect output'
+
+test_done
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
new file mode 100755
index 0000000000..b42f1382bc
--- /dev/null
+++ b/t/t3002-ls-files-dashpath.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-ls-files test (-- to terminate the path list).
+
+This test runs git-ls-files --others with the following on the
+filesystem.
+
+ path0 - a file
+ -foo - a file with a funny name.
+ -- - another file with a funny name.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ setup \
+ 'echo frotz >path0 &&
+ echo frotz >./-foo &&
+ echo frotz >./--'
+
+test_expect_success \
+ 'git-ls-files without path restriction.' \
+ 'git-ls-files --others >output &&
+ diff -u output - <<EOF
+--
+-foo
+output
+path0
+EOF
+'
+
+test_expect_success \
+ 'git-ls-files with path restriction.' \
+ 'git-ls-files --others path0 >output &&
+ diff -u output - <<EOF
+path0
+EOF
+'
+
+test_expect_success \
+ 'git-ls-files with path restriction with --.' \
+ 'git-ls-files --others -- path0 >output &&
+ diff -u output - <<EOF
+path0
+EOF
+'
+
+test_expect_success \
+ 'git-ls-files with path restriction with -- --.' \
+ 'git-ls-files --others -- -- >output &&
+ diff -u output - <<EOF
+--
+EOF
+'
+
+test_expect_success \
+ 'git-ls-files with no path restriction.' \
+ 'git-ls-files --others -- >output &&
+ diff -u output - <<EOF
+--
+-foo
+output
+path0
+EOF
+'
+
+test_done
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
new file mode 100755
index 0000000000..5fc1976711
--- /dev/null
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-ls-files -k and -m flags test.
+
+This test prepares the following in the cache:
+
+ path0 - a file
+ path1 - a symlink
+ path2/file2 - a file in a directory
+ path3/file3 - a file in a directory
+
+and the following on the filesystem:
+
+ path0/file0 - a file in a directory
+ path1/file1 - a file in a directory
+ path2 - a file
+ path3 - a symlink
+ path4 - a file
+ path5 - a symlink
+ path6/file6 - a file in a directory
+
+git-ls-files -k should report that existing filesystem
+objects except path4, path5 and path6/file6 to be killed.
+
+Also for modification test, the cache and working tree have:
+
+ path7 - an empty file, modified to a non-empty file.
+ path8 - a non-empty file, modified to an empty file.
+ path9 - an empty file, cache dirtied.
+ path10 - a non-empty file, cache dirtied.
+
+We should report path0, path1, path2/file2, path3/file3, path7 and path8
+modified without reporting path9 and path10.
+'
+. ./test-lib.sh
+
+date >path0
+ln -s xyzzy path1
+mkdir path2 path3
+date >path2/file2
+date >path3/file3
+: >path7
+date >path8
+: >path9
+date >path10
+test_expect_success \
+ 'git-update-index --add to add various paths.' \
+ "git-update-index --add -- path0 path1 path?/file? path7 path8 path9 path10"
+
+rm -fr path? ;# leave path10 alone
+date >path2
+ln -s frotz path3
+ln -s nitfol path5
+mkdir path0 path1 path6
+date >path0/file0
+date >path1/file1
+date >path6/file6
+date >path7
+: >path8
+: >path9
+touch path10
+
+test_expect_success \
+ 'git-ls-files -k to show killed files.' \
+ 'git-ls-files -k >.output'
+cat >.expected <<EOF
+path0/file0
+path1/file1
+path2
+path3
+EOF
+
+test_expect_success \
+ 'validate git-ls-files -k output.' \
+ 'diff .output .expected'
+
+test_expect_success \
+ 'git-ls-files -m to show modified files.' \
+ 'git-ls-files -m >.output'
+cat >.expected <<EOF
+path0
+path1
+path2/file2
+path3/file3
+path7
+path8
+EOF
+
+test_expect_success \
+ 'validate git-ls-files -m output.' \
+ 'diff .output .expected'
+
+test_done
diff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh
new file mode 100755
index 0000000000..d55559e553
--- /dev/null
+++ b/t/t3020-ls-files-error-unmatch.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Carl D. Worth
+#
+
+test_description='git-ls-files test for --error-unmatch option
+
+This test runs git-ls-files --error-unmatch to ensure it correctly
+returns an error when a non-existent path is provided on the command
+line.
+'
+. ./test-lib.sh
+
+touch foo bar
+git-update-index --add foo bar
+git-commit -m "add foo bar"
+
+test_expect_failure \
+ 'git-ls-files --error-unmatch should fail with unmatched path.' \
+ 'git-ls-files --error-unmatch foo bar-does-not-match'
+
+test_expect_success \
+ 'git-ls-files --error-unmatch should succeed eith matched paths.' \
+ 'git-ls-files --error-unmatch foo bar'
+
+test_done
+1
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
new file mode 100755
index 0000000000..2ec06d3d39
--- /dev/null
+++ b/t/t3100-ls-tree-restrict.sh
@@ -0,0 +1,158 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-ls-tree test.
+
+This test runs git-ls-tree with the following in a tree.
+
+ path0 - a file
+ path1 - a symlink
+ path2/foo - a file in a directory
+ path2/bazbo - a symlink in a directory
+ path2/baz/b - a file in a directory in a directory
+
+The new path restriction code should do the right thing for path2 and
+path2/baz. Also path0/ should snow nothing.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'mkdir path2 path2/baz &&
+ echo Hi >path0 &&
+ ln -s path0 path1 &&
+ echo Lo >path2/foo &&
+ ln -s ../path1 path2/bazbo &&
+ echo Mi >path2/baz/b &&
+ find path? \( -type f -o -type l \) -print |
+ xargs git-update-index --add &&
+ tree=`git-write-tree` &&
+ echo $tree'
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+test_output () {
+ sed -e "s/ $_x40 / X /" <current >check
+ diff -u expected check
+}
+
+test_expect_success \
+ 'ls-tree plain' \
+ 'git-ls-tree $tree >current &&
+ cat >expected <<\EOF &&
+100644 blob X path0
+120000 blob X path1
+040000 tree X path2
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree recursive' \
+ 'git-ls-tree -r $tree >current &&
+ cat >expected <<\EOF &&
+100644 blob X path0
+120000 blob X path1
+100644 blob X path2/baz/b
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree recursive with -t' \
+ 'git-ls-tree -r -t $tree >current &&
+ cat >expected <<\EOF &&
+100644 blob X path0
+120000 blob X path1
+040000 tree X path2
+040000 tree X path2/baz
+100644 blob X path2/baz/b
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree recursive with -d' \
+ 'git-ls-tree -r -d $tree >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2
+040000 tree X path2/baz
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path' \
+ 'git-ls-tree $tree path >current &&
+ cat >expected <<\EOF &&
+EOF
+ test_output'
+
+
+# it used to be path1 and then path0, but with pathspec semantics
+# they are shown in canonical order.
+test_expect_success \
+ 'ls-tree filtered with path1 path0' \
+ 'git-ls-tree $tree path1 path0 >current &&
+ cat >expected <<\EOF &&
+100644 blob X path0
+120000 blob X path1
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path0/' \
+ 'git-ls-tree $tree path0/ >current &&
+ cat >expected <<\EOF &&
+EOF
+ test_output'
+
+# It used to show path2 and its immediate children but
+# with pathspec semantics it shows only path2
+test_expect_success \
+ 'ls-tree filtered with path2' \
+ 'git-ls-tree $tree path2 >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2
+EOF
+ test_output'
+
+# ... and path2/ shows the children.
+test_expect_success \
+ 'ls-tree filtered with path2/' \
+ 'git-ls-tree $tree path2/ >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2/baz
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
+# The same change -- exact match does not show children of
+# path2/baz
+test_expect_success \
+ 'ls-tree filtered with path2/baz' \
+ 'git-ls-tree $tree path2/baz >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2/baz
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path2/bak' \
+ 'git-ls-tree $tree path2/bak >current &&
+ cat >expected <<\EOF &&
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree -t filtered with path2/bak' \
+ 'git-ls-tree -t $tree path2/bak >current &&
+ cat >expected <<\EOF &&
+040000 tree X path2
+EOF
+ test_output'
+
+test_done
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
new file mode 100755
index 0000000000..d78deb1e71
--- /dev/null
+++ b/t/t3101-ls-tree-dirname.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+# Copyright (c) 2005 Robert Fitzsimons
+#
+
+test_description='git-ls-tree directory and filenames handling.
+
+This test runs git-ls-tree with the following in a tree.
+
+ 1.txt - a file
+ 2.txt - a file
+ path0/a/b/c/1.txt - a file in a directory
+ path1/b/c/1.txt - a file in a directory
+ path2/1.txt - a file in a directory
+ path3/1.txt - a file in a directory
+ path3/2.txt - a file in a directory
+
+Test the handling of mulitple directories which have matching file
+entries. Also test odd filename and missing entries handling.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'echo 111 >1.txt &&
+ echo 222 >2.txt &&
+ mkdir path0 path0/a path0/a/b path0/a/b/c &&
+ echo 111 >path0/a/b/c/1.txt &&
+ mkdir path1 path1/b path1/b/c &&
+ echo 111 >path1/b/c/1.txt &&
+ mkdir path2 &&
+ echo 111 >path2/1.txt &&
+ mkdir path3 &&
+ echo 111 >path3/1.txt &&
+ echo 222 >path3/2.txt &&
+ find *.txt path* \( -type f -o -type l \) -print |
+ xargs git-update-index --add &&
+ tree=`git-write-tree` &&
+ echo $tree'
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+test_output () {
+ sed -e "s/ $_x40 / X /" <current >check
+ diff -u expected check
+}
+
+test_expect_success \
+ 'ls-tree plain' \
+ 'git-ls-tree $tree >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 2.txt
+040000 tree X path0
+040000 tree X path1
+040000 tree X path2
+040000 tree X path3
+EOF
+ test_output'
+
+# Recursive does not show tree nodes anymore...
+test_expect_success \
+ 'ls-tree recursive' \
+ 'git-ls-tree -r $tree >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 2.txt
+100644 blob X path0/a/b/c/1.txt
+100644 blob X path1/b/c/1.txt
+100644 blob X path2/1.txt
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter 1.txt' \
+ 'git-ls-tree $tree 1.txt >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter path1/b/c/1.txt' \
+ 'git-ls-tree $tree path1/b/c/1.txt >current &&
+ cat >expected <<\EOF &&
+100644 blob X path1/b/c/1.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter all 1.txt files' \
+ 'git-ls-tree $tree 1.txt path0/a/b/c/1.txt path1/b/c/1.txt path2/1.txt path3/1.txt >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X path0/a/b/c/1.txt
+100644 blob X path1/b/c/1.txt
+100644 blob X path2/1.txt
+100644 blob X path3/1.txt
+EOF
+ test_output'
+
+# I am not so sure about this one after ls-tree doing pathspec match.
+# Having both path0/a and path0/a/b/c makes path0/a redundant, and
+# it behaves as if path0/a/b/c, path1/b/c, path2 and path3 are specified.
+test_expect_success \
+ 'ls-tree filter directories' \
+ 'git-ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
+ cat >expected <<\EOF &&
+040000 tree X path0/a/b/c
+040000 tree X path1/b/c
+040000 tree X path2
+040000 tree X path3
+EOF
+ test_output'
+
+# Again, duplicates are filtered away so this is equivalent to
+# having 1.txt and path3
+test_expect_success \
+ 'ls-tree filter odd names' \
+ 'git-ls-tree $tree 1.txt /1.txt //1.txt path3/1.txt /path3/1.txt //path3//1.txt path3 /path3/ path3// >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter missing files and extra slashes' \
+ 'git-ls-tree $tree 1.txt/ abc.txt path3//23.txt path3/2.txt/// >current &&
+ cat >expected <<\EOF &&
+EOF
+ test_output'
+
+test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
new file mode 100755
index 0000000000..5b04efc89d
--- /dev/null
+++ b/t/t3200-branch.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Amos Waterland
+#
+
+test_description='git branch --foo should not create bogus branch
+
+This test runs git branch --help and checks that the argument is properly
+handled. Specifically, that a bogus branch is not created.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'prepare an trivial repository' \
+ 'echo Hello > A &&
+ git-update-index --add A &&
+ git-commit -m "Initial commit." &&
+ HEAD=$(git-rev-parse --verify HEAD)'
+
+test_expect_success \
+ 'git branch --help should return success now.' \
+ 'git-branch --help'
+
+test_expect_failure \
+ 'git branch --help should not have created a bogus branch' \
+ 'test -f .git/refs/heads/--help'
+
+test_expect_success \
+ 'git branch abc should create a branch' \
+ 'git-branch abc && test -f .git/refs/heads/abc'
+
+test_expect_success \
+ 'git branch a/b/c should create a branch' \
+ 'git-branch a/b/c && test -f .git/refs/heads/a/b/c'
+
+cat >expect <<EOF
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from HEAD
+EOF
+test_expect_success \
+ 'git branch -l d/e/f should create a branch and a log' \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ git-branch -l d/e/f &&
+ test -f .git/refs/heads/d/e/f &&
+ test -f .git/logs/refs/heads/d/e/f &&
+ diff expect .git/logs/refs/heads/d/e/f'
+
+test_expect_success \
+ 'git branch -d d/e/f should delete a branch and a log' \
+ 'git-branch -d d/e/f &&
+ test ! -f .git/refs/heads/d/e/f &&
+ test ! -f .git/logs/refs/heads/d/e/f'
+
+cat >expect <<EOF
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 checkout: Created from master^0
+EOF
+test_expect_success \
+ 'git checkout -b g/h/i -l should create a branch and a log' \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ git-checkout -b g/h/i -l master &&
+ test -f .git/refs/heads/g/h/i &&
+ test -f .git/logs/refs/heads/g/h/i &&
+ diff expect .git/logs/refs/heads/g/h/i'
+
+test_done
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
new file mode 100755
index 0000000000..c12270efab
--- /dev/null
+++ b/t/t3300-funny-names.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Pathnames with funny characters.
+
+This test tries pathnames with funny characters in the working
+tree, index, and tree objects.
+'
+
+. ./test-lib.sh
+
+p0='no-funny'
+p1='tabs ," (dq) and spaces'
+p2='just space'
+
+cat >"$p0" <<\EOF
+1. A quick brown fox jumps over the lazy cat, oops dog.
+2. A quick brown fox jumps over the lazy cat, oops dog.
+3. A quick brown fox jumps over the lazy cat, oops dog.
+EOF
+
+cat >"$p1" "$p0"
+echo 'Foo Bar Baz' >"$p2"
+
+test -f "$p1" && cmp "$p0" "$p1" || {
+ # since FAT/NTFS does not allow tabs in filenames, skip this test
+ say 'Your filesystem does not allow tabs in filenames, test skipped.'
+ test_done
+}
+
+echo 'just space
+no-funny' >expected
+test_expect_success 'git-ls-files no-funny' \
+ 'git-update-index --add "$p0" "$p2" &&
+ git-ls-files >current &&
+ diff -u expected current'
+
+t0=`git-write-tree`
+echo "$t0" >t0
+
+cat > expected <<\EOF
+just space
+no-funny
+"tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git-ls-files with-funny' \
+ 'git-update-index --add "$p1" &&
+ git-ls-files >current &&
+ diff -u expected current'
+
+echo 'just space
+no-funny
+tabs ," (dq) and spaces' >expected
+test_expect_success 'git-ls-files -z with-funny' \
+ 'git-ls-files -z | tr \\0 \\012 >current &&
+ diff -u expected current'
+
+t1=`git-write-tree`
+echo "$t1" >t1
+
+cat > expected <<\EOF
+just space
+no-funny
+"tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git-ls-tree with funny' \
+ 'git-ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current &&
+ diff -u expected current'
+
+cat > expected <<\EOF
+A "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git-diff-index with-funny' \
+ 'git-diff-index --name-status $t0 >current &&
+ diff -u expected current'
+
+test_expect_success 'git-diff-tree with-funny' \
+ 'git-diff-tree --name-status $t0 $t1 >current &&
+ diff -u expected current'
+
+echo 'A
+tabs ," (dq) and spaces' >expected
+test_expect_success 'git-diff-index -z with-funny' \
+ 'git-diff-index -z --name-status $t0 | tr \\0 \\012 >current &&
+ diff -u expected current'
+
+test_expect_success 'git-diff-tree -z with-funny' \
+ 'git-diff-tree -z --name-status $t0 $t1 | tr \\0 \\012 >current &&
+ diff -u expected current'
+
+cat > expected <<\EOF
+CNUM no-funny "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git-diff-tree -C with-funny' \
+ 'git-diff-tree -C --find-copies-harder --name-status \
+ $t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current &&
+ diff -u expected current'
+
+cat > expected <<\EOF
+RNUM no-funny "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git-diff-tree delete with-funny' \
+ 'git-update-index --force-remove "$p0" &&
+ git-diff-index -M --name-status \
+ $t0 | sed -e 's/^R[0-9]*/RNUM/' >current &&
+ diff -u expected current'
+
+cat > expected <<\EOF
+diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+similarity index NUM%
+rename from no-funny
+rename to "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git-diff-tree delete with-funny' \
+ 'git-diff-index -M -p $t0 |
+ sed -e "s/index [0-9]*%/index NUM%/" >current &&
+ diff -u expected current'
+
+chmod +x "$p1"
+cat > expected <<\EOF
+diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+old mode 100644
+new mode 100755
+similarity index NUM%
+rename from no-funny
+rename to "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git-diff-tree delete with-funny' \
+ 'git-diff-index -M -p $t0 |
+ sed -e "s/index [0-9]*%/index NUM%/" >current &&
+ diff -u expected current'
+
+cat >expected <<\EOF
+ "tabs\t,\" (dq) and spaces"
+ 1 files changed, 0 insertions(+), 0 deletions(-)
+EOF
+test_expect_success 'git-diff-tree rename with-funny applied' \
+ 'git-diff-index -M -p $t0 |
+ git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
+ diff -u expected current'
+
+cat > expected <<\EOF
+ no-funny
+ "tabs\t,\" (dq) and spaces"
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+EOF
+test_expect_success 'git-diff-tree delete with-funny applied' \
+ 'git-diff-index -p $t0 |
+ git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
+ diff -u expected current'
+
+test_expect_success 'git-apply non-git diff' \
+ 'git-diff-index -p $t0 |
+ sed -ne "/^[-+@]/p" |
+ git-apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
+ diff -u expected current'
+
+test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
new file mode 100755
index 0000000000..b9d3131cc2
--- /dev/null
+++ b/t/t3400-rebase.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Amos Waterland
+#
+
+test_description='git rebase should not destroy author information
+
+This test runs git rebase and checks that the author information is not lost.
+'
+. ./test-lib.sh
+
+export GIT_AUTHOR_EMAIL=bogus_email_address
+
+test_expect_success \
+ 'prepare repository with topic branch, then rebase against master' \
+ 'echo First > A &&
+ git-update-index --add A &&
+ git-commit -m "Add A." &&
+ git checkout -b my-topic-branch &&
+ echo Second > B &&
+ git-update-index --add B &&
+ git-commit -m "Add B." &&
+ git checkout -f master &&
+ echo Third >> A &&
+ git-update-index A &&
+ git-commit -m "Modify A." &&
+ git checkout -f my-topic-branch &&
+ git rebase master'
+
+test_expect_failure \
+ 'the rebase operation should not have destroyed author information' \
+ 'git log | grep "Author:" | grep "<>"'
+
+test_done
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
new file mode 100755
index 0000000000..32dc9c5e74
--- /dev/null
+++ b/t/t3401-rebase-partial.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Yann Dirson, based on t3400 by Amos Waterland
+#
+
+test_description='git rebase should detect patches integrated upstream
+
+This test cherry-picks one local change of two into master branch, and
+checks that git rebase succeeds with only the second patch in the
+local branch.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'prepare repository with topic branch' \
+ 'echo First > A &&
+ git-update-index --add A &&
+ git-commit -m "Add A." &&
+
+ git-checkout -b my-topic-branch &&
+
+ echo Second > B &&
+ git-update-index --add B &&
+ git-commit -m "Add B." &&
+
+ echo AnotherSecond > C &&
+ git-update-index --add C &&
+ git-commit -m "Add C." &&
+
+ git-checkout -f master &&
+
+ echo Third >> A &&
+ git-update-index A &&
+ git-commit -m "Modify A."
+'
+
+test_expect_success \
+ 'pick top patch from topic branch into master' \
+ 'git-cherry-pick my-topic-branch^0 &&
+ git-checkout -f my-topic-branch
+'
+
+test_debug \
+ 'git-cherry master &&
+ git-format-patch -k --stdout --full-index master >/dev/null &&
+ gitk --all & sleep 1
+'
+
+test_expect_success \
+ 'rebase topic branch against new master and check git-am did not get halted' \
+ 'git-rebase master && test ! -d .dotest'
+
+test_done
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
new file mode 100755
index 0000000000..e83bbee074
--- /dev/null
+++ b/t/t3500-cherry.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Yann Dirson, based on t3400 by Amos Waterland
+#
+
+test_description='git-cherry should detect patches integrated upstream
+
+This test cherry-picks one local change of two into master branch, and
+checks that git-cherry only returns the second patch in the local branch
+'
+. ./test-lib.sh
+
+export GIT_AUTHOR_EMAIL=bogus_email_address
+
+test_expect_success \
+ 'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
+ 'echo First > A &&
+ git-update-index --add A &&
+ git-commit -m "Add A." &&
+
+ git-checkout -b my-topic-branch &&
+
+ echo Second > B &&
+ git-update-index --add B &&
+ git-commit -m "Add B." &&
+
+ sleep 2 &&
+ echo AnotherSecond > C &&
+ git-update-index --add C &&
+ git-commit -m "Add C." &&
+
+ git-checkout -f master &&
+ rm -f B C &&
+
+ echo Third >> A &&
+ git-update-index A &&
+ git-commit -m "Modify A." &&
+
+ expr "$(echo $(git-cherry master my-topic-branch) )" : "+ [^ ]* + .*"
+'
+
+test_expect_success \
+ 'check that cherry with limit returns only the top patch'\
+ 'expr "$(echo $(git-cherry master my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
+'
+
+test_expect_success \
+ 'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' \
+ 'git-cherry-pick my-topic-branch^0 &&
+ echo $(git-cherry master my-topic-branch) &&
+ expr "$(echo $(git-cherry master my-topic-branch) )" : "+ [^ ]* - .*"
+'
+
+test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
new file mode 100755
index 0000000000..201d1642da
--- /dev/null
+++ b/t/t3600-rm.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Carl D. Worth
+#
+
+test_description='Test of the various options to git-rm.'
+
+. ./test-lib.sh
+
+# Setup some files to be removed, some with funny characters
+test_expect_success \
+ 'Initialize test directory' \
+ "touch -- foo bar baz 'space embedded' -q &&
+ git-add -- foo bar baz 'space embedded' -q &&
+ git-commit -m 'add normal files' &&
+ test_tabs=y &&
+ if touch -- 'tab embedded' 'newline
+embedded'
+ then
+ git-add -- 'tab embedded' 'newline
+embedded' &&
+ git-commit -m 'add files with tabs and newlines'
+ else
+ say 'Your filesystem does not allow tabs in filenames.'
+ test_tabs=n
+ fi"
+
+# Later we will try removing an unremovable path to make sure
+# git-rm barfs, but if the test is run as root that cannot be
+# arranged.
+test_expect_success \
+ 'Determine rm behavior' \
+ ': >test-file
+ chmod a-w .
+ rm -f test-file
+ test -f test-file && test_failed_remove=y
+ chmod 775 .
+ rm -f test-file'
+
+test_expect_success \
+ 'Pre-check that foo exists and is in index before git-rm foo' \
+ '[ -f foo ] && git-ls-files --error-unmatch foo'
+
+test_expect_success \
+ 'Test that git-rm foo succeeds' \
+ 'git-rm foo'
+
+test_expect_success \
+ 'Post-check that foo exists but is not in index after git-rm foo' \
+ '[ -f foo ] && ! git-ls-files --error-unmatch foo'
+
+test_expect_success \
+ 'Pre-check that bar exists and is in index before "git-rm -f bar"' \
+ '[ -f bar ] && git-ls-files --error-unmatch bar'
+
+test_expect_success \
+ 'Test that "git-rm -f bar" succeeds' \
+ 'git-rm -f bar'
+
+test_expect_success \
+ 'Post-check that bar does not exist and is not in index after "git-rm -f bar"' \
+ '! [ -f bar ] && ! git-ls-files --error-unmatch bar'
+
+test_expect_success \
+ 'Test that "git-rm -- -q" succeeds (remove a file that looks like an option)' \
+ 'git-rm -- -q'
+
+test "$test_tabs" = y && test_expect_success \
+ "Test that \"git-rm -f\" succeeds with embedded space, tab, or newline characters." \
+ "git-rm -f 'space embedded' 'tab embedded' 'newline
+embedded'"
+
+if test "$test_failed_remove" = y; then
+chmod a-w .
+test_expect_failure \
+ 'Test that "git-rm -f" fails if its rm fails' \
+ 'git-rm -f baz'
+chmod 775 .
+else
+ test_expect_success 'skipping removal failure (perhaps running as root?)' :
+fi
+
+test_expect_success \
+ 'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
+ 'git-ls-files --error-unmatch baz'
+
+test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
new file mode 100755
index 0000000000..6cd05c3d90
--- /dev/null
+++ b/t/t3700-add.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Carl D. Worth
+#
+
+test_description='Test of git-add, including the -- option.'
+
+. ./test-lib.sh
+
+test_expect_success \
+ 'Test of git-add' \
+ 'touch foo && git-add foo'
+
+test_expect_success \
+ 'Post-check that foo is in the index' \
+ 'git-ls-files foo | grep foo'
+
+test_expect_success \
+ 'Test that "git-add -- -q" works' \
+ 'touch -- -q && git-add -- -q'
+
+test_done
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
new file mode 100755
index 0000000000..67b9681d36
--- /dev/null
+++ b/t/t4000-diff-format.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test built-in diff output engine.
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh
+
+echo >path0 'Line 1
+Line 2
+line 3'
+cat path0 >path1
+chmod +x path1
+
+test_expect_success \
+ 'update-cache --add two files with and without +x.' \
+ 'git-update-index --add path0 path1'
+
+mv path0 path0-
+sed -e 's/line/Line/' <path0- >path0
+chmod +x path0
+rm -f path1
+test_expect_success \
+ 'git-diff-files -p after editing work tree.' \
+ 'git-diff-files -p >current'
+
+# that's as far as it comes
+if [ "$(git repo-config --get core.filemode)" = false ]
+then
+ say 'filemode disabled on the filesystem'
+ test_done
+fi
+
+cat >expected <<\EOF
+diff --git a/path0 b/path0
+old mode 100644
+new mode 100755
+--- a/path0
++++ b/path0
+@@ -1,3 +1,3 @@
+ Line 1
+ Line 2
+-line 3
++Line 3
+diff --git a/path1 b/path1
+deleted file mode 100755
+--- a/path1
++++ /dev/null
+@@ -1,3 +0,0 @@
+-Line 1
+-Line 2
+-line 3
+EOF
+
+test_expect_success \
+ 'validate git-diff-files -p output.' \
+ 'compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
new file mode 100755
index 0000000000..2e3c20d6b9
--- /dev/null
+++ b/t/t4001-diff-rename.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test rename detection in diff engine.
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh
+
+echo >path0 'Line 1
+Line 2
+Line 3
+Line 4
+Line 5
+Line 6
+Line 7
+Line 8
+Line 9
+Line 10
+line 11
+Line 12
+Line 13
+Line 14
+Line 15
+'
+
+test_expect_success \
+ 'update-cache --add a file.' \
+ 'git-update-index --add path0'
+
+test_expect_success \
+ 'write that tree.' \
+ 'tree=$(git-write-tree) && echo $tree'
+
+sed -e 's/line/Line/' <path0 >path1
+rm -f path0
+test_expect_success \
+ 'renamed and edited the file.' \
+ 'git-update-index --add --remove path0 path1'
+
+test_expect_success \
+ 'git-diff-index -p -M after rename and editing.' \
+ 'git-diff-index -p -M $tree >current'
+cat >expected <<\EOF
+diff --git a/path0 b/path1
+rename from path0
+rename to path1
+--- a/path0
++++ b/path1
+@@ -8,7 +8,7 @@ Line 7
+ Line 8
+ Line 9
+ Line 10
+-line 11
++Line 11
+ Line 12
+ Line 13
+ Line 14
+EOF
+
+test_expect_success \
+ 'validate the output.' \
+ 'compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
new file mode 100755
index 0000000000..56eda63fc2
--- /dev/null
+++ b/t/t4002-diff-basic.sh
@@ -0,0 +1,247 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test diff raw-output.
+
+'
+. ./test-lib.sh
+. ../lib-read-tree-m-3way.sh
+
+cat >.test-plain-OA <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A AA
+:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A AN
+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD
+:000000 040000 0000000000000000000000000000000000000000 6d50f65d3bdab91c63444294d38f08aeff328e42 A DF
+:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D DM
+:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D DN
+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL
+:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M MD
+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M MM
+:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M MN
+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS
+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M TT
+:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 5e5f22072bb39f6e12cf663a57cb634c76eefb49 M Z
+EOF
+
+cat >.test-recursive-OA <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A AA
+:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A AN
+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD
+:000000 100644 0000000000000000000000000000000000000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 A DF/DF
+:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D DM
+:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D DN
+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL
+:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M MD
+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M MM
+:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M MN
+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS
+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M TT
+:000000 100644 0000000000000000000000000000000000000000 8acb8e9750e3f644bf323fcf3d338849db106c77 A Z/AA
+:000000 100644 0000000000000000000000000000000000000000 087494262084cefee7ed484d20c8dc0580791272 A Z/AN
+:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D Z/DD
+:100644 000000 9b541b2275c06e3a7b13f28badf5294e2ae63df4 0000000000000000000000000000000000000000 D Z/DM
+:100644 000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a 0000000000000000000000000000000000000000 D Z/DN
+:100644 100644 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 M Z/MD
+:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 61422ba9c2c873416061a88cd40a59a35b576474 M Z/MM
+:100644 100644 b16d7b25b869f2beb124efa53467d8a1550ad694 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd M Z/MN
+EOF
+cat >.test-plain-OB <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A AA
+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD
+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF
+:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M DM
+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL
+:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D MD
+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM
+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA
+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND
+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM
+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS
+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT
+:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 1ba523955d5160681af65cb776411f574c1e8155 M Z
+EOF
+cat >.test-recursive-OB <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A AA
+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD
+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF
+:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M DM
+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL
+:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D MD
+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM
+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA
+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND
+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM
+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS
+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT
+:000000 100644 0000000000000000000000000000000000000000 6c0b99286d0bce551ac4a7b3dff8b706edff3715 A Z/AA
+:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D Z/DD
+:100644 100644 9b541b2275c06e3a7b13f28badf5294e2ae63df4 d77371d15817fcaa57eeec27f770c505ba974ec1 M Z/DM
+:100644 000000 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 0000000000000000000000000000000000000000 D Z/MD
+:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 697aad7715a1e7306ca76290a3dd4208fbaeddfa M Z/MM
+:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A Z/NA
+:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D Z/ND
+:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M Z/NM
+EOF
+cat >.test-plain-AB <<\EOF
+:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M AA
+:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D AN
+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF
+:040000 000000 6d50f65d3bdab91c63444294d38f08aeff328e42 0000000000000000000000000000000000000000 D DF
+:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A DM
+:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A DN
+:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D MD
+:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM
+:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M MN
+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA
+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND
+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM
+:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT
+:040000 040000 5e5f22072bb39f6e12cf663a57cb634c76eefb49 1ba523955d5160681af65cb776411f574c1e8155 M Z
+EOF
+cat >.test-recursive-AB <<\EOF
+:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M AA
+:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D AN
+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF
+:100644 000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 0000000000000000000000000000000000000000 D DF/DF
+:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A DM
+:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A DN
+:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D MD
+:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM
+:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M MN
+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA
+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND
+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM
+:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT
+:100644 100644 8acb8e9750e3f644bf323fcf3d338849db106c77 6c0b99286d0bce551ac4a7b3dff8b706edff3715 M Z/AA
+:100644 000000 087494262084cefee7ed484d20c8dc0580791272 0000000000000000000000000000000000000000 D Z/AN
+:000000 100644 0000000000000000000000000000000000000000 d77371d15817fcaa57eeec27f770c505ba974ec1 A Z/DM
+:000000 100644 0000000000000000000000000000000000000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a A Z/DN
+:100644 000000 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 0000000000000000000000000000000000000000 D Z/MD
+:100644 100644 61422ba9c2c873416061a88cd40a59a35b576474 697aad7715a1e7306ca76290a3dd4208fbaeddfa M Z/MM
+:100644 100644 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd b16d7b25b869f2beb124efa53467d8a1550ad694 M Z/MN
+:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A Z/NA
+:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D Z/ND
+:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M Z/NM
+EOF
+
+x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+x40="$x40$x40$x40$x40$x40$x40$x40$x40"
+z40='0000000000000000000000000000000000000000'
+cmp_diff_files_output () {
+ # diff-files never reports additions. Also it does not fill in the
+ # object ID for the changed files because it wants you to look at the
+ # filesystem.
+ sed <"$2" >.test-tmp \
+ -e '/^:000000 /d;s/'$x40'\( [MCRNDU][0-9]*\) /'$z40'\1 /' &&
+ diff "$1" .test-tmp
+}
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git-diff-tree $tree_O $tree_A >.test-a &&
+ cmp -s .test-a .test-plain-OA'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git-diff-tree -r $tree_O $tree_A >.test-a &&
+ cmp -s .test-a .test-recursive-OA'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git-diff-tree $tree_O $tree_B >.test-a &&
+ cmp -s .test-a .test-plain-OB'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git-diff-tree -r $tree_O $tree_B >.test-a &&
+ cmp -s .test-a .test-recursive-OB'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git-diff-tree $tree_A $tree_B >.test-a &&
+ cmp -s .test-a .test-plain-AB'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git-diff-tree -r $tree_A $tree_B >.test-a &&
+ cmp -s .test-a .test-recursive-AB'
+
+test_expect_success \
+ 'diff-cache O with A in cache' \
+ 'git-read-tree $tree_A &&
+ git-diff-index --cached $tree_O >.test-a &&
+ cmp -s .test-a .test-recursive-OA'
+
+test_expect_success \
+ 'diff-cache O with B in cache' \
+ 'git-read-tree $tree_B &&
+ git-diff-index --cached $tree_O >.test-a &&
+ cmp -s .test-a .test-recursive-OB'
+
+test_expect_success \
+ 'diff-cache A with B in cache' \
+ 'git-read-tree $tree_B &&
+ git-diff-index --cached $tree_A >.test-a &&
+ cmp -s .test-a .test-recursive-AB'
+
+test_expect_success \
+ 'diff-files with O in cache and A checked out' \
+ 'rm -fr Z [A-Z][A-Z] &&
+ git-read-tree $tree_A &&
+ git-checkout-index -f -a &&
+ git-read-tree --reset $tree_O || return 1
+ git-update-index --refresh >/dev/null ;# this can exit non-zero
+ git-diff-files >.test-a &&
+ cmp_diff_files_output .test-a .test-recursive-OA'
+
+test_expect_success \
+ 'diff-files with O in cache and B checked out' \
+ 'rm -fr Z [A-Z][A-Z] &&
+ git-read-tree $tree_B &&
+ git-checkout-index -f -a &&
+ git-read-tree --reset $tree_O || return 1
+ git-update-index --refresh >/dev/null ;# this can exit non-zero
+ git-diff-files >.test-a &&
+ cmp_diff_files_output .test-a .test-recursive-OB'
+
+test_expect_success \
+ 'diff-files with A in cache and B checked out' \
+ 'rm -fr Z [A-Z][A-Z] &&
+ git-read-tree $tree_B &&
+ git-checkout-index -f -a &&
+ git-read-tree --reset $tree_A || return 1
+ git-update-index --refresh >/dev/null ;# this can exit non-zero
+ git-diff-files >.test-a &&
+ cmp_diff_files_output .test-a .test-recursive-AB'
+
+################################################################
+# Now we have established the baseline, we do not have to
+# rely on individual object ID values that much.
+
+test_expect_success \
+ 'diff-tree O A == diff-tree -R A O' \
+ 'git-diff-tree $tree_O $tree_A >.test-a &&
+ git-diff-tree -R $tree_A $tree_O >.test-b &&
+ cmp -s .test-a .test-b'
+
+test_expect_success \
+ 'diff-tree -r O A == diff-tree -r -R A O' \
+ 'git-diff-tree -r $tree_O $tree_A >.test-a &&
+ git-diff-tree -r -R $tree_A $tree_O >.test-b &&
+ cmp -s .test-a .test-b'
+
+test_expect_success \
+ 'diff-tree B A == diff-tree -R A B' \
+ 'git-diff-tree $tree_B $tree_A >.test-a &&
+ git-diff-tree -R $tree_A $tree_B >.test-b &&
+ cmp -s .test-a .test-b'
+
+test_expect_success \
+ 'diff-tree -r B A == diff-tree -r -R A B' \
+ 'git-diff-tree -r $tree_B $tree_A >.test-a &&
+ git-diff-tree -r -R $tree_A $tree_B >.test-b &&
+ cmp -s .test-a .test-b'
+
+test_done
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
new file mode 100755
index 0000000000..27519704d4
--- /dev/null
+++ b/t/t4003-diff-rename-1.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='More rename detection
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'cat ../../COPYING >COPYING &&
+ echo frotz >rezrov &&
+ git-update-index --add COPYING rezrov &&
+ tree=$(git-write-tree) &&
+ echo $tree'
+
+test_expect_success \
+ 'prepare work tree' \
+ 'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+ sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+ rm -f COPYING &&
+ git-update-index --add --remove COPYING COPYING.?'
+
+# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
+# both are slightly edited, and unchanged rezrov. So we say you
+# copy-and-edit one, and rename-and-edit the other. We do not say
+# anything about rezrov.
+
+GIT_DIFF_OPTS=--unified=0 git-diff-index -M -p $tree >current
+cat >expected <<\EOF
+diff --git a/COPYING b/COPYING.1
+copy from COPYING
+copy to COPYING.1
+--- a/COPYING
++++ b/COPYING.1
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ However, in order to allow a migration to GPLv3 if that seems like
+diff --git a/COPYING b/COPYING.2
+rename from COPYING
+rename to COPYING.2
+--- a/COPYING
++++ b/COPYING.2
+@@ -2 +2 @@
+- Note that the only valid version of the GPL as far as this project
++ Note that the only valid version of the G.P.L as far as this project
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ HOWEVER, in order to allow a migration to G.P.Lv3 if that seems like
+@@ -12 +12 @@
+- This file is licensed under the GPL v2, or a later version
++ This file is licensed under the G.P.L v2, or a later version
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#1)' \
+ 'compare_diff_patch current expected'
+
+test_expect_success \
+ 'prepare work tree again' \
+ 'mv COPYING.2 COPYING &&
+ git-update-index --add --remove COPYING COPYING.1 COPYING.2'
+
+# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
+# both are slightly edited, and unchanged rezrov. So we say you
+# edited one, and copy-and-edit the other. We do not say
+# anything about rezrov.
+
+GIT_DIFF_OPTS=--unified=0 git-diff-index -C -p $tree >current
+cat >expected <<\EOF
+diff --git a/COPYING b/COPYING
+--- a/COPYING
++++ b/COPYING
+@@ -2 +2 @@
+- Note that the only valid version of the GPL as far as this project
++ Note that the only valid version of the G.P.L as far as this project
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ HOWEVER, in order to allow a migration to G.P.Lv3 if that seems like
+@@ -12 +12 @@
+- This file is licensed under the GPL v2, or a later version
++ This file is licensed under the G.P.L v2, or a later version
+diff --git a/COPYING b/COPYING.1
+copy from COPYING
+copy to COPYING.1
+--- a/COPYING
++++ b/COPYING.1
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ However, in order to allow a migration to GPLv3 if that seems like
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#2)' \
+ 'compare_diff_patch current expected'
+
+test_expect_success \
+ 'prepare work tree once again' \
+ 'cat ../../COPYING >COPYING &&
+ git-update-index --add --remove COPYING COPYING.1'
+
+# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
+# but COPYING is not edited. We say you copy-and-edit COPYING.1; this
+# is only possible because -C mode now reports the unmodified file to
+# the diff-core. Unchanged rezrov, although being fed to
+# git-diff-index as well, should not be mentioned.
+
+GIT_DIFF_OPTS=--unified=0 \
+ git-diff-index -C --find-copies-harder -p $tree >current
+cat >expected <<\EOF
+diff --git a/COPYING b/COPYING.1
+copy from COPYING
+copy to COPYING.1
+--- a/COPYING
++++ b/COPYING.1
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ However, in order to allow a migration to GPLv3 if that seems like
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#3)' \
+ 'compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
new file mode 100755
index 0000000000..a23aaa0a94
--- /dev/null
+++ b/t/t4004-diff-rename-symlink.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='More rename detection tests.
+
+The rename detection logic should be able to detect pure rename or
+copy of symbolic links, but should not produce rename/copy followed
+by an edit for them.
+'
+. ./test-lib.sh
+. ../diff-lib.sh
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'echo xyzzy | tr -d '\\\\'012 >yomin &&
+ ln -s xyzzy frotz &&
+ git-update-index --add frotz yomin &&
+ tree=$(git-write-tree) &&
+ echo $tree'
+
+test_expect_success \
+ 'prepare work tree' \
+ 'mv frotz rezrov &&
+ rm -f yomin &&
+ ln -s xyzzy nitfol &&
+ ln -s xzzzy bozbar &&
+ git-update-index --add --remove frotz rezrov nitfol bozbar yomin'
+
+# tree has frotz pointing at xyzzy, and yomin that contains xyzzy to
+# confuse things. work tree has rezrov (xyzzy) nitfol (xyzzy) and
+# bozbar (xzzzy).
+# rezrov and nitfol are rename/copy of frotz and bozbar should be
+# a new creation.
+
+GIT_DIFF_OPTS=--unified=0 git-diff-index -M -p $tree >current
+cat >expected <<\EOF
+diff --git a/bozbar b/bozbar
+new file mode 120000
+--- /dev/null
++++ b/bozbar
+@@ -0,0 +1 @@
++xzzzy
+\ No newline at end of file
+diff --git a/frotz b/nitfol
+similarity index 100%
+copy from frotz
+copy to nitfol
+diff --git a/frotz b/rezrov
+similarity index 100%
+rename from frotz
+rename to rezrov
+diff --git a/yomin b/yomin
+deleted file mode 100644
+--- a/yomin
++++ /dev/null
+@@ -1 +0,0 @@
+-xyzzy
+\ No newline at end of file
+EOF
+
+test_expect_success \
+ 'validate diff output' \
+ 'compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
new file mode 100755
index 0000000000..684fd23a41
--- /dev/null
+++ b/t/t4005-diff-rename-2.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Same rename detection as t4003 but testing diff-raw.
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'cat ../../COPYING >COPYING &&
+ echo frotz >rezrov &&
+ git-update-index --add COPYING rezrov &&
+ tree=$(git-write-tree) &&
+ echo $tree'
+
+test_expect_success \
+ 'prepare work tree' \
+ 'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+ sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+ rm -f COPYING &&
+ git-update-index --add --remove COPYING COPYING.?'
+
+# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
+# both are slightly edited, and unchanged rezrov. We say COPYING.1
+# and COPYING.2 are based on COPYING, and do not say anything about
+# rezrov.
+
+git-diff-index -M $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234 COPYING COPYING.2
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#1)' \
+ 'compare_diff_raw current expected'
+
+################################################################
+
+test_expect_success \
+ 'prepare work tree again' \
+ 'mv COPYING.2 COPYING &&
+ git-update-index --add --remove COPYING COPYING.1 COPYING.2'
+
+# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
+# both are slightly edited, and unchanged rezrov. We say COPYING.1
+# is based on COPYING and COPYING is still there, and do not say anything
+# about rezrov.
+
+git-diff-index -C $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M COPYING
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#2)' \
+ 'compare_diff_raw current expected'
+
+################################################################
+
+# tree has COPYING and rezrov. work tree has the same COPYING and
+# copy-edited COPYING.1, and unchanged rezrov. We should not say
+# anything about rezrov nor COPYING, since the revised again diff-raw
+# nows how to say Copy.
+
+test_expect_success \
+ 'prepare work tree once again' \
+ 'cat ../../COPYING >COPYING &&
+ git-update-index --add --remove COPYING COPYING.1'
+
+git-diff-index -C --find-copies-harder $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#3)' \
+ 'compare_diff_raw current expected'
+
+test_done
diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh
new file mode 100755
index 0000000000..8ad69d1115
--- /dev/null
+++ b/t/t4006-diff-mode.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test mode change diffs.
+
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'echo frotz >rezrov &&
+ git-update-index --add rezrov &&
+ tree=`git-write-tree` &&
+ echo $tree'
+
+if [ "$(git repo-config --get core.filemode)" = false ]
+then
+ say 'filemode disabled on the filesystem, using update-index --chmod=+x'
+ test_expect_success \
+ 'git-update-index --chmod=+x' \
+ 'git-update-index rezrov &&
+ git-update-index --chmod=+x rezrov &&
+ git-diff-index $tree >current'
+else
+ test_expect_success \
+ 'chmod' \
+ 'chmod +x rezrov &&
+ git-update-index rezrov &&
+ git-diff-index $tree >current'
+fi
+
+_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
+sed -e 's/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /' <current >check
+echo ":100644 100755 X X M rezrov" >expected
+
+test_expect_success \
+ 'verify' \
+ 'diff -u expected check'
+
+test_done
+
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
new file mode 100755
index 0000000000..bb6ba69258
--- /dev/null
+++ b/t/t4007-rename-3.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Rename interaction with pathspec.
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'mkdir path0 path1 &&
+ cp ../../COPYING path0/COPYING &&
+ git-update-index --add path0/COPYING &&
+ tree=$(git-write-tree) &&
+ echo $tree'
+
+test_expect_success \
+ 'prepare work tree' \
+ 'cp path0/COPYING path1/COPYING &&
+ git-update-index --add --remove path0/COPYING path1/COPYING'
+
+# In the tree, there is only path0/COPYING. In the cache, path0 and
+# path1 both have COPYING and the latter is a copy of path0/COPYING.
+# Comparing the full tree with cache should tell us so.
+
+git-diff-index -C --find-copies-harder $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100 path0/COPYING path1/COPYING
+EOF
+
+test_expect_success \
+ 'validate the result (#1)' \
+ 'compare_diff_raw current expected'
+
+# In the tree, there is only path0/COPYING. In the cache, path0 and
+# path1 both have COPYING and the latter is a copy of path0/COPYING.
+# However when we say we care only about path1, we should just see
+# path1/COPYING suddenly appearing from nowhere, not detected as
+# a copy from path0/COPYING.
+
+git-diff-index -C $tree path1 >current
+
+cat >expected <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
+EOF
+
+test_expect_success \
+ 'validate the result (#2)' \
+ 'compare_diff_raw current expected'
+
+test_expect_success \
+ 'tweak work tree' \
+ 'rm -f path0/COPYING &&
+ git-update-index --remove path0/COPYING'
+
+# In the tree, there is only path0/COPYING. In the cache, path0 does
+# not have COPYING anymore and path1 has COPYING which is a copy of
+# path0/COPYING. Showing the full tree with cache should tell us about
+# the rename.
+
+git-diff-index -C $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 path0/COPYING path1/COPYING
+EOF
+
+test_expect_success \
+ 'validate the result (#3)' \
+ 'compare_diff_raw current expected'
+
+# In the tree, there is only path0/COPYING. In the cache, path0 does
+# not have COPYING anymore and path1 has COPYING which is a copy of
+# path0/COPYING. When we say we care only about path1, we should just
+# see path1/COPYING appearing from nowhere.
+
+git-diff-index -C $tree path1 >current
+
+cat >expected <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
+EOF
+
+test_expect_success \
+ 'validate the result (#4)' \
+ 'compare_diff_raw current expected'
+
+test_done
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
new file mode 100755
index 0000000000..263ac1ebf7
--- /dev/null
+++ b/t/t4008-diff-break-rewrite.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Break and then rename
+
+We have two very different files, file0 and file1, registered in a tree.
+
+We update file1 so drastically that it is more similar to file0, and
+then remove file0. With -B, changes to file1 should be broken into
+separate delete and create, resulting in removal of file0, removal of
+original file1 and creation of completely rewritten file1.
+
+Further, with -B and -M together, these three modifications should
+turn into rename-edit of file0 into file1.
+
+Starting from the same two files in the tree, we swap file0 and file1.
+With -B, this should be detected as two complete rewrites, resulting in
+four changes in total.
+
+Further, with -B and -M together, these should turn into two renames.
+'
+. ./test-lib.sh
+. ../diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ setup \
+ 'cat ../../README >file0 &&
+ cat ../../COPYING >file1 &&
+ git-update-index --add file0 file1 &&
+ tree=$(git-write-tree) &&
+ echo "$tree"'
+
+test_expect_success \
+ 'change file1 with copy-edit of file0 and remove file0' \
+ 'sed -e "s/git/GIT/" file0 >file1 &&
+ rm -f file0 &&
+ git-update-index --remove file0 file1'
+
+test_expect_success \
+ 'run diff with -B' \
+ 'git-diff-index -B --cached "$tree" >current'
+
+cat >expected <<\EOF
+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 11e331465a89c394dc25c780de230043750c1ec8 M100 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B (#1)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -B and -M' \
+ 'git-diff-index -B -M "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c R100 file0 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B -M (#2)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'swap file0 and file1' \
+ 'rm -f file0 file1 &&
+ git-read-tree -m $tree &&
+ git-checkout-index -f -u -a &&
+ mv file0 tmp &&
+ mv file1 file0 &&
+ mv tmp file1 &&
+ git-update-index file0 file1'
+
+test_expect_success \
+ 'run diff with -B' \
+ 'git-diff-index -B "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 6ff87c4664981e4397625791c8ea3bbb5f2279a3 M100 file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B (#3)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -B and -M' \
+ 'git-diff-index -B -M "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 file1 file0
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R100 file0 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B -M (#4)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'make file0 into something completely different' \
+ 'rm -f file0 &&
+ ln -s frotz file0 &&
+ git-update-index file0 file1'
+
+test_expect_success \
+ 'run diff with -B' \
+ 'git-diff-index -B "$tree" >current'
+
+cat >expected <<\EOF
+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B (#5)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -B' \
+ 'git-diff-index -B -M "$tree" >current'
+
+# This should not mistake file0 as the copy source of new file1
+# due to type differences.
+cat >expected <<\EOF
+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B -M (#6)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -M' \
+ 'git-diff-index -M "$tree" >current'
+
+# This should not mistake file0 as the copy source of new file1
+# due to type differences.
+cat >expected <<\EOF
+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M file1
+EOF
+
+test_expect_success \
+ 'validate result of -M (#7)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'file1 edited to look like file0 and file0 rename-edited to file2' \
+ 'rm -f file0 file1 &&
+ git-read-tree -m $tree &&
+ git-checkout-index -f -u -a &&
+ sed -e "s/git/GIT/" file0 >file1 &&
+ sed -e "s/git/GET/" file0 >file2 &&
+ rm -f file0
+ git-update-index --add --remove file0 file1 file2'
+
+test_expect_success \
+ 'run diff with -B' \
+ 'git-diff-index -B "$tree" >current'
+
+cat >expected <<\EOF
+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 08bb2fb671deff4c03a4d4a0a1315dff98d5732c M100 file1
+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 A file2
+EOF
+
+test_expect_success \
+ 'validate result of -B (#8)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -B -M' \
+ 'git-diff-index -B -M "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c C095 file0 file1
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 59f832e5c8b3f7e486be15ad0cd3e95ba9af8998 R095 file0 file2
+EOF
+
+test_expect_success \
+ 'validate result of -B -M (#9)' \
+ 'compare_diff_raw expected current'
+
+test_done
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
new file mode 100755
index 0000000000..2f2f8b1216
--- /dev/null
+++ b/t/t4009-diff-rename-4.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Same rename detection as t4003 but testing diff-raw -z.
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'cat ../../COPYING >COPYING &&
+ echo frotz >rezrov &&
+ git-update-index --add COPYING rezrov &&
+ tree=$(git-write-tree) &&
+ echo $tree'
+
+test_expect_success \
+ 'prepare work tree' \
+ 'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+ sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+ rm -f COPYING &&
+ git-update-index --add --remove COPYING COPYING.?'
+
+# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
+# both are slightly edited, and unchanged rezrov. We say COPYING.1
+# and COPYING.2 are based on COPYING, and do not say anything about
+# rezrov.
+
+git-diff-index -z -M $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+COPYING
+COPYING.1
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234
+COPYING
+COPYING.2
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#1)' \
+ 'compare_diff_raw_z current expected'
+
+################################################################
+
+test_expect_success \
+ 'prepare work tree again' \
+ 'mv COPYING.2 COPYING &&
+ git-update-index --add --remove COPYING COPYING.1 COPYING.2'
+
+# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
+# both are slightly edited, and unchanged rezrov. We say COPYING.1
+# is based on COPYING and COPYING is still there, and do not say anything
+# about rezrov.
+
+git-diff-index -z -C $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M
+COPYING
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+COPYING
+COPYING.1
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#2)' \
+ 'compare_diff_raw_z current expected'
+
+################################################################
+
+# tree has COPYING and rezrov. work tree has the same COPYING and
+# copy-edited COPYING.1, and unchanged rezrov. We should not say
+# anything about rezrov nor COPYING, since the revised again diff-raw
+# nows how to say Copy.
+
+test_expect_success \
+ 'prepare work tree once again' \
+ 'cat ../../COPYING >COPYING &&
+ git-update-index --add --remove COPYING COPYING.1'
+
+git-diff-index -z -C --find-copies-harder $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+COPYING
+COPYING.1
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#3)' \
+ 'compare_diff_raw_z current expected'
+
+test_done
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
new file mode 100755
index 0000000000..9e1544df9d
--- /dev/null
+++ b/t/t4010-diff-pathspec.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Pathspec restrictions
+
+Prepare:
+ file0
+ path1/file1
+'
+. ./test-lib.sh
+. ../diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ setup \
+ 'echo frotz >file0 &&
+ mkdir path1 &&
+ echo rezrov >path1/file1 &&
+ git-update-index --add file0 path1/file1 &&
+ tree=`git-write-tree` &&
+ echo "$tree" &&
+ echo nitfol >file0 &&
+ echo yomin >path1/file1 &&
+ git-update-index file0 path1/file1'
+
+cat >expected <<\EOF
+EOF
+test_expect_success \
+ 'limit to path should show nothing' \
+ 'git-diff-index --cached $tree -- path >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1
+EOF
+test_expect_success \
+ 'limit to path1 should show path1/file1' \
+ 'git-diff-index --cached $tree -- path1 >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1
+EOF
+test_expect_success \
+ 'limit to path1/ should show path1/file1' \
+ 'git-diff-index --cached $tree -- path1/ >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M file0
+EOF
+test_expect_success \
+ 'limit to file0 should show file0' \
+ 'git-diff-index --cached $tree -- file0 >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+EOF
+test_expect_success \
+ 'limit to file0/ should emit nothing.' \
+ 'git-diff-index --cached $tree -- file0/ >current &&
+ compare_diff_raw current expected'
+
+test_done
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
new file mode 100755
index 0000000000..379a831f0b
--- /dev/null
+++ b/t/t4011-diff-symlink.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test diff of symlinks.
+
+'
+. ./test-lib.sh
+. ../diff-lib.sh
+
+cat > expected << EOF
+diff --git a/frotz b/frotz
+new file mode 120000
+index 0000000..7c465af
+--- /dev/null
++++ b/frotz
+@@ -0,0 +1 @@
++xyzzy
+\ No newline at end of file
+EOF
+
+test_expect_success \
+ 'diff new symlink' \
+ 'ln -s xyzzy frotz &&
+ git-update-index &&
+ tree=$(git-write-tree) &&
+ git-update-index --add frotz &&
+ GIT_DIFF_OPTS=--unified=0 git-diff-index -M -p $tree > current &&
+ compare_diff_patch current expected'
+
+test_expect_success \
+ 'diff unchanged symlink' \
+ 'tree=$(git-write-tree) &&
+ git-update-index frotz &&
+ test -z "$(git-diff-index --name-only $tree)"'
+
+cat > expected << EOF
+diff --git a/frotz b/frotz
+deleted file mode 120000
+index 7c465af..0000000
+--- a/frotz
++++ /dev/null
+@@ -1 +0,0 @@
+-xyzzy
+\ No newline at end of file
+EOF
+
+test_expect_success \
+ 'diff removed symlink' \
+ 'rm frotz &&
+ git-diff-index -M -p $tree > current &&
+ compare_diff_patch current expected'
+
+cat > expected << EOF
+diff --git a/frotz b/frotz
+EOF
+
+test_expect_success \
+ 'diff identical, but newly created symlink' \
+ 'sleep 3 &&
+ ln -s xyzzy frotz &&
+ git-diff-index -M -p $tree > current &&
+ compare_diff_patch current expected'
+
+cat > expected << EOF
+diff --git a/frotz b/frotz
+index 7c465af..df1db54 120000
+--- a/frotz
++++ b/frotz
+@@ -1 +1 @@
+-xyzzy
+\ No newline at end of file
++yxyyz
+\ No newline at end of file
+EOF
+
+test_expect_success \
+ 'diff different symlink' \
+ 'rm frotz &&
+ ln -s yxyyz frotz &&
+ git-diff-index -M -p $tree > current &&
+ compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
new file mode 100755
index 0000000000..323606c65c
--- /dev/null
+++ b/t/t4012-diff-binary.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='Binary diff and apply
+'
+
+. ./test-lib.sh
+
+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 ../test4012.png >b &&
+ echo git >c &&
+ cat b b >d'
+
+cat > expected <<\EOF
+ a | 2 +-
+ b | Bin
+ c | 2 +-
+ d | Bin
+ 4 files changed, 2 insertions(+), 2 deletions(-)
+EOF
+test_expect_success 'diff without --binary' \
+ 'git-diff | git-apply --stat --summary >current &&
+ cmp current expected'
+
+test_expect_success 'diff with --binary' \
+ 'git-diff --binary | git-apply --stat --summary >current &&
+ cmp current expected'
+
+# 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 '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_done
diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
new file mode 100755
index 0000000000..6579f06b05
--- /dev/null
+++ b/t/t4100-apply-stat.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply --stat --summary test.
+
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'rename' \
+ 'git-apply --stat --summary <../t4100/t-apply-1.patch >current &&
+ diff -u ../t4100/t-apply-1.expect current'
+
+test_expect_success \
+ 'copy' \
+ 'git-apply --stat --summary <../t4100/t-apply-2.patch >current &&
+ diff -u ../t4100/t-apply-2.expect current'
+
+test_expect_success \
+ 'rewrite' \
+ 'git-apply --stat --summary <../t4100/t-apply-3.patch >current &&
+ diff -u ../t4100/t-apply-3.expect current'
+
+test_expect_success \
+ 'mode' \
+ 'git-apply --stat --summary <../t4100/t-apply-4.patch >current &&
+ diff -u ../t4100/t-apply-4.expect current'
+
+test_expect_success \
+ 'non git' \
+ 'git-apply --stat --summary <../t4100/t-apply-5.patch >current &&
+ diff -u ../t4100/t-apply-5.expect current'
+
+test_expect_success \
+ 'non git' \
+ 'git-apply --stat --summary <../t4100/t-apply-6.patch >current &&
+ diff -u ../t4100/t-apply-6.expect current'
+
+test_expect_success \
+ 'non git' \
+ 'git-apply --stat --summary <../t4100/t-apply-7.patch >current &&
+ diff -u ../t4100/t-apply-7.expect current'
+
+test_done
+
diff --git a/t/t4100/t-apply-1.expect b/t/t4100/t-apply-1.expect
new file mode 100644
index 0000000000..540e64db85
--- /dev/null
+++ b/t/t4100/t-apply-1.expect
@@ -0,0 +1,11 @@
+ Documentation/git-ssh-pull.txt | 12 ++++++------
+ Documentation/git-ssh-push.txt | 10 +++++-----
+ Documentation/git.txt | 6 +++---
+ Makefile | 6 +++---
+ ssh-pull.c | 4 ++--
+ ssh-push.c | 14 +++++++-------
+ 6 files changed, 26 insertions(+), 26 deletions(-)
+ rename Documentation/{git-rpull.txt => git-ssh-pull.txt} (90%)
+ rename Documentation/{git-rpush.txt => git-ssh-push.txt} (71%)
+ rename rpull.c => ssh-pull.c (97%)
+ rename rpush.c => ssh-push.c (93%)
diff --git a/t/t4100/t-apply-1.patch b/t/t4100/t-apply-1.patch
new file mode 100644
index 0000000000..de587517f4
--- /dev/null
+++ b/t/t4100/t-apply-1.patch
@@ -0,0 +1,194 @@
+418aaf847a8b3ffffb4f777a2dd5262ca5ce0ef7 (from dc93841715dfa9a9cdda6f2c4a25eec831ea7aa0)
+diff --git a/Documentation/git-rpull.txt b/Documentation/git-ssh-pull.txt
+similarity index 90%
+rename from Documentation/git-rpull.txt
+rename to Documentation/git-ssh-pull.txt
+--- a/Documentation/git-rpull.txt
++++ b/Documentation/git-ssh-pull.txt
+@@ -1,21 +1,21 @@
+-git-rpull(1)
+-============
++git-ssh-pull(1)
++===============
+ v0.1, May 2005
+
+ NAME
+ ----
+-git-rpull - Pulls from a remote repository over ssh connection
++git-ssh-pull - Pulls from a remote repository over ssh connection
+
+
+
+ SYNOPSIS
+ --------
+-'git-rpull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url
++'git-ssh-pull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url
+
+ DESCRIPTION
+ -----------
+-Pulls from a remote repository over ssh connection, invoking git-rpush on
+-the other end.
++Pulls from a remote repository over ssh connection, invoking git-ssh-push
++on the other end.
+
+ OPTIONS
+ -------
+diff --git a/Documentation/git-rpush.txt b/Documentation/git-ssh-push.txt
+similarity index 71%
+rename from Documentation/git-rpush.txt
+rename to Documentation/git-ssh-push.txt
+--- a/Documentation/git-rpush.txt
++++ b/Documentation/git-ssh-push.txt
+@@ -1,19 +1,19 @@
+-git-rpush(1)
+-============
++git-ssh-push(1)
++===============
+ v0.1, May 2005
+
+ NAME
+ ----
+-git-rpush - Helper "server-side" program used by git-rpull
++git-ssh-push - Helper "server-side" program used by git-ssh-pull
+
+
+ SYNOPSIS
+ --------
+-'git-rpush'
++'git-ssh-push'
+
+ DESCRIPTION
+ -----------
+-Helper "server-side" program used by git-rpull.
++Helper "server-side" program used by git-ssh-pull.
+
+
+ Author
+diff --git a/Documentation/git.txt b/Documentation/git.txt
+--- a/Documentation/git.txt
++++ b/Documentation/git.txt
+@@ -148,7 +148,7 @@ link:git-resolve-script.html[git-resolve
+ link:git-tag-script.html[git-tag-script]::
+ An example script to create a tag object signed with GPG
+
+-link:git-rpull.html[git-rpull]::
++link:git-ssh-pull.html[git-ssh-pull]::
+ Pulls from a remote repository over ssh connection
+
+ Interogators:
+@@ -156,8 +156,8 @@ Interogators:
+ link:git-diff-helper.html[git-diff-helper]::
+ Generates patch format output for git-diff-*
+
+-link:git-rpush.html[git-rpush]::
+- Helper "server-side" program used by git-rpull
++link:git-ssh-push.html[git-ssh-push]::
++ Helper "server-side" program used by git-ssh-pull
+
+
+
+diff --git a/Makefile b/Makefile
+--- a/Makefile
++++ b/Makefile
+@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files
+ git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
+ git-check-files git-ls-tree git-merge-base git-merge-cache \
+ git-unpack-file git-export git-diff-cache git-convert-cache \
+- git-http-pull git-rpush git-rpull git-rev-list git-mktag \
++ git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \
+ git-diff-helper git-tar-tree git-local-pull git-write-blob \
+ git-get-tar-commit-id git-mkdelta git-apply git-stripspace
+
+@@ -105,8 +105,8 @@ git-diff-cache: diff-cache.c
+ git-convert-cache: convert-cache.c
+ git-http-pull: http-pull.c pull.c
+ git-local-pull: local-pull.c pull.c
+-git-rpush: rsh.c
+-git-rpull: rsh.c pull.c
++git-ssh-push: rsh.c
++git-ssh-pull: rsh.c pull.c
+ git-rev-list: rev-list.c
+ git-mktag: mktag.c
+ git-diff-helper: diff-helper.c
+diff --git a/rpull.c b/ssh-pull.c
+similarity index 97%
+rename from rpull.c
+rename to ssh-pull.c
+--- a/rpull.c
++++ b/ssh-pull.c
+@@ -64,13 +64,13 @@ int main(int argc, char **argv)
+ arg++;
+ }
+ if (argc < arg + 2) {
+- usage("git-rpull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url");
++ usage("git-ssh-pull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url");
+ return 1;
+ }
+ commit_id = argv[arg];
+ url = argv[arg + 1];
+
+- if (setup_connection(&fd_in, &fd_out, "git-rpush", url, arg, argv + 1))
++ if (setup_connection(&fd_in, &fd_out, "git-ssh-push", url, arg, argv + 1))
+ return 1;
+
+ if (get_version())
+diff --git a/rpush.c b/ssh-push.c
+similarity index 93%
+rename from rpush.c
+rename to ssh-push.c
+--- a/rpush.c
++++ b/ssh-push.c
+@@ -16,7 +16,7 @@ int serve_object(int fd_in, int fd_out)
+ do {
+ size = read(fd_in, sha1 + posn, 20 - posn);
+ if (size < 0) {
+- perror("git-rpush: read ");
++ perror("git-ssh-push: read ");
+ return -1;
+ }
+ if (!size)
+@@ -30,7 +30,7 @@ int serve_object(int fd_in, int fd_out)
+ buf = map_sha1_file(sha1, &objsize);
+
+ if (!buf) {
+- fprintf(stderr, "git-rpush: could not find %s\n",
++ fprintf(stderr, "git-ssh-push: could not find %s\n",
+ sha1_to_hex(sha1));
+ remote = -1;
+ }
+@@ -45,9 +45,9 @@ int serve_object(int fd_in, int fd_out)
+ size = write(fd_out, buf + posn, objsize - posn);
+ if (size <= 0) {
+ if (!size) {
+- fprintf(stderr, "git-rpush: write closed");
++ fprintf(stderr, "git-ssh-push: write closed");
+ } else {
+- perror("git-rpush: write ");
++ perror("git-ssh-push: write ");
+ }
+ return -1;
+ }
+@@ -71,7 +71,7 @@ void service(int fd_in, int fd_out) {
+ retval = read(fd_in, &type, 1);
+ if (retval < 1) {
+ if (retval < 0)
+- perror("rpush: read ");
++ perror("git-ssh-push: read ");
+ return;
+ }
+ if (type == 'v' && serve_version(fd_in, fd_out))
+@@ -91,12 +91,12 @@ int main(int argc, char **argv)
+ arg++;
+ }
+ if (argc < arg + 2) {
+- usage("git-rpush [-c] [-t] [-a] commit-id url");
++ usage("git-ssh-push [-c] [-t] [-a] commit-id url");
+ return 1;
+ }
+ commit_id = argv[arg];
+ url = argv[arg + 1];
+- if (setup_connection(&fd_in, &fd_out, "git-rpull", url, arg, argv + 1))
++ if (setup_connection(&fd_in, &fd_out, "git-ssh-pull", url, arg, argv + 1))
+ return 1;
+
+ service(fd_in, fd_out);
diff --git a/t/t4100/t-apply-2.expect b/t/t4100/t-apply-2.expect
new file mode 100644
index 0000000000..d1e6459749
--- /dev/null
+++ b/t/t4100/t-apply-2.expect
@@ -0,0 +1,5 @@
+ Makefile | 2 +-
+ git-fetch-script | 5 -----
+ git-pull-script | 34 +---------------------------------
+ 3 files changed, 2 insertions(+), 39 deletions(-)
+ copy git-pull-script => git-fetch-script (87%)
diff --git a/t/t4100/t-apply-2.patch b/t/t4100/t-apply-2.patch
new file mode 100644
index 0000000000..cfdc80885b
--- /dev/null
+++ b/t/t4100/t-apply-2.patch
@@ -0,0 +1,72 @@
+7ef76925d9c19ef74874e1735e2436e56d0c4897 (from 6b14d7faf0bad026a81a27bac07b47691f621b8f)
+diff --git a/Makefile b/Makefile
+--- a/Makefile
++++ b/Makefile
+@@ -20,7 +20,7 @@ INSTALL=install
+
+ SCRIPTS=git-apply-patch-script git-merge-one-file-script git-prune-script \
+ git-pull-script git-tag-script git-resolve-script git-whatchanged \
+- git-deltafy-script
++ git-deltafy-script git-fetch-script
+
+ PROG= git-update-cache git-diff-files git-init-db git-write-tree \
+ git-read-tree git-commit-tree git-cat-file git-fsck-cache \
+diff --git a/git-pull-script b/git-fetch-script
+similarity index 87%
+copy from git-pull-script
+copy to git-fetch-script
+--- a/git-pull-script
++++ b/git-fetch-script
+@@ -39,8 +39,3 @@ download_one "$merge_repo/$merge_name" "
+
+ echo "Getting object database"
+ download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)"
+-
+-git-resolve-script \
+- "$(cat "$GIT_DIR"/HEAD)" \
+- "$(cat "$GIT_DIR"/MERGE_HEAD)" \
+- "$merge_repo"
+diff --git a/git-pull-script b/git-pull-script
+--- a/git-pull-script
++++ b/git-pull-script
+@@ -6,39 +6,7 @@ merge_name=${2:-HEAD}
+ : ${GIT_DIR=.git}
+ : ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"}
+
+-download_one () {
+- # remote_path="$1" local_file="$2"
+- case "$1" in
+- http://*)
+- wget -q -O "$2" "$1" ;;
+- /*)
+- test -f "$1" && cat >"$2" "$1" ;;
+- *)
+- rsync -L "$1" "$2" ;;
+- esac
+-}
+-
+-download_objects () {
+- # remote_repo="$1" head_sha1="$2"
+- case "$1" in
+- http://*)
+- git-http-pull -a "$2" "$1/"
+- ;;
+- /*)
+- git-local-pull -l -a "$2" "$1/"
+- ;;
+- *)
+- rsync -avz --ignore-existing \
+- "$1/objects/." "$GIT_OBJECT_DIRECTORY"/.
+- ;;
+- esac
+-}
+-
+-echo "Getting remote $merge_name"
+-download_one "$merge_repo/$merge_name" "$GIT_DIR"/MERGE_HEAD
+-
+-echo "Getting object database"
+-download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)"
++git-fetch-script "$merge_repo" "$merge_name"
+
+ git-resolve-script \
+ "$(cat "$GIT_DIR"/HEAD)" \
diff --git a/t/t4100/t-apply-3.expect b/t/t4100/t-apply-3.expect
new file mode 100644
index 0000000000..912a552a7a
--- /dev/null
+++ b/t/t4100/t-apply-3.expect
@@ -0,0 +1,7 @@
+ Documentation/git-ls-tree.txt | 20 +-
+ ls-tree.c | 459 ++++++++++++++++++++++-------------------
+ t/t3100-ls-tree-restrict.sh | 3
+ tree.c | 2
+ tree.h | 1
+ 5 files changed, 262 insertions(+), 223 deletions(-)
+ rewrite ls-tree.c (82%)
diff --git a/t/t4100/t-apply-3.patch b/t/t4100/t-apply-3.patch
new file mode 100644
index 0000000000..90cdbaa5bb
--- /dev/null
+++ b/t/t4100/t-apply-3.patch
@@ -0,0 +1,567 @@
+6af1f0192ff8740fe77db7cf02c739ccfbdf119c (from 2bc2564145835996734d6ed5d1880f85b17233d6)
+diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
+--- a/Documentation/git-ls-tree.txt
++++ b/Documentation/git-ls-tree.txt
+@@ -4,23 +4,26 @@ v0.1, May 2005
+
+ NAME
+ ----
+-git-ls-tree - Displays a tree object in human readable form
++git-ls-tree - Lists the contents of a tree object.
+
+
+ SYNOPSIS
+ --------
+-'git-ls-tree' [-r] [-z] <tree-ish> [paths...]
++'git-ls-tree' [-d] [-r] [-z] <tree-ish> [paths...]
+
+ DESCRIPTION
+ -----------
+-Converts the tree object to a human readable (and script processable)
+-form.
++Lists the contents of a tree object, like what "/bin/ls -a" does
++in the current working directory.
+
+ OPTIONS
+ -------
+ <tree-ish>::
+ Id of a tree.
+
++-d::
++ show only the named tree entry itself, not its children
++
+ -r::
+ recurse into sub-trees
+
+@@ -28,18 +31,19 @@ OPTIONS
+ \0 line termination on output
+
+ paths::
+- Optionally, restrict the output of git-ls-tree to specific
+- paths. Directories will only list their tree blob ids.
+- Implies -r.
++ When paths are given, shows them. Otherwise implicitly
++ uses the root level of the tree as the sole path argument.
++
+
+ Output Format
+ -------------
+- <mode>\t <type>\t <object>\t <file>
++ <mode> SP <type> SP <object> TAB <file>
+
+
+ Author
+ ------
+ Written by Linus Torvalds <torvalds@osdl.org>
++Completely rewritten from scratch by Junio C Hamano <junkio@cox.net>
+
+ Documentation
+ --------------
+diff --git a/ls-tree.c b/ls-tree.c
+dissimilarity index 82%
+--- ls-tree.c
++++ ls-tree.c
+@@ -1,212 +1,247 @@
+-/*
+- * GIT - The information manager from hell
+- *
+- * Copyright (C) Linus Torvalds, 2005
+- */
+-#include "cache.h"
+-
+-static int line_termination = '\n';
+-static int recursive = 0;
+-
+-struct path_prefix {
+- struct path_prefix *prev;
+- const char *name;
+-};
+-
+-#define DEBUG(fmt, ...)
+-
+-static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix)
+-{
+- int len = 0;
+- if (prefix) {
+- if (prefix->prev) {
+- len = string_path_prefix(buff,blen,prefix->prev);
+- buff += len;
+- blen -= len;
+- if (blen > 0) {
+- *buff = '/';
+- len++;
+- buff++;
+- blen--;
+- }
+- }
+- strncpy(buff,prefix->name,blen);
+- return len + strlen(prefix->name);
+- }
+-
+- return 0;
+-}
+-
+-static void print_path_prefix(struct path_prefix *prefix)
+-{
+- if (prefix) {
+- if (prefix->prev) {
+- print_path_prefix(prefix->prev);
+- putchar('/');
+- }
+- fputs(prefix->name, stdout);
+- }
+-}
+-
+-/*
+- * return:
+- * -1 if prefix is *not* a subset of path
+- * 0 if prefix == path
+- * 1 if prefix is a subset of path
+- */
+-static int pathcmp(const char *path, struct path_prefix *prefix)
+-{
+- char buff[PATH_MAX];
+- int len,slen;
+-
+- if (prefix == NULL)
+- return 1;
+-
+- len = string_path_prefix(buff, sizeof buff, prefix);
+- slen = strlen(path);
+-
+- if (slen < len)
+- return -1;
+-
+- if (strncmp(path,buff,len) == 0) {
+- if (slen == len)
+- return 0;
+- else
+- return 1;
+- }
+-
+- return -1;
+-}
+-
+-/*
+- * match may be NULL, or a *sorted* list of paths
+- */
+-static void list_recursive(void *buffer,
+- const char *type,
+- unsigned long size,
+- struct path_prefix *prefix,
+- char **match, int matches)
+-{
+- struct path_prefix this_prefix;
+- this_prefix.prev = prefix;
+-
+- if (strcmp(type, "tree"))
+- die("expected a 'tree' node");
+-
+- if (matches)
+- recursive = 1;
+-
+- while (size) {
+- int namelen = strlen(buffer)+1;
+- void *eltbuf = NULL;
+- char elttype[20];
+- unsigned long eltsize;
+- unsigned char *sha1 = buffer + namelen;
+- char *path = strchr(buffer, ' ') + 1;
+- unsigned int mode;
+- const char *matched = NULL;
+- int mtype = -1;
+- int mindex;
+-
+- if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
+- die("corrupt 'tree' file");
+- buffer = sha1 + 20;
+- size -= namelen + 20;
+-
+- this_prefix.name = path;
+- for ( mindex = 0; mindex < matches; mindex++) {
+- mtype = pathcmp(match[mindex],&this_prefix);
+- if (mtype >= 0) {
+- matched = match[mindex];
+- break;
+- }
+- }
+-
+- /*
+- * If we're not matching, or if this is an exact match,
+- * print out the info
+- */
+- if (!matches || (matched != NULL && mtype == 0)) {
+- printf("%06o %s %s\t", mode,
+- S_ISDIR(mode) ? "tree" : "blob",
+- sha1_to_hex(sha1));
+- print_path_prefix(&this_prefix);
+- putchar(line_termination);
+- }
+-
+- if (! recursive || ! S_ISDIR(mode))
+- continue;
+-
+- if (matches && ! matched)
+- continue;
+-
+- if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) {
+- error("cannot read %s", sha1_to_hex(sha1));
+- continue;
+- }
+-
+- /* If this is an exact directory match, we may have
+- * directory files following this path. Match on them.
+- * Otherwise, we're at a pach subcomponent, and we need
+- * to try to match again.
+- */
+- if (mtype == 0)
+- mindex++;
+-
+- list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex);
+- free(eltbuf);
+- }
+-}
+-
+-static int qcmp(const void *a, const void *b)
+-{
+- return strcmp(*(char **)a, *(char **)b);
+-}
+-
+-static int list(unsigned char *sha1,char **path)
+-{
+- void *buffer;
+- unsigned long size;
+- int npaths;
+-
+- for (npaths = 0; path[npaths] != NULL; npaths++)
+- ;
+-
+- qsort(path,npaths,sizeof(char *),qcmp);
+-
+- buffer = read_object_with_reference(sha1, "tree", &size, NULL);
+- if (!buffer)
+- die("unable to read sha1 file");
+- list_recursive(buffer, "tree", size, NULL, path, npaths);
+- free(buffer);
+- return 0;
+-}
+-
+-static const char *ls_tree_usage = "git-ls-tree [-r] [-z] <key> [paths...]";
+-
+-int main(int argc, char **argv)
+-{
+- unsigned char sha1[20];
+-
+- while (1 < argc && argv[1][0] == '-') {
+- switch (argv[1][1]) {
+- case 'z':
+- line_termination = 0;
+- break;
+- case 'r':
+- recursive = 1;
+- break;
+- default:
+- usage(ls_tree_usage);
+- }
+- argc--; argv++;
+- }
+-
+- if (argc < 2)
+- usage(ls_tree_usage);
+- if (get_sha1(argv[1], sha1) < 0)
+- usage(ls_tree_usage);
+- if (list(sha1, &argv[2]) < 0)
+- die("list failed");
+- return 0;
+-}
++/*
++ * GIT - The information manager from hell
++ *
++ * Copyright (C) Linus Torvalds, 2005
++ */
++#include "cache.h"
++#include "blob.h"
++#include "tree.h"
++
++static int line_termination = '\n';
++#define LS_RECURSIVE 1
++#define LS_TREE_ONLY 2
++static int ls_options = 0;
++
++static struct tree_entry_list root_entry;
++
++static void prepare_root(unsigned char *sha1)
++{
++ unsigned char rsha[20];
++ unsigned long size;
++ void *buf;
++ struct tree *root_tree;
++
++ buf = read_object_with_reference(sha1, "tree", &size, rsha);
++ free(buf);
++ if (!buf)
++ die("Could not read %s", sha1_to_hex(sha1));
++
++ root_tree = lookup_tree(rsha);
++ if (!root_tree)
++ die("Could not read %s", sha1_to_hex(sha1));
++
++ /* Prepare a fake entry */
++ root_entry.directory = 1;
++ root_entry.executable = root_entry.symlink = 0;
++ root_entry.mode = S_IFDIR;
++ root_entry.name = "";
++ root_entry.item.tree = root_tree;
++ root_entry.parent = NULL;
++}
++
++static int prepare_children(struct tree_entry_list *elem)
++{
++ if (!elem->directory)
++ return -1;
++ if (!elem->item.tree->object.parsed) {
++ struct tree_entry_list *e;
++ if (parse_tree(elem->item.tree))
++ return -1;
++ /* Set up the parent link */
++ for (e = elem->item.tree->entries; e; e = e->next)
++ e->parent = elem;
++ }
++ return 0;
++}
++
++static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem,
++ const char *path,
++ const char *path_end)
++{
++ const char *ep;
++ int len;
++
++ while (path < path_end) {
++ if (prepare_children(elem))
++ return NULL;
++
++ /* In elem->tree->entries, find the one that has name
++ * that matches what is between path and ep.
++ */
++ elem = elem->item.tree->entries;
++
++ ep = strchr(path, '/');
++ if (!ep || path_end <= ep)
++ ep = path_end;
++ len = ep - path;
++
++ while (elem) {
++ if ((strlen(elem->name) == len) &&
++ !strncmp(elem->name, path, len))
++ break;
++ elem = elem->next;
++ }
++ if (path_end <= ep || !elem)
++ return elem;
++ while (*ep == '/' && ep < path_end)
++ ep++;
++ path = ep;
++ }
++ return NULL;
++}
++
++static struct tree_entry_list *find_entry(const char *path,
++ const char *path_end)
++{
++ /* Find tree element, descending from root, that
++ * corresponds to the named path, lazily expanding
++ * the tree if possible.
++ */
++ if (path == path_end) {
++ /* Special. This is the root level */
++ return &root_entry;
++ }
++ return find_entry_0(&root_entry, path, path_end);
++}
++
++static void show_entry_name(struct tree_entry_list *e)
++{
++ /* This is yucky. The root level is there for
++ * our convenience but we really want to do a
++ * forest.
++ */
++ if (e->parent && e->parent != &root_entry) {
++ show_entry_name(e->parent);
++ putchar('/');
++ }
++ printf("%s", e->name);
++}
++
++static const char *entry_type(struct tree_entry_list *e)
++{
++ return (e->directory ? "tree" : "blob");
++}
++
++static const char *entry_hex(struct tree_entry_list *e)
++{
++ return sha1_to_hex(e->directory
++ ? e->item.tree->object.sha1
++ : e->item.blob->object.sha1);
++}
++
++/* forward declaration for mutually recursive routines */
++static int show_entry(struct tree_entry_list *, int);
++
++static int show_children(struct tree_entry_list *e, int level)
++{
++ if (prepare_children(e))
++ die("internal error: ls-tree show_children called with non tree");
++ e = e->item.tree->entries;
++ while (e) {
++ show_entry(e, level);
++ e = e->next;
++ }
++ return 0;
++}
++
++static int show_entry(struct tree_entry_list *e, int level)
++{
++ int err = 0;
++
++ if (e != &root_entry) {
++ printf("%06o %s %s ", e->mode, entry_type(e),
++ entry_hex(e));
++ show_entry_name(e);
++ putchar(line_termination);
++ }
++
++ if (e->directory) {
++ /* If this is a directory, we have the following cases:
++ * (1) This is the top-level request (explicit path from the
++ * command line, or "root" if there is no command line).
++ * a. Without any flag. We show direct children. We do not
++ * recurse into them.
++ * b. With -r. We do recurse into children.
++ * c. With -d. We do not recurse into children.
++ * (2) We came here because our caller is either (1-a) or
++ * (1-b).
++ * a. Without any flag. We do not show our children (which
++ * are grandchildren for the original request).
++ * b. With -r. We continue to recurse into our children.
++ * c. With -d. We should not have come here to begin with.
++ */
++ if (level == 0 && !(ls_options & LS_TREE_ONLY))
++ /* case (1)-a and (1)-b */
++ err = err | show_children(e, level+1);
++ else if (level && ls_options & LS_RECURSIVE)
++ /* case (2)-b */
++ err = err | show_children(e, level+1);
++ }
++ return err;
++}
++
++static int list_one(const char *path, const char *path_end)
++{
++ int err = 0;
++ struct tree_entry_list *e = find_entry(path, path_end);
++ if (!e) {
++ /* traditionally ls-tree does not complain about
++ * missing path. We may change this later to match
++ * what "/bin/ls -a" does, which is to complain.
++ */
++ return err;
++ }
++ err = err | show_entry(e, 0);
++ return err;
++}
++
++static int list(char **path)
++{
++ int i;
++ int err = 0;
++ for (i = 0; path[i]; i++) {
++ int len = strlen(path[i]);
++ while (0 <= len && path[i][len] == '/')
++ len--;
++ err = err | list_one(path[i], path[i] + len);
++ }
++ return err;
++}
++
++static const char *ls_tree_usage =
++ "git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
++
++int main(int argc, char **argv)
++{
++ static char *path0[] = { "", NULL };
++ char **path;
++ unsigned char sha1[20];
++
++ while (1 < argc && argv[1][0] == '-') {
++ switch (argv[1][1]) {
++ case 'z':
++ line_termination = 0;
++ break;
++ case 'r':
++ ls_options |= LS_RECURSIVE;
++ break;
++ case 'd':
++ ls_options |= LS_TREE_ONLY;
++ break;
++ default:
++ usage(ls_tree_usage);
++ }
++ argc--; argv++;
++ }
++
++ if (argc < 2)
++ usage(ls_tree_usage);
++ if (get_sha1(argv[1], sha1) < 0)
++ usage(ls_tree_usage);
++
++ path = (argc == 2) ? path0 : (argv + 2);
++ prepare_root(sha1);
++ if (list(path) < 0)
++ die("list failed");
++ return 0;
++}
+diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
+--- a/t/t3100-ls-tree-restrict.sh
++++ b/t/t3100-ls-tree-restrict.sh
+@@ -74,8 +74,8 @@ test_expect_success \
+ 'ls-tree filtered' \
+ 'git-ls-tree $tree path1 path0 >current &&
+ cat >expected <<\EOF &&
+-100644 blob X path0
+ 120000 blob X path1
++100644 blob X path0
+ EOF
+ test_output'
+
+@@ -85,7 +85,6 @@ test_expect_success \
+ cat >expected <<\EOF &&
+ 040000 tree X path2
+ 040000 tree X path2/baz
+-100644 blob X path2/baz/b
+ 120000 blob X path2/bazbo
+ 100644 blob X path2/foo
+ EOF
+diff --git a/tree.c b/tree.c
+--- a/tree.c
++++ b/tree.c
+@@ -133,7 +133,7 @@ int parse_tree_buffer(struct tree *item,
+ }
+ if (obj)
+ add_ref(&item->object, obj);
+-
++ entry->parent = NULL; /* needs to be filled by the user */
+ *list_p = entry;
+ list_p = &entry->next;
+ }
+diff --git a/tree.h b/tree.h
+--- a/tree.h
++++ b/tree.h
+@@ -16,6 +16,7 @@ struct tree_entry_list {
+ struct tree *tree;
+ struct blob *blob;
+ } item;
++ struct tree_entry_list *parent;
+ };
+
+ struct tree {
diff --git a/t/t4100/t-apply-4.expect b/t/t4100/t-apply-4.expect
new file mode 100644
index 0000000000..1ec028b3d0
--- /dev/null
+++ b/t/t4100/t-apply-4.expect
@@ -0,0 +1,5 @@
+ t/t0000-basic.sh | 0
+ t/test-lib.sh | 0
+ 2 files changed, 0 insertions(+), 0 deletions(-)
+ mode change 100644 => 100755 t/t0000-basic.sh
+ mode change 100644 => 100755 t/test-lib.sh
diff --git a/t/t4100/t-apply-4.patch b/t/t4100/t-apply-4.patch
new file mode 100644
index 0000000000..4a56ab5cf4
--- /dev/null
+++ b/t/t4100/t-apply-4.patch
@@ -0,0 +1,7 @@
+ceede59ea90cebad52ba9c8263fef3fb6ef17593 (from 368f99d57e8ed17243f2e164431449d48bfca2fb)
+diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
+old mode 100644
+new mode 100755
+diff --git a/t/test-lib.sh b/t/test-lib.sh
+old mode 100644
+new mode 100755
diff --git a/t/t4100/t-apply-5.expect b/t/t4100/t-apply-5.expect
new file mode 100644
index 0000000000..b387df15d4
--- /dev/null
+++ b/t/t4100/t-apply-5.expect
@@ -0,0 +1,19 @@
+ Documentation/git-rpull.txt | 50 -------------------
+ Documentation/git-rpush.txt | 30 ------------
+ Documentation/git-ssh-pull.txt | 50 +++++++++++++++++++
+ Documentation/git-ssh-push.txt | 30 ++++++++++++
+ Documentation/git.txt | 6 +-
+ Makefile | 6 +-
+ rpull.c | 83 --------------------------------
+ rpush.c | 104 ----------------------------------------
+ ssh-pull.c | 83 ++++++++++++++++++++++++++++++++
+ ssh-push.c | 104 ++++++++++++++++++++++++++++++++++++++++
+ 10 files changed, 273 insertions(+), 273 deletions(-)
+ delete Documentation/git-rpull.txt
+ delete Documentation/git-rpush.txt
+ create Documentation/git-ssh-pull.txt
+ create Documentation/git-ssh-push.txt
+ delete rpull.c
+ delete rpush.c
+ create ssh-pull.c
+ create ssh-push.c
diff --git a/t/t4100/t-apply-5.patch b/t/t4100/t-apply-5.patch
new file mode 100644
index 0000000000..de11623d1b
--- /dev/null
+++ b/t/t4100/t-apply-5.patch
@@ -0,0 +1,612 @@
+diff a/Documentation/git-rpull.txt b/Documentation/git-rpull.txt
+--- a/Documentation/git-rpull.txt
++++ /dev/null
+@@ -1,50 +0,0 @@
+-git-rpull(1)
+-============
+-v0.1, May 2005
+-
+-NAME
+-----
+-git-rpull - Pulls from a remote repository over ssh connection
+-
+-
+-
+-SYNOPSIS
+---------
+-'git-rpull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url
+-
+-DESCRIPTION
+------------
+-Pulls from a remote repository over ssh connection, invoking git-rpush on
+-the other end.
+-
+-OPTIONS
+--------
+--c::
+- Get the commit objects.
+--t::
+- Get trees associated with the commit objects.
+--a::
+- Get all the objects.
+--d::
+- Do not check for delta base objects (use this option
+- only when you know the remote repository is not
+- deltified).
+---recover::
+- Check dependency of deltified object more carefully than
+- usual, to recover after earlier pull that was interrupted.
+--v::
+- Report what is downloaded.
+-
+-
+-Author
+-------
+-Written by Linus Torvalds <torvalds@osdl.org>
+-
+-Documentation
+---------------
+-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+-
+-GIT
+----
+-Part of the link:git.html[git] suite
+-
+diff a/Documentation/git-rpush.txt b/Documentation/git-rpush.txt
+--- a/Documentation/git-rpush.txt
++++ /dev/null
+@@ -1,30 +0,0 @@
+-git-rpush(1)
+-============
+-v0.1, May 2005
+-
+-NAME
+-----
+-git-rpush - Helper "server-side" program used by git-rpull
+-
+-
+-SYNOPSIS
+---------
+-'git-rpush'
+-
+-DESCRIPTION
+------------
+-Helper "server-side" program used by git-rpull.
+-
+-
+-Author
+-------
+-Written by Linus Torvalds <torvalds@osdl.org>
+-
+-Documentation
+---------------
+-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+-
+-GIT
+----
+-Part of the link:git.html[git] suite
+-
+diff a/Documentation/git-ssh-pull.txt b/Documentation/git-ssh-pull.txt
+--- /dev/null
++++ b/Documentation/git-ssh-pull.txt
+@@ -0,0 +1,50 @@
++git-ssh-pull(1)
++===============
++v0.1, May 2005
++
++NAME
++----
++git-ssh-pull - Pulls from a remote repository over ssh connection
++
++
++
++SYNOPSIS
++--------
++'git-ssh-pull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url
++
++DESCRIPTION
++-----------
++Pulls from a remote repository over ssh connection, invoking git-ssh-push
++on the other end.
++
++OPTIONS
++-------
++-c::
++ Get the commit objects.
++-t::
++ Get trees associated with the commit objects.
++-a::
++ Get all the objects.
++-d::
++ Do not check for delta base objects (use this option
++ only when you know the remote repository is not
++ deltified).
++--recover::
++ Check dependency of deltified object more carefully than
++ usual, to recover after earlier pull that was interrupted.
++-v::
++ Report what is downloaded.
++
++
++Author
++------
++Written by Linus Torvalds <torvalds@osdl.org>
++
++Documentation
++--------------
++Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
++
++GIT
++---
++Part of the link:git.html[git] suite
++
+diff a/Documentation/git-ssh-push.txt b/Documentation/git-ssh-push.txt
+--- /dev/null
++++ b/Documentation/git-ssh-push.txt
+@@ -0,0 +1,30 @@
++git-ssh-push(1)
++===============
++v0.1, May 2005
++
++NAME
++----
++git-ssh-push - Helper "server-side" program used by git-ssh-pull
++
++
++SYNOPSIS
++--------
++'git-ssh-push'
++
++DESCRIPTION
++-----------
++Helper "server-side" program used by git-ssh-pull.
++
++
++Author
++------
++Written by Linus Torvalds <torvalds@osdl.org>
++
++Documentation
++--------------
++Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
++
++GIT
++---
++Part of the link:git.html[git] suite
++
+diff a/Documentation/git.txt b/Documentation/git.txt
+--- a/Documentation/git.txt
++++ b/Documentation/git.txt
+@@ -148,7 +148,7 @@ link:git-resolve-script.html[git-resolve
+ link:git-tag-script.html[git-tag-script]::
+ An example script to create a tag object signed with GPG
+
+-link:git-rpull.html[git-rpull]::
++link:git-ssh-pull.html[git-ssh-pull]::
+ Pulls from a remote repository over ssh connection
+
+ Interogators:
+@@ -156,8 +156,8 @@ Interogators:
+ link:git-diff-helper.html[git-diff-helper]::
+ Generates patch format output for git-diff-*
+
+-link:git-rpush.html[git-rpush]::
+- Helper "server-side" program used by git-rpull
++link:git-ssh-push.html[git-ssh-push]::
++ Helper "server-side" program used by git-ssh-pull
+
+
+
+diff a/Makefile b/Makefile
+--- a/Makefile
++++ b/Makefile
+@@ -30,7 +30,7 @@ PROG= git-update-cache git-diff-files
+ git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
+ git-check-files git-ls-tree git-merge-base git-merge-cache \
+ git-unpack-file git-export git-diff-cache git-convert-cache \
+- git-http-pull git-rpush git-rpull git-rev-list git-mktag \
++ git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \
+ git-diff-helper git-tar-tree git-local-pull git-write-blob \
+ git-get-tar-commit-id git-mkdelta git-apply git-stripspace
+
+@@ -105,8 +105,8 @@ git-diff-cache: diff-cache.c
+ git-convert-cache: convert-cache.c
+ git-http-pull: http-pull.c pull.c
+ git-local-pull: local-pull.c pull.c
+-git-rpush: rsh.c
+-git-rpull: rsh.c pull.c
++git-ssh-push: rsh.c
++git-ssh-pull: rsh.c pull.c
+ git-rev-list: rev-list.c
+ git-mktag: mktag.c
+ git-diff-helper: diff-helper.c
+diff a/rpull.c b/rpull.c
+--- a/rpull.c
++++ /dev/null
+@@ -1,83 +0,0 @@
+-#include "cache.h"
+-#include "commit.h"
+-#include "rsh.h"
+-#include "pull.h"
+-
+-static int fd_in;
+-static int fd_out;
+-
+-static unsigned char remote_version = 0;
+-static unsigned char local_version = 1;
+-
+-int fetch(unsigned char *sha1)
+-{
+- int ret;
+- signed char remote;
+- char type = 'o';
+- if (has_sha1_file(sha1))
+- return 0;
+- write(fd_out, &type, 1);
+- write(fd_out, sha1, 20);
+- if (read(fd_in, &remote, 1) < 1)
+- return -1;
+- if (remote < 0)
+- return remote;
+- ret = write_sha1_from_fd(sha1, fd_in);
+- if (!ret)
+- pull_say("got %s\n", sha1_to_hex(sha1));
+- return ret;
+-}
+-
+-int get_version(void)
+-{
+- char type = 'v';
+- write(fd_out, &type, 1);
+- write(fd_out, &local_version, 1);
+- if (read(fd_in, &remote_version, 1) < 1) {
+- return error("Couldn't read version from remote end");
+- }
+- return 0;
+-}
+-
+-int main(int argc, char **argv)
+-{
+- char *commit_id;
+- char *url;
+- int arg = 1;
+-
+- while (arg < argc && argv[arg][0] == '-') {
+- if (argv[arg][1] == 't') {
+- get_tree = 1;
+- } else if (argv[arg][1] == 'c') {
+- get_history = 1;
+- } else if (argv[arg][1] == 'd') {
+- get_delta = 0;
+- } else if (!strcmp(argv[arg], "--recover")) {
+- get_delta = 2;
+- } else if (argv[arg][1] == 'a') {
+- get_all = 1;
+- get_tree = 1;
+- get_history = 1;
+- } else if (argv[arg][1] == 'v') {
+- get_verbosely = 1;
+- }
+- arg++;
+- }
+- if (argc < arg + 2) {
+- usage("git-rpull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url");
+- return 1;
+- }
+- commit_id = argv[arg];
+- url = argv[arg + 1];
+-
+- if (setup_connection(&fd_in, &fd_out, "git-rpush", url, arg, argv + 1))
+- return 1;
+-
+- if (get_version())
+- return 1;
+-
+- if (pull(commit_id))
+- return 1;
+-
+- return 0;
+-}
+diff a/rpush.c b/rpush.c
+--- a/rpush.c
++++ /dev/null
+@@ -1,104 +0,0 @@
+-#include "cache.h"
+-#include "rsh.h"
+-#include <sys/socket.h>
+-#include <errno.h>
+-
+-unsigned char local_version = 1;
+-unsigned char remote_version = 0;
+-
+-int serve_object(int fd_in, int fd_out) {
+- ssize_t size;
+- int posn = 0;
+- char sha1[20];
+- unsigned long objsize;
+- void *buf;
+- signed char remote;
+- do {
+- size = read(fd_in, sha1 + posn, 20 - posn);
+- if (size < 0) {
+- perror("git-rpush: read ");
+- return -1;
+- }
+- if (!size)
+- return -1;
+- posn += size;
+- } while (posn < 20);
+-
+- /* fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1)); */
+- remote = 0;
+-
+- buf = map_sha1_file(sha1, &objsize);
+-
+- if (!buf) {
+- fprintf(stderr, "git-rpush: could not find %s\n",
+- sha1_to_hex(sha1));
+- remote = -1;
+- }
+-
+- write(fd_out, &remote, 1);
+-
+- if (remote < 0)
+- return 0;
+-
+- posn = 0;
+- do {
+- size = write(fd_out, buf + posn, objsize - posn);
+- if (size <= 0) {
+- if (!size) {
+- fprintf(stderr, "git-rpush: write closed");
+- } else {
+- perror("git-rpush: write ");
+- }
+- return -1;
+- }
+- posn += size;
+- } while (posn < objsize);
+- return 0;
+-}
+-
+-int serve_version(int fd_in, int fd_out)
+-{
+- if (read(fd_in, &remote_version, 1) < 1)
+- return -1;
+- write(fd_out, &local_version, 1);
+- return 0;
+-}
+-
+-void service(int fd_in, int fd_out) {
+- char type;
+- int retval;
+- do {
+- retval = read(fd_in, &type, 1);
+- if (retval < 1) {
+- if (retval < 0)
+- perror("rpush: read ");
+- return;
+- }
+- if (type == 'v' && serve_version(fd_in, fd_out))
+- return;
+- if (type == 'o' && serve_object(fd_in, fd_out))
+- return;
+- } while (1);
+-}
+-
+-int main(int argc, char **argv)
+-{
+- int arg = 1;
+- char *commit_id;
+- char *url;
+- int fd_in, fd_out;
+- while (arg < argc && argv[arg][0] == '-') {
+- arg++;
+- }
+- if (argc < arg + 2) {
+- usage("git-rpush [-c] [-t] [-a] commit-id url");
+- return 1;
+- }
+- commit_id = argv[arg];
+- url = argv[arg + 1];
+- if (setup_connection(&fd_in, &fd_out, "git-rpull", url, arg, argv + 1))
+- return 1;
+-
+- service(fd_in, fd_out);
+- return 0;
+-}
+diff a/ssh-pull.c b/ssh-pull.c
+--- /dev/null
++++ b/ssh-pull.c
+@@ -0,0 +1,83 @@
++#include "cache.h"
++#include "commit.h"
++#include "rsh.h"
++#include "pull.h"
++
++static int fd_in;
++static int fd_out;
++
++static unsigned char remote_version = 0;
++static unsigned char local_version = 1;
++
++int fetch(unsigned char *sha1)
++{
++ int ret;
++ signed char remote;
++ char type = 'o';
++ if (has_sha1_file(sha1))
++ return 0;
++ write(fd_out, &type, 1);
++ write(fd_out, sha1, 20);
++ if (read(fd_in, &remote, 1) < 1)
++ return -1;
++ if (remote < 0)
++ return remote;
++ ret = write_sha1_from_fd(sha1, fd_in);
++ if (!ret)
++ pull_say("got %s\n", sha1_to_hex(sha1));
++ return ret;
++}
++
++int get_version(void)
++{
++ char type = 'v';
++ write(fd_out, &type, 1);
++ write(fd_out, &local_version, 1);
++ if (read(fd_in, &remote_version, 1) < 1) {
++ return error("Couldn't read version from remote end");
++ }
++ return 0;
++}
++
++int main(int argc, char **argv)
++{
++ char *commit_id;
++ char *url;
++ int arg = 1;
++
++ while (arg < argc && argv[arg][0] == '-') {
++ if (argv[arg][1] == 't') {
++ get_tree = 1;
++ } else if (argv[arg][1] == 'c') {
++ get_history = 1;
++ } else if (argv[arg][1] == 'd') {
++ get_delta = 0;
++ } else if (!strcmp(argv[arg], "--recover")) {
++ get_delta = 2;
++ } else if (argv[arg][1] == 'a') {
++ get_all = 1;
++ get_tree = 1;
++ get_history = 1;
++ } else if (argv[arg][1] == 'v') {
++ get_verbosely = 1;
++ }
++ arg++;
++ }
++ if (argc < arg + 2) {
++ usage("git-ssh-pull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url");
++ return 1;
++ }
++ commit_id = argv[arg];
++ url = argv[arg + 1];
++
++ if (setup_connection(&fd_in, &fd_out, "git-ssh-push", url, arg, argv + 1))
++ return 1;
++
++ if (get_version())
++ return 1;
++
++ if (pull(commit_id))
++ return 1;
++
++ return 0;
++}
+diff a/ssh-push.c b/ssh-push.c
+--- /dev/null
++++ b/ssh-push.c
+@@ -0,0 +1,104 @@
++#include "cache.h"
++#include "rsh.h"
++#include <sys/socket.h>
++#include <errno.h>
++
++unsigned char local_version = 1;
++unsigned char remote_version = 0;
++
++int serve_object(int fd_in, int fd_out) {
++ ssize_t size;
++ int posn = 0;
++ char sha1[20];
++ unsigned long objsize;
++ void *buf;
++ signed char remote;
++ do {
++ size = read(fd_in, sha1 + posn, 20 - posn);
++ if (size < 0) {
++ perror("git-ssh-push: read ");
++ return -1;
++ }
++ if (!size)
++ return -1;
++ posn += size;
++ } while (posn < 20);
++
++ /* fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1)); */
++ remote = 0;
++
++ buf = map_sha1_file(sha1, &objsize);
++
++ if (!buf) {
++ fprintf(stderr, "git-ssh-push: could not find %s\n",
++ sha1_to_hex(sha1));
++ remote = -1;
++ }
++
++ write(fd_out, &remote, 1);
++
++ if (remote < 0)
++ return 0;
++
++ posn = 0;
++ do {
++ size = write(fd_out, buf + posn, objsize - posn);
++ if (size <= 0) {
++ if (!size) {
++ fprintf(stderr, "git-ssh-push: write closed");
++ } else {
++ perror("git-ssh-push: write ");
++ }
++ return -1;
++ }
++ posn += size;
++ } while (posn < objsize);
++ return 0;
++}
++
++int serve_version(int fd_in, int fd_out)
++{
++ if (read(fd_in, &remote_version, 1) < 1)
++ return -1;
++ write(fd_out, &local_version, 1);
++ return 0;
++}
++
++void service(int fd_in, int fd_out) {
++ char type;
++ int retval;
++ do {
++ retval = read(fd_in, &type, 1);
++ if (retval < 1) {
++ if (retval < 0)
++ perror("git-ssh-push: read ");
++ return;
++ }
++ if (type == 'v' && serve_version(fd_in, fd_out))
++ return;
++ if (type == 'o' && serve_object(fd_in, fd_out))
++ return;
++ } while (1);
++}
++
++int main(int argc, char **argv)
++{
++ int arg = 1;
++ char *commit_id;
++ char *url;
++ int fd_in, fd_out;
++ while (arg < argc && argv[arg][0] == '-') {
++ arg++;
++ }
++ if (argc < arg + 2) {
++ usage("git-ssh-push [-c] [-t] [-a] commit-id url");
++ return 1;
++ }
++ commit_id = argv[arg];
++ url = argv[arg + 1];
++ if (setup_connection(&fd_in, &fd_out, "git-ssh-pull", url, arg, argv + 1))
++ return 1;
++
++ service(fd_in, fd_out);
++ return 0;
++}
diff --git a/t/t4100/t-apply-6.expect b/t/t4100/t-apply-6.expect
new file mode 100644
index 0000000000..1c343d459e
--- /dev/null
+++ b/t/t4100/t-apply-6.expect
@@ -0,0 +1,5 @@
+ Makefile | 2 +-
+ git-fetch-script | 41 +++++++++++++++++++++++++++++++++++++++++
+ git-pull-script | 34 +---------------------------------
+ 3 files changed, 43 insertions(+), 34 deletions(-)
+ create git-fetch-script
diff --git a/t/t4100/t-apply-6.patch b/t/t4100/t-apply-6.patch
new file mode 100644
index 0000000000..d9753637fc
--- /dev/null
+++ b/t/t4100/t-apply-6.patch
@@ -0,0 +1,101 @@
+diff a/Makefile b/Makefile
+--- a/Makefile
++++ b/Makefile
+@@ -20,7 +20,7 @@ INSTALL=install
+
+ SCRIPTS=git-apply-patch-script git-merge-one-file-script git-prune-script \
+ git-pull-script git-tag-script git-resolve-script git-whatchanged \
+- git-deltafy-script
++ git-deltafy-script git-fetch-script
+
+ PROG= git-update-cache git-diff-files git-init-db git-write-tree \
+ git-read-tree git-commit-tree git-cat-file git-fsck-cache \
+diff a/git-fetch-script b/git-fetch-script
+--- /dev/null
++++ b/git-fetch-script
+@@ -0,0 +1,41 @@
++#!/bin/sh
++#
++merge_repo=$1
++merge_name=${2:-HEAD}
++
++: ${GIT_DIR=.git}
++: ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"}
++
++download_one () {
++ # remote_path="$1" local_file="$2"
++ case "$1" in
++ http://*)
++ wget -q -O "$2" "$1" ;;
++ /*)
++ test -f "$1" && cat >"$2" "$1" ;;
++ *)
++ rsync -L "$1" "$2" ;;
++ esac
++}
++
++download_objects () {
++ # remote_repo="$1" head_sha1="$2"
++ case "$1" in
++ http://*)
++ git-http-pull -a "$2" "$1/"
++ ;;
++ /*)
++ git-local-pull -l -a "$2" "$1/"
++ ;;
++ *)
++ rsync -avz --ignore-existing \
++ "$1/objects/." "$GIT_OBJECT_DIRECTORY"/.
++ ;;
++ esac
++}
++
++echo "Getting remote $merge_name"
++download_one "$merge_repo/$merge_name" "$GIT_DIR"/MERGE_HEAD
++
++echo "Getting object database"
++download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)"
+diff a/git-pull-script b/git-pull-script
+--- a/git-pull-script
++++ b/git-pull-script
+@@ -6,39 +6,7 @@ merge_name=${2:-HEAD}
+ : ${GIT_DIR=.git}
+ : ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"}
+
+-download_one () {
+- # remote_path="$1" local_file="$2"
+- case "$1" in
+- http://*)
+- wget -q -O "$2" "$1" ;;
+- /*)
+- test -f "$1" && cat >"$2" "$1" ;;
+- *)
+- rsync -L "$1" "$2" ;;
+- esac
+-}
+-
+-download_objects () {
+- # remote_repo="$1" head_sha1="$2"
+- case "$1" in
+- http://*)
+- git-http-pull -a "$2" "$1/"
+- ;;
+- /*)
+- git-local-pull -l -a "$2" "$1/"
+- ;;
+- *)
+- rsync -avz --ignore-existing \
+- "$1/objects/." "$GIT_OBJECT_DIRECTORY"/.
+- ;;
+- esac
+-}
+-
+-echo "Getting remote $merge_name"
+-download_one "$merge_repo/$merge_name" "$GIT_DIR"/MERGE_HEAD
+-
+-echo "Getting object database"
+-download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)"
++git-fetch-script "$merge_repo" "$merge_name"
+
+ git-resolve-script \
+ "$(cat "$GIT_DIR"/HEAD)" \
diff --git a/t/t4100/t-apply-7.expect b/t/t4100/t-apply-7.expect
new file mode 100644
index 0000000000..1283164d99
--- /dev/null
+++ b/t/t4100/t-apply-7.expect
@@ -0,0 +1,6 @@
+ Documentation/git-ls-tree.txt | 20 +-
+ ls-tree.c | 333 +++++++++++++++++++++++------------------
+ t/t3100-ls-tree-restrict.sh | 3
+ tree.c | 2
+ tree.h | 1
+ 5 files changed, 199 insertions(+), 160 deletions(-)
diff --git a/t/t4100/t-apply-7.patch b/t/t4100/t-apply-7.patch
new file mode 100644
index 0000000000..07c6589e74
--- /dev/null
+++ b/t/t4100/t-apply-7.patch
@@ -0,0 +1,494 @@
+diff a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
+--- a/Documentation/git-ls-tree.txt
++++ b/Documentation/git-ls-tree.txt
+@@ -4,23 +4,26 @@ v0.1, May 2005
+
+ NAME
+ ----
+-git-ls-tree - Displays a tree object in human readable form
++git-ls-tree - Lists the contents of a tree object.
+
+
+ SYNOPSIS
+ --------
+-'git-ls-tree' [-r] [-z] <tree-ish> [paths...]
++'git-ls-tree' [-d] [-r] [-z] <tree-ish> [paths...]
+
+ DESCRIPTION
+ -----------
+-Converts the tree object to a human readable (and script processable)
+-form.
++Lists the contents of a tree object, like what "/bin/ls -a" does
++in the current working directory.
+
+ OPTIONS
+ -------
+ <tree-ish>::
+ Id of a tree.
+
++-d::
++ show only the named tree entry itself, not its children
++
+ -r::
+ recurse into sub-trees
+
+@@ -28,18 +31,19 @@ OPTIONS
+ \0 line termination on output
+
+ paths::
+- Optionally, restrict the output of git-ls-tree to specific
+- paths. Directories will only list their tree blob ids.
+- Implies -r.
++ When paths are given, shows them. Otherwise implicitly
++ uses the root level of the tree as the sole path argument.
++
+
+ Output Format
+ -------------
+- <mode>\t <type>\t <object>\t <file>
++ <mode> SP <type> SP <object> TAB <file>
+
+
+ Author
+ ------
+ Written by Linus Torvalds <torvalds@osdl.org>
++Completely rewritten from scratch by Junio C Hamano <junkio@cox.net>
+
+ Documentation
+ --------------
+diff a/ls-tree.c b/ls-tree.c
+--- a/ls-tree.c
++++ b/ls-tree.c
+@@ -4,188 +4,217 @@
+ * Copyright (C) Linus Torvalds, 2005
+ */
+ #include "cache.h"
++#include "blob.h"
++#include "tree.h"
+
+ static int line_termination = '\n';
+-static int recursive = 0;
++#define LS_RECURSIVE 1
++#define LS_TREE_ONLY 2
++static int ls_options = 0;
+
+-struct path_prefix {
+- struct path_prefix *prev;
+- const char *name;
+-};
+-
+-#define DEBUG(fmt, ...)
+-
+-static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix)
+-{
+- int len = 0;
+- if (prefix) {
+- if (prefix->prev) {
+- len = string_path_prefix(buff,blen,prefix->prev);
+- buff += len;
+- blen -= len;
+- if (blen > 0) {
+- *buff = '/';
+- len++;
+- buff++;
+- blen--;
+- }
+- }
+- strncpy(buff,prefix->name,blen);
+- return len + strlen(prefix->name);
+- }
++static struct tree_entry_list root_entry;
+
+- return 0;
++static void prepare_root(unsigned char *sha1)
++{
++ unsigned char rsha[20];
++ unsigned long size;
++ void *buf;
++ struct tree *root_tree;
++
++ buf = read_object_with_reference(sha1, "tree", &size, rsha);
++ free(buf);
++ if (!buf)
++ die("Could not read %s", sha1_to_hex(sha1));
++
++ root_tree = lookup_tree(rsha);
++ if (!root_tree)
++ die("Could not read %s", sha1_to_hex(sha1));
++
++ /* Prepare a fake entry */
++ root_entry.directory = 1;
++ root_entry.executable = root_entry.symlink = 0;
++ root_entry.mode = S_IFDIR;
++ root_entry.name = "";
++ root_entry.item.tree = root_tree;
++ root_entry.parent = NULL;
+ }
+
+-static void print_path_prefix(struct path_prefix *prefix)
++static int prepare_children(struct tree_entry_list *elem)
+ {
+- if (prefix) {
+- if (prefix->prev) {
+- print_path_prefix(prefix->prev);
+- putchar('/');
+- }
+- fputs(prefix->name, stdout);
++ if (!elem->directory)
++ return -1;
++ if (!elem->item.tree->object.parsed) {
++ struct tree_entry_list *e;
++ if (parse_tree(elem->item.tree))
++ return -1;
++ /* Set up the parent link */
++ for (e = elem->item.tree->entries; e; e = e->next)
++ e->parent = elem;
+ }
++ return 0;
+ }
+
+-/*
+- * return:
+- * -1 if prefix is *not* a subset of path
+- * 0 if prefix == path
+- * 1 if prefix is a subset of path
+- */
+-static int pathcmp(const char *path, struct path_prefix *prefix)
+-{
+- char buff[PATH_MAX];
+- int len,slen;
++static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem,
++ const char *path,
++ const char *path_end)
++{
++ const char *ep;
++ int len;
++
++ while (path < path_end) {
++ if (prepare_children(elem))
++ return NULL;
+
+- if (prefix == NULL)
+- return 1;
++ /* In elem->tree->entries, find the one that has name
++ * that matches what is between path and ep.
++ */
++ elem = elem->item.tree->entries;
+
+- len = string_path_prefix(buff, sizeof buff, prefix);
+- slen = strlen(path);
++ ep = strchr(path, '/');
++ if (!ep || path_end <= ep)
++ ep = path_end;
++ len = ep - path;
++
++ while (elem) {
++ if ((strlen(elem->name) == len) &&
++ !strncmp(elem->name, path, len))
++ break;
++ elem = elem->next;
++ }
++ if (path_end <= ep || !elem)
++ return elem;
++ while (*ep == '/' && ep < path_end)
++ ep++;
++ path = ep;
++ }
++ return NULL;
++}
+
+- if (slen < len)
+- return -1;
++static struct tree_entry_list *find_entry(const char *path,
++ const char *path_end)
++{
++ /* Find tree element, descending from root, that
++ * corresponds to the named path, lazily expanding
++ * the tree if possible.
++ */
++ if (path == path_end) {
++ /* Special. This is the root level */
++ return &root_entry;
++ }
++ return find_entry_0(&root_entry, path, path_end);
++}
+
+- if (strncmp(path,buff,len) == 0) {
+- if (slen == len)
+- return 0;
+- else
+- return 1;
++static void show_entry_name(struct tree_entry_list *e)
++{
++ /* This is yucky. The root level is there for
++ * our convenience but we really want to do a
++ * forest.
++ */
++ if (e->parent && e->parent != &root_entry) {
++ show_entry_name(e->parent);
++ putchar('/');
+ }
++ printf("%s", e->name);
++}
+
+- return -1;
+-}
++static const char *entry_type(struct tree_entry_list *e)
++{
++ return (e->directory ? "tree" : "blob");
++}
+
+-/*
+- * match may be NULL, or a *sorted* list of paths
+- */
+-static void list_recursive(void *buffer,
+- const char *type,
+- unsigned long size,
+- struct path_prefix *prefix,
+- char **match, int matches)
+-{
+- struct path_prefix this_prefix;
+- this_prefix.prev = prefix;
+-
+- if (strcmp(type, "tree"))
+- die("expected a 'tree' node");
+-
+- if (matches)
+- recursive = 1;
+-
+- while (size) {
+- int namelen = strlen(buffer)+1;
+- void *eltbuf = NULL;
+- char elttype[20];
+- unsigned long eltsize;
+- unsigned char *sha1 = buffer + namelen;
+- char *path = strchr(buffer, ' ') + 1;
+- unsigned int mode;
+- const char *matched = NULL;
+- int mtype = -1;
+- int mindex;
+-
+- if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
+- die("corrupt 'tree' file");
+- buffer = sha1 + 20;
+- size -= namelen + 20;
+-
+- this_prefix.name = path;
+- for ( mindex = 0; mindex < matches; mindex++) {
+- mtype = pathcmp(match[mindex],&this_prefix);
+- if (mtype >= 0) {
+- matched = match[mindex];
+- break;
+- }
+- }
++static const char *entry_hex(struct tree_entry_list *e)
++{
++ return sha1_to_hex(e->directory
++ ? e->item.tree->object.sha1
++ : e->item.blob->object.sha1);
++}
+
+- /*
+- * If we're not matching, or if this is an exact match,
+- * print out the info
+- */
+- if (!matches || (matched != NULL && mtype == 0)) {
+- printf("%06o %s %s\t", mode,
+- S_ISDIR(mode) ? "tree" : "blob",
+- sha1_to_hex(sha1));
+- print_path_prefix(&this_prefix);
+- putchar(line_termination);
+- }
++/* forward declaration for mutually recursive routines */
++static int show_entry(struct tree_entry_list *, int);
+
+- if (! recursive || ! S_ISDIR(mode))
+- continue;
++static int show_children(struct tree_entry_list *e, int level)
++{
++ if (prepare_children(e))
++ die("internal error: ls-tree show_children called with non tree");
++ e = e->item.tree->entries;
++ while (e) {
++ show_entry(e, level);
++ e = e->next;
++ }
++ return 0;
++}
+
+- if (matches && ! matched)
+- continue;
++static int show_entry(struct tree_entry_list *e, int level)
++{
++ int err = 0;
+
+- if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) {
+- error("cannot read %s", sha1_to_hex(sha1));
+- continue;
+- }
++ if (e != &root_entry) {
++ printf("%06o %s %s ", e->mode, entry_type(e),
++ entry_hex(e));
++ show_entry_name(e);
++ putchar(line_termination);
++ }
+
+- /* If this is an exact directory match, we may have
+- * directory files following this path. Match on them.
+- * Otherwise, we're at a pach subcomponent, and we need
+- * to try to match again.
++ if (e->directory) {
++ /* If this is a directory, we have the following cases:
++ * (1) This is the top-level request (explicit path from the
++ * command line, or "root" if there is no command line).
++ * a. Without any flag. We show direct children. We do not
++ * recurse into them.
++ * b. With -r. We do recurse into children.
++ * c. With -d. We do not recurse into children.
++ * (2) We came here because our caller is either (1-a) or
++ * (1-b).
++ * a. Without any flag. We do not show our children (which
++ * are grandchildren for the original request).
++ * b. With -r. We continue to recurse into our children.
++ * c. With -d. We should not have come here to begin with.
+ */
+- if (mtype == 0)
+- mindex++;
+-
+- list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex);
+- free(eltbuf);
++ if (level == 0 && !(ls_options & LS_TREE_ONLY))
++ /* case (1)-a and (1)-b */
++ err = err | show_children(e, level+1);
++ else if (level && ls_options & LS_RECURSIVE)
++ /* case (2)-b */
++ err = err | show_children(e, level+1);
+ }
++ return err;
+ }
+
+-static int qcmp(const void *a, const void *b)
++static int list_one(const char *path, const char *path_end)
+ {
+- return strcmp(*(char **)a, *(char **)b);
++ int err = 0;
++ struct tree_entry_list *e = find_entry(path, path_end);
++ if (!e) {
++ /* traditionally ls-tree does not complain about
++ * missing path. We may change this later to match
++ * what "/bin/ls -a" does, which is to complain.
++ */
++ return err;
++ }
++ err = err | show_entry(e, 0);
++ return err;
+ }
+
+-static int list(unsigned char *sha1,char **path)
++static int list(char **path)
+ {
+- void *buffer;
+- unsigned long size;
+- int npaths;
+-
+- for (npaths = 0; path[npaths] != NULL; npaths++)
+- ;
+-
+- qsort(path,npaths,sizeof(char *),qcmp);
+-
+- buffer = read_object_with_reference(sha1, "tree", &size, NULL);
+- if (!buffer)
+- die("unable to read sha1 file");
+- list_recursive(buffer, "tree", size, NULL, path, npaths);
+- free(buffer);
+- return 0;
++ int i;
++ int err = 0;
++ for (i = 0; path[i]; i++) {
++ int len = strlen(path[i]);
++ while (0 <= len && path[i][len] == '/')
++ len--;
++ err = err | list_one(path[i], path[i] + len);
++ }
++ return err;
+ }
+
+-static const char *ls_tree_usage = "git-ls-tree [-r] [-z] <key> [paths...]";
++static const char *ls_tree_usage =
++ "git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
+
+ int main(int argc, char **argv)
+ {
++ static char *path0[] = { "", NULL };
++ char **path;
+ unsigned char sha1[20];
+
+ while (1 < argc && argv[1][0] == '-') {
+@@ -194,7 +223,10 @@ int main(int argc, char **argv)
+ line_termination = 0;
+ break;
+ case 'r':
+- recursive = 1;
++ ls_options |= LS_RECURSIVE;
++ break;
++ case 'd':
++ ls_options |= LS_TREE_ONLY;
+ break;
+ default:
+ usage(ls_tree_usage);
+@@ -206,7 +238,10 @@ int main(int argc, char **argv)
+ usage(ls_tree_usage);
+ if (get_sha1(argv[1], sha1) < 0)
+ usage(ls_tree_usage);
+- if (list(sha1, &argv[2]) < 0)
++
++ path = (argc == 2) ? path0 : (argv + 2);
++ prepare_root(sha1);
++ if (list(path) < 0)
+ die("list failed");
+ return 0;
+ }
+diff a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
+--- a/t/t3100-ls-tree-restrict.sh
++++ b/t/t3100-ls-tree-restrict.sh
+@@ -74,8 +74,8 @@ test_expect_success \
+ 'ls-tree filtered' \
+ 'git-ls-tree $tree path1 path0 >current &&
+ cat >expected <<\EOF &&
+-100644 blob X path0
+ 120000 blob X path1
++100644 blob X path0
+ EOF
+ test_output'
+
+@@ -85,7 +85,6 @@ test_expect_success \
+ cat >expected <<\EOF &&
+ 040000 tree X path2
+ 040000 tree X path2/baz
+-100644 blob X path2/baz/b
+ 120000 blob X path2/bazbo
+ 100644 blob X path2/foo
+ EOF
+diff a/tree.c b/tree.c
+--- a/tree.c
++++ b/tree.c
+@@ -133,7 +133,7 @@ int parse_tree_buffer(struct tree *item,
+ }
+ if (obj)
+ add_ref(&item->object, obj);
+-
++ entry->parent = NULL; /* needs to be filled by the user */
+ *list_p = entry;
+ list_p = &entry->next;
+ }
+diff a/tree.h b/tree.h
+--- a/tree.h
++++ b/tree.h
+@@ -16,6 +16,7 @@ struct tree_entry_list {
+ struct tree *tree;
+ struct blob *blob;
+ } item;
++ struct tree_entry_list *parent;
+ };
+
+ struct tree {
diff --git a/t/t4101-apply-nonl.sh b/t/t4101-apply-nonl.sh
new file mode 100755
index 0000000000..26b131d0d5
--- /dev/null
+++ b/t/t4101-apply-nonl.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply should handle files with incomplete lines.
+
+'
+. ./test-lib.sh
+
+# setup
+
+(echo a; echo b) >frotz.0
+(echo a; echo b; echo c) >frotz.1
+(echo a; echo b | tr -d '\012') >frotz.2
+(echo a; echo c; echo b | tr -d '\012') >frotz.3
+
+for i in 0 1 2 3
+do
+ for j in 0 1 2 3
+ do
+ test $i -eq $j && continue
+ diff -u frotz.$i frotz.$j |
+ sed -e '
+ /^---/s|.*|--- a/frotz|
+ /^+++/s|.*|+++ b/frotz|' >diff.$i-$j
+ cat frotz.$i >frotz
+ test_expect_success \
+ "apply diff between $i and $j" \
+ "git-apply <diff.$i-$j && diff frotz.$j frotz"
+ done
+done
+
+test_done
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
new file mode 100755
index 0000000000..fbb508d389
--- /dev/null
+++ b/t/t4102-apply-rename.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply handling copy/rename patch.
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat >test-patch <<\EOF
+diff --git a/foo b/bar
+similarity index 47%
+copy from foo
+copy to bar
+--- a/foo
++++ b/bar
+@@ -1 +1 @@
+-This is foo
++This is bar
+EOF
+
+echo 'This is foo' >foo
+chmod +x foo
+
+test_expect_success setup \
+ 'git-update-index --add foo'
+
+test_expect_success apply \
+ 'git-apply --index --stat --summary --apply test-patch'
+
+if [ "$(git repo-config --get core.filemode)" = false ]
+then
+ say 'filemode disabled on the filesystem'
+else
+ test_expect_success validate \
+ 'test -f bar && ls -l bar | grep "^-..x......"'
+fi
+
+test_done
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
new file mode 100755
index 0000000000..00bd8b15c6
--- /dev/null
+++ b/t/t4103-apply-binary.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply handling binary patches
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat >file1 <<EOF
+A quick brown fox jumps over the lazy dog.
+A tiny little penguin runs around in circles.
+There is a flag with Linux written on it.
+A slow black-and-white panda just sits there,
+munching on his bamboo.
+EOF
+cat file1 >file2
+cat file1 >file4
+
+git-update-index --add --remove file1 file2 file4
+git-commit -m 'Initial Version' 2>/dev/null
+
+git-checkout -b binary
+tr 'x' '\0' <file1 >file3
+cat file3 >file4
+git-add file2
+tr '\0' 'v' <file3 >file1
+rm -f file2
+git-update-index --add --remove file1 file2 file3 file4
+git-commit -m 'Second Version'
+
+git-diff-tree -p master binary >B.diff
+git-diff-tree -p -C master binary >C.diff
+
+git-diff-tree -p --full-index master binary >BF.diff
+git-diff-tree -p --full-index -C master binary >CF.diff
+
+test_expect_success 'stat binary diff -- should not fail.' \
+ 'git-checkout master
+ git-apply --stat --summary B.diff'
+
+test_expect_success 'stat binary diff (copy) -- should not fail.' \
+ 'git-checkout master
+ git-apply --stat --summary C.diff'
+
+test_expect_failure 'check binary diff -- should fail.' \
+ 'git-checkout master
+ git-apply --check B.diff'
+
+test_expect_failure 'check binary diff (copy) -- should fail.' \
+ 'git-checkout master
+ git-apply --check C.diff'
+
+test_expect_failure 'check incomplete binary diff with replacement -- should fail.' \
+ 'git-checkout master
+ git-apply --check --allow-binary-replacement B.diff'
+
+test_expect_failure 'check incomplete binary diff with replacement (copy) -- should fail.' \
+ 'git-checkout master
+ git-apply --check --allow-binary-replacement C.diff'
+
+test_expect_success 'check binary diff with replacement.' \
+ 'git-checkout master
+ git-apply --check --allow-binary-replacement BF.diff'
+
+test_expect_success 'check binary diff with replacement (copy).' \
+ 'git-checkout master
+ git-apply --check --allow-binary-replacement CF.diff'
+
+# Now we start applying them.
+
+do_reset () {
+ rm -f file?
+ git-reset --hard
+ git-checkout -f master
+}
+
+test_expect_failure 'apply binary diff -- should fail.' \
+ 'do_reset
+ git-apply B.diff'
+
+test_expect_failure 'apply binary diff -- should fail.' \
+ 'do_reset
+ git-apply --index B.diff'
+
+test_expect_failure 'apply binary diff (copy) -- should fail.' \
+ 'do_reset
+ git-apply C.diff'
+
+test_expect_failure 'apply binary diff (copy) -- should fail.' \
+ 'do_reset
+ git-apply --index C.diff'
+
+test_expect_failure 'apply binary diff without replacement -- should fail.' \
+ 'do_reset
+ git-apply BF.diff'
+
+test_expect_failure 'apply binary diff without replacement (copy) -- should fail.' \
+ 'do_reset
+ git-apply CF.diff'
+
+test_expect_success 'apply binary diff.' \
+ 'do_reset
+ git-apply --allow-binary-replacement --index BF.diff &&
+ test -z "$(git-diff --name-status binary)"'
+
+test_expect_success 'apply binary diff (copy).' \
+ 'do_reset
+ git-apply --allow-binary-replacement --index CF.diff &&
+ test -z "$(git-diff --name-status binary)"'
+
+test_done
diff --git a/t/t4109-apply-multifrag.sh b/t/t4109-apply-multifrag.sh
new file mode 100755
index 0000000000..5988e1ae4c
--- /dev/null
+++ b/t/t4109-apply-multifrag.sh
@@ -0,0 +1,176 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+# Copyright (c) 2005 Robert Fitzsimons
+#
+
+test_description='git-apply test patches with multiple fragments.
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat > patch1.patch <<\EOF
+diff --git a/main.c b/main.c
+new file mode 100644
+--- /dev/null
++++ b/main.c
+@@ -0,0 +1,23 @@
++#include <stdio.h>
++
++int func(int num);
++void print_int(int num);
++
++int main() {
++ int i;
++
++ for (i = 0; i < 10; i++) {
++ print_int(func(i));
++ }
++
++ return 0;
++}
++
++int func(int num) {
++ return num * num;
++}
++
++void print_int(int num) {
++ printf("%d", num);
++}
++
+EOF
+cat > patch2.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -1,7 +1,9 @@
++#include <stdlib.h>
+ #include <stdio.h>
+
+ int func(int num);
+ void print_int(int num);
++void print_ln();
+
+ int main() {
+ int i;
+@@ -10,6 +12,8 @@
+ print_int(func(i));
+ }
+
++ print_ln();
++
+ return 0;
+ }
+
+@@ -21,3 +25,7 @@
+ printf("%d", num);
+ }
+
++void print_ln() {
++ printf("\n");
++}
++
+EOF
+cat > patch3.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -1,9 +1,7 @@
+-#include <stdlib.h>
+ #include <stdio.h>
+
+ int func(int num);
+ void print_int(int num);
+-void print_ln();
+
+ int main() {
+ int i;
+@@ -12,8 +10,6 @@
+ print_int(func(i));
+ }
+
+- print_ln();
+-
+ return 0;
+ }
+
+@@ -25,7 +21,3 @@
+ printf("%d", num);
+ }
+
+-void print_ln() {
+- printf("\n");
+-}
+-
+EOF
+cat > patch4.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -1,13 +1,14 @@
+ #include <stdio.h>
+
+ int func(int num);
+-void print_int(int num);
++int func2(int num);
+
+ int main() {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+- print_int(func(i));
++ printf("%d", func(i));
++ printf("%d", func3(i));
+ }
+
+ return 0;
+@@ -17,7 +18,7 @@
+ return num * num;
+ }
+
+-void print_int(int num) {
+- printf("%d", num);
++int func2(int num) {
++ return num * num * num;
+ }
+
+EOF
+
+test_expect_success "S = git-apply (1)" \
+ 'git-apply patch1.patch patch2.patch'
+mv main.c main.c.git
+
+test_expect_success "S = patch (1)" \
+ 'cat patch1.patch patch2.patch | patch -p1'
+
+test_expect_success "S = cmp (1)" \
+ 'cmp main.c.git main.c'
+
+rm -f main.c main.c.git
+
+test_expect_success "S = git-apply (2)" \
+ 'git-apply patch1.patch patch2.patch patch3.patch'
+mv main.c main.c.git
+
+test_expect_success "S = patch (2)" \
+ 'cat patch1.patch patch2.patch patch3.patch | patch -p1'
+
+test_expect_success "S = cmp (2)" \
+ 'cmp main.c.git main.c'
+
+rm -f main.c main.c.git
+
+test_expect_success "S = git-apply (3)" \
+ 'git-apply patch1.patch patch4.patch'
+mv main.c main.c.git
+
+test_expect_success "S = patch (3)" \
+ 'cat patch1.patch patch4.patch | patch -p1'
+
+test_expect_success "S = cmp (3)" \
+ 'cmp main.c.git main.c'
+
+test_done
+
diff --git a/t/t4110-apply-scan.sh b/t/t4110-apply-scan.sh
new file mode 100755
index 0000000000..005f744816
--- /dev/null
+++ b/t/t4110-apply-scan.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+# Copyright (c) 2005 Robert Fitzsimons
+#
+
+test_description='git-apply test for patches which require scanning forwards and backwards.
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat > patch1.patch <<\EOF
+diff --git a/new.txt b/new.txt
+new file mode 100644
+--- /dev/null
++++ b/new.txt
+@@ -0,0 +1,12 @@
++a1
++a11
++a111
++a1111
++b1
++b11
++b111
++b1111
++c1
++c11
++c111
++c1111
+EOF
+cat > patch2.patch <<\EOF
+diff --git a/new.txt b/new.txt
+--- a/new.txt
++++ b/new.txt
+@@ -1,7 +1,3 @@
+-a1
+-a11
+-a111
+-a1111
+ b1
+ b11
+ b111
+EOF
+cat > patch3.patch <<\EOF
+diff --git a/new.txt b/new.txt
+--- a/new.txt
++++ b/new.txt
+@@ -6,6 +6,10 @@
+ b11
+ b111
+ b1111
++b2
++b22
++b222
++b2222
+ c1
+ c11
+ c111
+EOF
+cat > patch4.patch <<\EOF
+diff --git a/new.txt b/new.txt
+--- a/new.txt
++++ b/new.txt
+@@ -1,3 +1,7 @@
++a1
++a11
++a111
++a1111
+ b1
+ b11
+ b111
+EOF
+cat > patch5.patch <<\EOF
+diff --git a/new.txt b/new.txt
+--- a/new.txt
++++ b/new.txt
+@@ -10,3 +10,7 @@
+ c11
+ c111
+ c1111
++c2
++c22
++c222
++c2222
+EOF
+
+test_expect_success "S = git-apply scan" \
+ 'git-apply patch1.patch patch2.patch patch3.patch patch4.patch patch5.patch'
+mv new.txt apply.txt
+
+test_expect_success "S = patch scan" \
+ 'cat patch1.patch patch2.patch patch3.patch patch4.patch patch5.patch | patch'
+mv new.txt patch.txt
+
+test_expect_success "S = cmp" \
+ 'cmp apply.txt patch.txt'
+
+test_done
+
diff --git a/t/t4112-apply-renames.sh b/t/t4112-apply-renames.sh
new file mode 100755
index 0000000000..a06f6956d5
--- /dev/null
+++ b/t/t4112-apply-renames.sh
@@ -0,0 +1,148 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply should not get confused with rename/copy.
+
+'
+
+. ./test-lib.sh
+
+# setup
+
+mkdir -p include/arch/x86_64/klibc klibc/arch/x86_64/include/klibc
+
+cat >include/arch/x86_64/klibc/archsetjmp.h <<\EOF
+/*
+ * arch/x86_64/include/klibc/archsetjmp.h
+ */
+
+#ifndef _KLIBC_ARCHSETJMP_H
+#define _KLIBC_ARCHSETJMP_H
+
+struct __jmp_buf {
+ unsigned long __rbx;
+ unsigned long __rsp;
+ unsigned long __rbp;
+ unsigned long __r12;
+ unsigned long __r13;
+ unsigned long __r14;
+ unsigned long __r15;
+ unsigned long __rip;
+};
+
+typedef struct __jmp_buf jmp_buf[1];
+
+#endif /* _SETJMP_H */
+EOF
+
+cat >klibc/arch/x86_64/include/klibc/archsetjmp.h <<\EOF
+/*
+ * arch/x86_64/include/klibc/archsetjmp.h
+ */
+
+#ifndef _KLIBC_ARCHSETJMP_H
+#define _KLIBC_ARCHSETJMP_H
+
+struct __jmp_buf {
+ unsigned long __rbx;
+ unsigned long __rsp;
+ unsigned long __rbp;
+ unsigned long __r12;
+ unsigned long __r13;
+ unsigned long __r14;
+ unsigned long __r15;
+ unsigned long __rip;
+};
+
+typedef struct __jmp_buf jmp_buf[1];
+
+#endif /* _SETJMP_H */
+EOF
+
+cat >patch <<\EOF
+diff --git a/klibc/arch/x86_64/include/klibc/archsetjmp.h b/include/arch/cris/klibc/archsetjmp.h
+similarity index 76%
+copy from klibc/arch/x86_64/include/klibc/archsetjmp.h
+copy to include/arch/cris/klibc/archsetjmp.h
+--- a/klibc/arch/x86_64/include/klibc/archsetjmp.h
++++ b/include/arch/cris/klibc/archsetjmp.h
+@@ -1,21 +1,24 @@
+ /*
+- * arch/x86_64/include/klibc/archsetjmp.h
++ * arch/cris/include/klibc/archsetjmp.h
+ */
+
+ #ifndef _KLIBC_ARCHSETJMP_H
+ #define _KLIBC_ARCHSETJMP_H
+
+ struct __jmp_buf {
+- unsigned long __rbx;
+- unsigned long __rsp;
+- unsigned long __rbp;
+- unsigned long __r12;
+- unsigned long __r13;
+- unsigned long __r14;
+- unsigned long __r15;
+- unsigned long __rip;
++ unsigned long __r0;
++ unsigned long __r1;
++ unsigned long __r2;
++ unsigned long __r3;
++ unsigned long __r4;
++ unsigned long __r5;
++ unsigned long __r6;
++ unsigned long __r7;
++ unsigned long __r8;
++ unsigned long __sp;
++ unsigned long __srp;
+ };
+
+ typedef struct __jmp_buf jmp_buf[1];
+
+-#endif /* _SETJMP_H */
++#endif /* _KLIBC_ARCHSETJMP_H */
+diff --git a/klibc/arch/x86_64/include/klibc/archsetjmp.h b/include/arch/m32r/klibc/archsetjmp.h
+similarity index 66%
+rename from klibc/arch/x86_64/include/klibc/archsetjmp.h
+rename to include/arch/m32r/klibc/archsetjmp.h
+--- a/klibc/arch/x86_64/include/klibc/archsetjmp.h
++++ b/include/arch/m32r/klibc/archsetjmp.h
+@@ -1,21 +1,21 @@
+ /*
+- * arch/x86_64/include/klibc/archsetjmp.h
++ * arch/m32r/include/klibc/archsetjmp.h
+ */
+
+ #ifndef _KLIBC_ARCHSETJMP_H
+ #define _KLIBC_ARCHSETJMP_H
+
+ struct __jmp_buf {
+- unsigned long __rbx;
+- unsigned long __rsp;
+- unsigned long __rbp;
++ unsigned long __r8;
++ unsigned long __r9;
++ unsigned long __r10;
++ unsigned long __r11;
+ unsigned long __r12;
+ unsigned long __r13;
+ unsigned long __r14;
+ unsigned long __r15;
+- unsigned long __rip;
+ };
+
+ typedef struct __jmp_buf jmp_buf[1];
+
+-#endif /* _SETJMP_H */
++#endif /* _KLIBC_ARCHSETJMP_H */
+EOF
+
+find include klibc -type f -print | xargs git-update-index --add --
+
+test_expect_success 'check rename/copy patch' 'git-apply --check patch'
+
+test_expect_success 'apply rename/copy patch' 'git-apply --index patch'
+
+test_done
diff --git a/t/t4113-apply-ending.sh b/t/t4113-apply-ending.sh
new file mode 100755
index 0000000000..7fd0cf62ec
--- /dev/null
+++ b/t/t4113-apply-ending.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Catalin Marinas
+#
+
+test_description='git-apply trying to add an ending line.
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat >test-patch <<\EOF
+diff --git a/file b/file
+--- a/file
++++ b/file
+@@ -1,2 +1,3 @@
+ a
+ b
++c
+EOF
+
+echo 'a' >file
+echo 'b' >>file
+echo 'c' >>file
+
+test_expect_success setup \
+ 'git-update-index --add file'
+
+# test
+
+test_expect_failure 'apply at the end' \
+ 'git-apply --index test-patch'
+
+cat >test-patch <<\EOF
+diff a/file b/file
+--- a/file
++++ b/file
+@@ -1,2 +1,3 @@
++a
+ b
+ c
+EOF
+
+echo >file 'a
+b
+c'
+git-update-index file
+
+test_expect_failure 'apply at the beginning' \
+ 'git-apply --index test-patch'
+
+test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
new file mode 100755
index 0000000000..278eb66701
--- /dev/null
+++ b/t/t5000-tar-tree.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+#
+# Copyright (C) 2005 Rene Scharfe
+#
+
+test_description='git-tar-tree and git-get-tar-commit-id test
+
+This test covers the topics of file contents, commit date handling and
+commit id embedding:
+
+ The contents of the repository is compared to the extracted tar
+ archive. The repository contains simple text files, symlinks and a
+ binary file (/bin/sh). Only pathes shorter than 99 characters are
+ used.
+
+ git-tar-tree applies the commit date to every file in the archive it
+ creates. The test sets the commit date to a specific value and checks
+ if the tar archive contains that value.
+
+ When giving git-tar-tree a commit id (in contrast to a tree id) it
+ embeds this commit id into the tar archive as a comment. The test
+ checks the ability of git-get-tar-commit-id to figure it out from the
+ tar file.
+
+'
+
+. ./test-lib.sh
+TAR=${TAR:-tar}
+
+test_expect_success \
+ 'populate workdir' \
+ 'mkdir a b c &&
+ echo simple textfile >a/a &&
+ mkdir a/bin &&
+ cp /bin/sh a/bin &&
+ ln -s a a/l1 &&
+ (p=long_path_to_a_file && cd a &&
+ for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
+ echo text >file_with_long_path) &&
+ (cd a && find .) | sort >a.lst'
+
+test_expect_success \
+ 'add files to repository' \
+ 'find a -type f | xargs git-update-index --add &&
+ find a -type l | xargs git-update-index --add &&
+ treeid=`git-write-tree` &&
+ echo $treeid >treeid &&
+ git-update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
+ git-commit-tree $treeid </dev/null)'
+
+test_expect_success \
+ 'git-tar-tree' \
+ 'git-tar-tree HEAD >b.tar'
+
+test_expect_success \
+ 'validate file modification time' \
+ 'TZ=GMT $TAR tvf b.tar a/a |
+ awk \{print\ \$4,\ \(length\(\$5\)\<7\)\ ?\ \$5\":00\"\ :\ \$5\} \
+ >b.mtime &&
+ echo "2005-05-27 22:00:00" >expected.mtime &&
+ diff expected.mtime b.mtime'
+
+test_expect_success \
+ 'git-get-tar-commit-id' \
+ 'git-get-tar-commit-id <b.tar >b.commitid &&
+ diff .git/$(git-symbolic-ref HEAD) b.commitid'
+
+test_expect_success \
+ 'extract tar archive' \
+ '(cd b && $TAR xf -) <b.tar'
+
+test_expect_success \
+ 'validate filenames' \
+ '(cd b/a && find .) | sort >b.lst &&
+ diff a.lst b.lst'
+
+test_expect_success \
+ 'validate file contents' \
+ 'diff -r a b/a'
+
+test_expect_success \
+ 'git-tar-tree with prefix' \
+ 'git-tar-tree HEAD prefix >c.tar'
+
+test_expect_success \
+ 'extract tar archive with prefix' \
+ '(cd c && $TAR xf -) <c.tar'
+
+test_expect_success \
+ 'validate filenames with prefix' \
+ '(cd c/prefix/a && find .) | sort >c.lst &&
+ diff a.lst c.lst'
+
+test_expect_success \
+ 'validate file contents with prefix' \
+ 'diff -r a c/prefix/a'
+
+test_done
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
new file mode 100755
index 0000000000..de45ac4e0f
--- /dev/null
+++ b/t/t5300-pack-object.sh
@@ -0,0 +1,199 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-pack-object
+
+'
+. ./test-lib.sh
+
+TRASH=`pwd`
+
+test_expect_success \
+ 'setup' \
+ 'rm -f .git/index*
+ for i in a b c
+ do
+ dd if=/dev/zero bs=4k count=1 | tr "\\0" $i >$i &&
+ git-update-index --add $i || return 1
+ done &&
+ cat c >d && echo foo >>d && git-update-index --add d &&
+ tree=`git-write-tree` &&
+ commit=`git-commit-tree $tree </dev/null` && {
+ echo $tree &&
+ echo $commit &&
+ git-ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ } >obj-list && {
+ git-diff-tree --root -p $commit &&
+ while read object
+ do
+ t=`git-cat-file -t $object` &&
+ git-cat-file $t $object || return 1
+ done <obj-list
+ } >expect'
+
+test_expect_success \
+ 'pack without delta' \
+ 'packname_1=$(git-pack-objects --window=0 test-1 <obj-list)'
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+ 'unpack without delta' \
+ "GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ git-init-db &&
+ git-unpack-objects -n <test-1-${packname_1}.pack &&
+ git-unpack-objects <test-1-${packname_1}.pack"
+
+unset GIT_OBJECT_DIRECTORY
+cd "$TRASH/.git2"
+
+test_expect_success \
+ 'check unpack without delta' \
+ '(cd ../.git && find objects -type f -print) |
+ while read path
+ do
+ cmp $path ../.git/$path || {
+ echo $path differs.
+ return 1
+ }
+ done'
+cd "$TRASH"
+
+test_expect_success \
+ 'pack with delta' \
+ 'pwd &&
+ packname_2=$(git-pack-objects test-2 <obj-list)'
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+ 'unpack with delta' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ git-init-db &&
+ git-unpack-objects -n <test-2-${packname_2}.pack &&
+ git-unpack-objects <test-2-${packname_2}.pack'
+
+unset GIT_OBJECT_DIRECTORY
+cd "$TRASH/.git2"
+test_expect_success \
+ 'check unpack with delta' \
+ '(cd ../.git && find objects -type f -print) |
+ while read path
+ do
+ cmp $path ../.git/$path || {
+ echo $path differs.
+ return 1
+ }
+ done'
+cd "$TRASH"
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+ 'use packed objects' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ git-init-db &&
+ cp test-1-${packname_1}.pack test-1-${packname_1}.idx .git2/objects/pack && {
+ git-diff-tree --root -p $commit &&
+ while read object
+ do
+ t=`git-cat-file -t $object` &&
+ git-cat-file $t $object || return 1
+ done <obj-list
+ } >current &&
+ diff expect current'
+
+
+test_expect_success \
+ 'use packed deltified objects' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ rm -f .git2/objects/pack/test-?.idx &&
+ cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && {
+ git-diff-tree --root -p $commit &&
+ while read object
+ do
+ t=`git-cat-file -t $object` &&
+ git-cat-file $t $object || return 1
+ done <obj-list
+ } >current &&
+ diff expect current'
+
+unset GIT_OBJECT_DIRECTORY
+
+test_expect_success \
+ 'verify pack' \
+ 'git-verify-pack test-1-${packname_1}.idx test-2-${packname_2}.idx'
+
+test_expect_success \
+ 'corrupt a pack and see if verify catches' \
+ 'cp test-1-${packname_1}.idx test-3.idx &&
+ cp test-2-${packname_2}.pack test-3.pack &&
+ if git-verify-pack test-3.idx
+ then false
+ else :;
+ fi &&
+
+ : PACK_SIGNATURE &&
+ cp test-1-${packname_1}.pack test-3.pack &&
+ dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
+ if git-verify-pack test-3.idx
+ then false
+ else :;
+ fi &&
+
+ : PACK_VERSION &&
+ cp test-1-${packname_1}.pack test-3.pack &&
+ dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
+ if git-verify-pack test-3.idx
+ then false
+ else :;
+ fi &&
+
+ : TYPE/SIZE byte of the first packed object data &&
+ cp test-1-${packname_1}.pack test-3.pack &&
+ dd if=/dev/zero of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
+ if git-verify-pack test-3.idx
+ then false
+ else :;
+ fi &&
+
+ : sum of the index file itself &&
+ l=`wc -c <test-3.idx` &&
+ l=`expr $l - 20` &&
+ cp test-1-${packname_1}.pack test-3.pack &&
+ dd if=/dev/zero of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
+ if git-verify-pack test-3.pack
+ then false
+ else :;
+ fi &&
+
+ :'
+
+test_expect_success \
+ 'build pack index for an existing pack' \
+ 'cp test-1-${packname_1}.pack test-3.pack &&
+ git-index-pack -o tmp.idx test-3.pack &&
+ cmp tmp.idx test-1-${packname_1}.idx &&
+
+ git-index-pack test-3.pack &&
+ cmp test-3.idx test-1-${packname_1}.idx &&
+
+ cp test-2-${packname_2}.pack test-3.pack &&
+ git-index-pack -o tmp.idx test-2-${packname_2}.pack &&
+ cmp tmp.idx test-2-${packname_2}.idx &&
+
+ git-index-pack test-3.pack &&
+ cmp test-3.idx test-2-${packname_2}.idx &&
+
+ :'
+
+test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
new file mode 100755
index 0000000000..f3694ac3c7
--- /dev/null
+++ b/t/t5400-send-pack.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='See why rewinding head breaks send-pack
+
+'
+. ./test-lib.sh
+
+touch cpio-test
+test_expect_success 'working cpio' 'echo cpio-test | cpio -o > /dev/null'
+
+cnt='1'
+test_expect_success setup '
+ tree=$(git-write-tree) &&
+ commit=$(echo "Commit #0" | git-commit-tree $tree) &&
+ zero=$commit &&
+ parent=$zero &&
+ for i in $cnt
+ do
+ sleep 1 &&
+ commit=$(echo "Commit #$i" | git-commit-tree $tree -p $parent) &&
+ parent=$commit || return 1
+ done &&
+ git-update-ref HEAD "$commit" &&
+ git-clone -l ./. victim &&
+ cd victim &&
+ git-log &&
+ cd .. &&
+ git-update-ref HEAD "$zero" &&
+ parent=$zero &&
+ for i in $cnt
+ do
+ sleep 1 &&
+ commit=$(echo "Rebase #$i" | git-commit-tree $tree -p $parent) &&
+ parent=$commit || return 1
+ done &&
+ git-update-ref HEAD "$commit" &&
+ echo Rebase &&
+ git-log'
+
+test_expect_success \
+ 'pushing rewound head should not barf but require --force' '
+ # should not fail but refuse to update.
+ if git-send-pack ./victim/.git/ master
+ then
+ # now it should fail with Pasky patch
+ echo >&2 Gaah, it should have failed.
+ false
+ else
+ echo >&2 Thanks, it correctly failed.
+ true
+ fi &&
+ if cmp victim/.git/refs/heads/master .git/refs/heads/master
+ then
+ # should have been left as it was!
+ false
+ else
+ true
+ fi &&
+ # this should update
+ git-send-pack --force ./victim/.git/ master &&
+ cmp victim/.git/refs/heads/master .git/refs/heads/master
+'
+
+test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
new file mode 100755
index 0000000000..f7625a6f46
--- /dev/null
+++ b/t/t5500-fetch-pack.sh
@@ -0,0 +1,131 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Testing multi_ack pack fetching
+
+'
+. ./test-lib.sh
+
+# Test fetch-pack/upload-pack pair.
+
+# Some convenience functions
+
+add () {
+ name=$1
+ text="$@"
+ branch=`echo $name | sed -e 's/^\(.\).*$/\1/'`
+ parents=""
+
+ shift
+ while test $1; do
+ parents="$parents -p $1"
+ shift
+ done
+
+ echo "$text" > test.txt
+ git-update-index --add test.txt
+ tree=$(git-write-tree)
+ # make sure timestamps are in correct order
+ sec=$(($sec+1))
+ commit=$(echo "$text" | GIT_AUTHOR_DATE=$sec \
+ git-commit-tree $tree $parents 2>>log2.txt)
+ export $name=$commit
+ echo $commit > .git/refs/heads/$branch
+ eval ${branch}TIP=$commit
+}
+
+count_objects () {
+ ls .git/objects/??/* 2>>log2.txt | wc -l | tr -d " "
+}
+
+test_expect_object_count () {
+ message=$1
+ count=$2
+
+ output="$(count_objects)"
+ test_expect_success \
+ "new object count $message" \
+ "test $count = $output"
+}
+
+pull_to_client () {
+ number=$1
+ heads=$2
+ count=$3
+ no_strict_count_check=$4
+
+ cd client
+ test_expect_success "$number pull" \
+ "git-fetch-pack -k -v .. $heads"
+ case "$heads" in *A*) echo $ATIP > .git/refs/heads/A;; esac
+ case "$heads" in *B*) echo $BTIP > .git/refs/heads/B;; esac
+ git-symbolic-ref HEAD refs/heads/`echo $heads | sed -e 's/^\(.\).*$/\1/'`
+
+ test_expect_success "fsck" 'git-fsck-objects --full > fsck.txt 2>&1'
+
+ test_expect_success 'check downloaded results' \
+ 'mv .git/objects/pack/pack-* . &&
+ p=`ls -1 pack-*.pack` &&
+ git-unpack-objects <$p &&
+ git-fsck-objects --full'
+
+ test_expect_success "new object count after $number pull" \
+ 'idx=`echo pack-*.idx` &&
+ pack_count=`git-show-index <$idx | wc -l` &&
+ test $pack_count = $count'
+ test -z "$pack_count" && pack_count=0
+ if [ -z "$no_strict_count_check" ]; then
+ test_expect_success "minimal count" "test $count = $pack_count"
+ else
+ test $count != $pack_count && \
+ echo "WARNING: $pack_count objects transmitted, only $count of which were needed"
+ fi
+ rm -f pack-*
+ cd ..
+}
+
+# Here begins the actual testing
+
+# A1 - ... - A20 - A21
+# \
+# B1 - B2 - .. - B70
+
+# client pulls A20, B1. Then tracks only B. Then pulls A.
+
+(
+ mkdir client &&
+ cd client &&
+ git-init-db 2>> log2.txt
+)
+
+add A1
+
+prev=1; cur=2; while [ $cur -le 10 ]; do
+ add A$cur $(eval echo \$A$prev)
+ prev=$cur
+ cur=$(($cur+1))
+done
+
+add B1 $A1
+
+echo $ATIP > .git/refs/heads/A
+echo $BTIP > .git/refs/heads/B
+git-symbolic-ref HEAD refs/heads/B
+
+pull_to_client 1st "B A" $((11*3))
+
+add A11 $A10
+
+prev=1; cur=2; while [ $cur -le 65 ]; do
+ add B$cur $(eval echo \$B$prev)
+ prev=$cur
+ cur=$(($cur+1))
+done
+
+pull_to_client 2nd "B" $((64*3))
+
+pull_to_client 3rd "A" $((1*3)) # old fails
+
+test_done
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
new file mode 100755
index 0000000000..0c6a363be9
--- /dev/null
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Copyright (C) 2006 Carl D. Worth <cworth@cworth.org>
+#
+
+test_description='test git-clone to cleanup after failure
+
+This test covers the fact that if git-clone fails, it should remove
+the directory it created, to avoid the user having to manually
+remove the directory before attempting a clone again.'
+
+. ./test-lib.sh
+
+test_expect_failure \
+ 'clone of non-existent source should fail' \
+ 'git-clone foo bar'
+
+test_expect_failure \
+ 'failed clone should not leave a directory' \
+ 'cd bar'
+
+# Need a repo to clone
+test_create_repo foo
+
+# clone doesn't like it if there is no HEAD. Is that a bug?
+(cd foo && touch file && git add file && git commit -m 'add file' >/dev/null 2>&1)
+
+test_expect_success \
+ 'clone should work now that source exists' \
+ 'git-clone foo bar'
+
+test_expect_success \
+ 'successfull clone must leave the directory' \
+ 'cd bar'
+
+test_done
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
new file mode 100755
index 0000000000..916ee15ba1
--- /dev/null
+++ b/t/t5700-clone-reference.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
+#
+
+test_description='test clone --reference'
+. ./test-lib.sh
+
+base_dir=`pwd`
+
+test_expect_success 'preparing first repository' \
+'test_create_repo A && cd A &&
+echo first > file1 &&
+git add file1 &&
+git commit -m initial'
+
+cd "$base_dir"
+
+test_expect_success 'preparing second repository' \
+'git clone A B && cd B &&
+echo second > file2 &&
+git add file2 &&
+git commit -m addition &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'cloning with reference' \
+'git clone -l -s --reference B A C'
+
+cd "$base_dir"
+
+test_expect_success 'existance of info/alternates' \
+'test `wc -l <C/.git/objects/info/alternates` = 2'
+
+cd "$base_dir"
+
+test_expect_success 'pulling from reference' \
+'cd C &&
+git pull ../B'
+
+cd "$base_dir"
+
+test_expect_success 'that reference gets used' \
+'cd C &&
+echo "0 objects, 0 kilobytes" > expected &&
+git count-objects > current &&
+diff expected current'
+
+cd "$base_dir"
+
+test_expect_success 'updating origin' \
+'cd A &&
+echo third > file3 &&
+git add file3 &&
+git commit -m update &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'pulling changes from origin' \
+'cd C &&
+git pull origin'
+
+cd "$base_dir"
+
+# the 2 local objects are commit and tree from the merge
+test_expect_success 'that alternate to origin gets used' \
+'cd C &&
+echo "2 objects" > expected &&
+git count-objects | cut -d, -f1 > current &&
+diff expected current'
+
+cd "$base_dir"
+
+test_done
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
new file mode 100755
index 0000000000..097d037f5d
--- /dev/null
+++ b/t/t5710-info-alternate.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+#
+# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
+#
+
+test_description='test transitive info/alternate entries'
+. ./test-lib.sh
+
+# test that a file is not reachable in the current repository
+# but that it is after creating a info/alternate entry
+reachable_via() {
+ alternate="$1"
+ file="$2"
+ if git cat-file -e "HEAD:$file"; then return 1; fi
+ echo "$alternate" >> .git/objects/info/alternate
+ git cat-file -e "HEAD:$file"
+}
+
+test_valid_repo() {
+ git fsck-objects --full > fsck.log &&
+ test `wc -l < fsck.log` = 0
+}
+
+base_dir=`pwd`
+
+test_expect_success 'preparing first repository' \
+'test_create_repo A && cd A &&
+echo "Hello World" > file1 &&
+git add file1 &&
+git commit -m "Initial commit" file1 &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'preparing second repository' \
+'git clone -l -s A B && cd B &&
+echo "foo bar" > file2 &&
+git add file2 &&
+git commit -m "next commit" file2 &&
+git repack -a -d -l &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'preparing third repository' \
+'git clone -l -s B C && cd C &&
+echo "Goodbye, cruel world" > file3 &&
+git add file3 &&
+git commit -m "one more" file3 &&
+git repack -a -d -l &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_failure 'creating too deep nesting' \
+'git clone -l -s C D &&
+git clone -l -s D E &&
+git clone -l -s E F &&
+git clone -l -s F G &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'validity of third repository' \
+'cd C &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'validity of fourth repository' \
+'cd D &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'breaking of loops' \
+"echo '$base_dir/B/.git/objects' >> '$base_dir'/A/.git/objects/info/alternates&&
+cd C &&
+test_valid_repo"
+
+cd "$base_dir"
+
+test_expect_failure 'that info/alternates is neccessary' \
+'cd C &&
+rm .git/objects/info/alternates &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'that relative alternate is possible for current dir' \
+'cd C &&
+echo "../../../B/.git/objects" > .git/objects/info/alternates &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_failure 'that relative alternate is only possible for current dir' \
+'cd D &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_done
+
diff --git a/t/t6000lib.sh b/t/t6000lib.sh
new file mode 100755
index 0000000000..d40262159b
--- /dev/null
+++ b/t/t6000lib.sh
@@ -0,0 +1,116 @@
+[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+
+:> sed.script
+
+# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
+tag()
+{
+ _tag=$1
+ [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
+ cat .git/refs/tags/$_tag
+}
+
+# Generate a commit using the text specified to make it unique and the tree
+# named by the tag specified.
+unique_commit()
+{
+ _text=$1
+ _tree=$2
+ shift 2
+ echo $_text | git-commit-tree $(tag $_tree) "$@"
+}
+
+# Save the output of a command into the tag specified. Prepend
+# a substitution script for the tag onto the front of sed.script
+save_tag()
+{
+ _tag=$1
+ [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+ shift 1
+ "$@" >.git/refs/tags/$_tag
+
+ echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
+ cat sed.script >> sed.script.tmp
+ rm sed.script
+ mv sed.script.tmp sed.script
+}
+
+# Replace unhelpful sha1 hashses with their symbolic equivalents
+entag()
+{
+ sed -f sed.script
+}
+
+# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
+# tag to a specified value. Restore the original value on return.
+as_author()
+{
+ _author=$1
+ shift 1
+ _save=$GIT_AUTHOR_EMAIL
+
+ export GIT_AUTHOR_EMAIL="$_author"
+ "$@"
+ if test -z "$_save"
+ then
+ unset GIT_AUTHOR_EMAIL
+ else
+ export GIT_AUTHOR_EMAIL="$_save"
+ fi
+}
+
+commit_date()
+{
+ _commit=$1
+ git-cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+}
+
+on_committer_date()
+{
+ _date=$1
+ shift 1
+ export GIT_COMMITTER_DATE="$_date"
+ "$@"
+ unset GIT_COMMITTER_DATE
+}
+
+# Execute a command and suppress any error output.
+hide_error()
+{
+ "$@" 2>/dev/null
+}
+
+check_output()
+{
+ _name=$1
+ shift 1
+ if eval "$*" | entag > $_name.actual
+ then
+ diff $_name.expected $_name.actual
+ else
+ return 1;
+ fi
+}
+
+# Turn a reasonable test description into a reasonable test name.
+# All alphanums translated into -'s which are then compressed and stripped
+# from front and back.
+name_from_description()
+{
+ tr "'" '-' | tr '~`!@#$%^&*()_+={}[]|\;:"<>,/? ' '-' | tr -s '-' | tr '[A-Z]' '[a-z]' | sed "s/^-*//;s/-*\$//"
+}
+
+
+# Execute the test described by the first argument, by eval'ing
+# command line specified in the 2nd argument. Check the status code
+# is zero and that the output matches the stream read from
+# stdin.
+test_output_expect_success()
+{
+ _description=$1
+ _test=$2
+ [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
+ _name=$(echo $_description | name_from_description)
+ cat > $_name.expected
+ test_expect_success "$_description" "check_output $_name \"$_test\""
+}
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
new file mode 100755
index 0000000000..693de9b32d
--- /dev/null
+++ b/t/t6002-rev-list-bisect.sh
@@ -0,0 +1,238 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Jon Seymour
+#
+test_description='Tests git-rev-list --bisect functionality'
+
+. ./test-lib.sh
+. ../t6000lib.sh # t6xxx specific functions
+
+# usage: test_bisection max-diff bisect-option head ^prune...
+#
+# e.g. test_bisection 1 --bisect l1 ^l0
+#
+test_bisection_diff()
+{
+ _max_diff=$1
+ _bisect_option=$2
+ shift 2
+ _bisection=$(git-rev-list $_bisect_option "$@")
+ _list_size=$(git-rev-list "$@" | wc -l)
+ _head=$1
+ shift 1
+ _bisection_size=$(git-rev-list $_bisection "$@" | wc -l)
+ [ -n "$_list_size" -a -n "$_bisection_size" ] ||
+ error "test_bisection_diff failed"
+
+ # Test if bisection size is close to half of list size within
+ # tolerance.
+ #
+ _bisect_err=`expr $_list_size - $_bisection_size \* 2`
+ test "$_bisect_err" -lt 0 && _bisect_err=`expr 0 - $_bisect_err`
+ _bisect_err=`expr $_bisect_err / 2` ; # floor
+
+ test_expect_success \
+ "bisection diff $_bisect_option $_head $* <= $_max_diff" \
+ 'test $_bisect_err -le $_max_diff'
+}
+
+date >path0
+git-update-index --add path0
+save_tag tree git-write-tree
+on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
+on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
+on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
+on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
+on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
+on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
+on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
+on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
+on_committer_date "1971-08-16 00:00:08" save_tag b2 unique_commit b2 tree -p b1
+on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b2 tree -p b2
+on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
+on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
+on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
+on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
+on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
+on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
+git-update-ref HEAD $(tag l5)
+
+
+# E
+# / \
+# e1 |
+# | |
+# e2 |
+# | |
+# e3 |
+# | |
+# e4 |
+# | |
+# | f1
+# | |
+# | f2
+# | |
+# | f3
+# | |
+# | f4
+# | |
+# e5 |
+# | |
+# e6 |
+# | |
+# e7 |
+# | |
+# e8 |
+# \ /
+# F
+
+
+on_committer_date "1971-08-16 00:00:00" hide_error save_tag F unique_commit F tree
+on_committer_date "1971-08-16 00:00:01" save_tag e8 unique_commit e8 tree -p F
+on_committer_date "1971-08-16 00:00:02" save_tag e7 unique_commit e7 tree -p e8
+on_committer_date "1971-08-16 00:00:03" save_tag e6 unique_commit e6 tree -p e7
+on_committer_date "1971-08-16 00:00:04" save_tag e5 unique_commit e5 tree -p e6
+on_committer_date "1971-08-16 00:00:05" save_tag f4 unique_commit f4 tree -p F
+on_committer_date "1971-08-16 00:00:06" save_tag f3 unique_commit f3 tree -p f4
+on_committer_date "1971-08-16 00:00:07" save_tag f2 unique_commit f2 tree -p f3
+on_committer_date "1971-08-16 00:00:08" save_tag f1 unique_commit f1 tree -p f2
+on_committer_date "1971-08-16 00:00:09" save_tag e4 unique_commit e4 tree -p e5
+on_committer_date "1971-08-16 00:00:10" save_tag e3 unique_commit e3 tree -p e4
+on_committer_date "1971-08-16 00:00:11" save_tag e2 unique_commit e2 tree -p e3
+on_committer_date "1971-08-16 00:00:12" save_tag e1 unique_commit e1 tree -p e2
+on_committer_date "1971-08-16 00:00:13" save_tag E unique_commit E tree -p e1 -p f1
+
+on_committer_date "1971-08-16 00:00:00" hide_error save_tag U unique_commit U tree
+on_committer_date "1971-08-16 00:00:01" save_tag u0 unique_commit u0 tree -p U
+on_committer_date "1971-08-16 00:00:01" save_tag u1 unique_commit u1 tree -p u0
+on_committer_date "1971-08-16 00:00:02" save_tag u2 unique_commit u2 tree -p u0
+on_committer_date "1971-08-16 00:00:03" save_tag u3 unique_commit u3 tree -p u0
+on_committer_date "1971-08-16 00:00:04" save_tag u4 unique_commit u4 tree -p u0
+on_committer_date "1971-08-16 00:00:05" save_tag u5 unique_commit u5 tree -p u0
+on_committer_date "1971-08-16 00:00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
+
+test_sequence()
+{
+ _bisect_option=$1
+
+ test_bisection_diff 0 $_bisect_option l0 ^root
+ test_bisection_diff 0 $_bisect_option l1 ^root
+ test_bisection_diff 0 $_bisect_option l2 ^root
+ test_bisection_diff 0 $_bisect_option a0 ^root
+ test_bisection_diff 0 $_bisect_option a1 ^root
+ test_bisection_diff 0 $_bisect_option a2 ^root
+ test_bisection_diff 0 $_bisect_option a3 ^root
+ test_bisection_diff 0 $_bisect_option b1 ^root
+ test_bisection_diff 0 $_bisect_option b2 ^root
+ test_bisection_diff 0 $_bisect_option b3 ^root
+ test_bisection_diff 0 $_bisect_option c1 ^root
+ test_bisection_diff 0 $_bisect_option c2 ^root
+ test_bisection_diff 0 $_bisect_option c3 ^root
+ test_bisection_diff 0 $_bisect_option E ^F
+ test_bisection_diff 0 $_bisect_option e1 ^F
+ test_bisection_diff 0 $_bisect_option e2 ^F
+ test_bisection_diff 0 $_bisect_option e3 ^F
+ test_bisection_diff 0 $_bisect_option e4 ^F
+ test_bisection_diff 0 $_bisect_option e5 ^F
+ test_bisection_diff 0 $_bisect_option e6 ^F
+ test_bisection_diff 0 $_bisect_option e7 ^F
+ test_bisection_diff 0 $_bisect_option f1 ^F
+ test_bisection_diff 0 $_bisect_option f2 ^F
+ test_bisection_diff 0 $_bisect_option f3 ^F
+ test_bisection_diff 0 $_bisect_option f4 ^F
+ test_bisection_diff 0 $_bisect_option E ^F
+
+ test_bisection_diff 1 $_bisect_option V ^U
+ test_bisection_diff 0 $_bisect_option V ^U ^u1 ^u2 ^u3
+ test_bisection_diff 0 $_bisect_option u1 ^U
+ test_bisection_diff 0 $_bisect_option u2 ^U
+ test_bisection_diff 0 $_bisect_option u3 ^U
+ test_bisection_diff 0 $_bisect_option u4 ^U
+ test_bisection_diff 0 $_bisect_option u5 ^U
+
+#
+# the following illustrate's Linus' binary bug blatt idea.
+#
+# assume the bug is actually at l3, but you don't know that - all you know is that l3 is broken
+# and it wasn't broken before
+#
+# keep bisecting the list, advancing the "bad" head and accumulating "good" heads until
+# the bisection point is the head - this is the bad point.
+#
+
+test_output_expect_success "--bisect l5 ^root" 'git-rev-list $_bisect_option l5 ^root' <<EOF
+c3
+EOF
+
+test_output_expect_success "$_bisect_option l5 ^root ^c3" 'git-rev-list $_bisect_option l5 ^root ^c3' <<EOF
+b4
+EOF
+
+test_output_expect_success "$_bisect_option l5 ^root ^c3 ^b4" 'git-rev-list $_bisect_option l5 ^c3 ^b4' <<EOF
+l3
+EOF
+
+test_output_expect_success "$_bisect_option l3 ^root ^c3 ^b4" 'git-rev-list $_bisect_option l3 ^root ^c3 ^b4' <<EOF
+a4
+EOF
+
+test_output_expect_success "$_bisect_option l5 ^b3 ^a3 ^b4 ^a4" 'git-rev-list $_bisect_option l3 ^b3 ^a3 ^a4' <<EOF
+l3
+EOF
+
+#
+# if l3 is bad, then l4 is bad too - so advance the bad pointer by making b4 the known bad head
+#
+
+test_output_expect_success "$_bisect_option l4 ^a2 ^a3 ^b ^a4" 'git-rev-list $_bisect_option l4 ^a2 ^a3 ^a4' <<EOF
+l3
+EOF
+
+test_output_expect_success "$_bisect_option l3 ^a2 ^a3 ^b ^a4" 'git-rev-list $_bisect_option l3 ^a2 ^a3 ^a4' <<EOF
+l3
+EOF
+
+# found!
+
+#
+# as another example, let's consider a4 to be the bad head, in which case
+#
+
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4" 'git-rev-list $_bisect_option a4 ^a2 ^a3 ^b4' <<EOF
+c2
+EOF
+
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4 ^c2" 'git-rev-list $_bisect_option a4 ^a2 ^a3 ^b4 ^c2' <<EOF
+c3
+EOF
+
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4 ^c2 ^c3" 'git-rev-list $_bisect_option a4 ^a2 ^a3 ^b4 ^c2 ^c3' <<EOF
+a4
+EOF
+
+# found!
+
+#
+# or consider c3 to be the bad head
+#
+
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4" 'git-rev-list $_bisect_option a4 ^a2 ^a3 ^b4' <<EOF
+c2
+EOF
+
+test_output_expect_success "$_bisect_option c3 ^a2 ^a3 ^b4 ^c2" 'git-rev-list $_bisect_option c3 ^a2 ^a3 ^b4 ^c2' <<EOF
+c3
+EOF
+
+# found!
+
+}
+
+test_sequence "--bisect"
+
+#
+#
+test_done
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
new file mode 100755
index 0000000000..d99a9ad39e
--- /dev/null
+++ b/t/t6003-rev-list-topo-order.sh
@@ -0,0 +1,408 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Jon Seymour
+#
+
+test_description='Tests git-rev-list --topo-order functionality'
+
+. ./test-lib.sh
+. ../t6000lib.sh # t6xxx specific functions
+
+list_duplicates()
+{
+ "$@" | sort | uniq -d
+}
+
+date >path0
+git-update-index --add path0
+save_tag tree git-write-tree
+on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
+on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
+on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
+on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
+on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
+on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
+on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
+on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
+on_committer_date "1971-08-16 00:00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
+on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b3 tree -p b2
+on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
+on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
+on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
+on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
+on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
+on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
+on_committer_date "1971-08-16 00:00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
+on_committer_date "1971-08-16 00:00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
+on_committer_date "1971-08-16 00:00:21" hide_error save_tag alt_root unique_commit alt_root tree
+on_committer_date "1971-08-16 00:00:22" save_tag r0 unique_commit r0 tree -p alt_root
+on_committer_date "1971-08-16 00:00:23" save_tag r1 unique_commit r1 tree -p r0
+on_committer_date "1971-08-16 00:00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
+on_committer_date "1971-08-16 00:00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
+
+
+#
+# note: as of 20/6, it isn't possible to create duplicate parents, so this
+# can't be tested.
+#
+#on_committer_date "1971-08-16 00:00:20" save_tag m3 unique_commit m3 tree -p c3 -p a4 -p c3
+hide_error save_tag e1 as_author e@example.com unique_commit e1 tree
+save_tag e2 as_author e@example.com unique_commit e2 tree -p e1
+save_tag f1 as_author f@example.com unique_commit f1 tree -p e1
+save_tag e3 as_author e@example.com unique_commit e3 tree -p e2
+save_tag f2 as_author f@example.com unique_commit f2 tree -p f1
+save_tag e4 as_author e@example.com unique_commit e4 tree -p e3 -p f2
+save_tag e5 as_author e@example.com unique_commit e5 tree -p e4
+save_tag f3 as_author f@example.com unique_commit f3 tree -p f2
+save_tag f4 as_author f@example.com unique_commit f4 tree -p f3
+save_tag e6 as_author e@example.com unique_commit e6 tree -p e5 -p f4
+save_tag f5 as_author f@example.com unique_commit f5 tree -p f4
+save_tag f6 as_author f@example.com unique_commit f6 tree -p f5 -p e6
+save_tag e7 as_author e@example.com unique_commit e7 tree -p e6
+save_tag e8 as_author e@example.com unique_commit e8 tree -p e7
+save_tag e9 as_author e@example.com unique_commit e9 tree -p e8
+save_tag f7 as_author f@example.com unique_commit f7 tree -p f6
+save_tag f8 as_author f@example.com unique_commit f8 tree -p f7
+save_tag f9 as_author f@example.com unique_commit f9 tree -p f8
+save_tag e10 as_author e@example.com unique_commit e1 tree -p e9 -p f8
+
+hide_error save_tag g0 unique_commit g0 tree
+save_tag g1 unique_commit g1 tree -p g0
+save_tag h1 unique_commit g2 tree -p g0
+save_tag g2 unique_commit g3 tree -p g1 -p h1
+save_tag h2 unique_commit g4 tree -p g2
+save_tag g3 unique_commit g5 tree -p g2
+save_tag g4 unique_commit g6 tree -p g3 -p h2
+
+git-update-ref HEAD $(tag l5)
+
+test_output_expect_success 'rev-list has correct number of entries' 'git-rev-list HEAD | wc -l | tr -d \" \"' <<EOF
+19
+EOF
+
+test_output_expect_success 'simple topo order' 'git-rev-list --topo-order HEAD' <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'two diamonds topo order (g6)' 'git-rev-list --topo-order g4' <<EOF
+g4
+h2
+g3
+g2
+h1
+g1
+g0
+EOF
+
+test_output_expect_success 'multiple heads' 'git-rev-list --topo-order a3 b3 c3' <<EOF
+a3
+a2
+a1
+c3
+c2
+c1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'multiple heads, prune at a1' 'git-rev-list --topo-order a3 b3 c3 ^a1' <<EOF
+a3
+a2
+c3
+c2
+c1
+b3
+b2
+b1
+EOF
+
+test_output_expect_success 'multiple heads, prune at l1' 'git-rev-list --topo-order a3 b3 c3 ^l1' <<EOF
+a3
+a2
+a1
+c3
+c2
+c1
+b3
+b2
+b1
+a0
+l2
+EOF
+
+test_output_expect_success 'cross-epoch, head at l5, prune at l1' 'git-rev-list --topo-order l5 ^l1' <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+EOF
+
+test_output_expect_success 'duplicated head arguments' 'git-rev-list --topo-order l5 l5 ^l1' <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+EOF
+
+test_output_expect_success 'prune near topo' 'git-rev-list --topo-order a4 ^c3' <<EOF
+a4
+b4
+a3
+a2
+a1
+b3
+EOF
+
+test_output_expect_success "head has no parent" 'git-rev-list --topo-order root' <<EOF
+root
+EOF
+
+test_output_expect_success "two nodes - one head, one base" 'git-rev-list --topo-order l0' <<EOF
+l0
+root
+EOF
+
+test_output_expect_success "three nodes one head, one internal, one base" 'git-rev-list --topo-order l1' <<EOF
+l1
+l0
+root
+EOF
+
+test_output_expect_success "linear prune l2 ^root" 'git-rev-list --topo-order l2 ^root' <<EOF
+l2
+l1
+l0
+EOF
+
+test_output_expect_success "linear prune l2 ^l0" 'git-rev-list --topo-order l2 ^l0' <<EOF
+l2
+l1
+EOF
+
+test_output_expect_success "linear prune l2 ^l1" 'git-rev-list --topo-order l2 ^l1' <<EOF
+l2
+EOF
+
+test_output_expect_success "linear prune l5 ^a4" 'git-rev-list --topo-order l5 ^a4' <<EOF
+l5
+l4
+l3
+EOF
+
+test_output_expect_success "linear prune l5 ^l3" 'git-rev-list --topo-order l5 ^l3' <<EOF
+l5
+l4
+EOF
+
+test_output_expect_success "linear prune l5 ^l4" 'git-rev-list --topo-order l5 ^l4' <<EOF
+l5
+EOF
+
+test_output_expect_success "max-count 10 - topo order" 'git-rev-list --topo-order --max-count=10 l5' <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+EOF
+
+test_output_expect_success "max-count 10 - non topo order" 'git-rev-list --max-count=10 l5' <<EOF
+l5
+l4
+l3
+a4
+b4
+a3
+a2
+c3
+c2
+b3
+EOF
+
+test_output_expect_success '--max-age=c3, no --topo-order' "git-rev-list --max-age=$(commit_date c3) l5" <<EOF
+l5
+l4
+l3
+a4
+b4
+a3
+a2
+c3
+EOF
+
+#
+# this test fails on --topo-order - a fix is required
+#
+#test_output_expect_success '--max-age=c3, --topo-order' "git-rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
+#l5
+#l4
+#l3
+#a4
+#c3
+#b4
+#a3
+#a2
+#EOF
+
+test_output_expect_success 'one specified head reachable from another a4, c3, --topo-order' "list_duplicates git-rev-list --topo-order a4 c3" <<EOF
+EOF
+
+test_output_expect_success 'one specified head reachable from another c3, a4, --topo-order' "list_duplicates git-rev-list --topo-order c3 a4" <<EOF
+EOF
+
+test_output_expect_success 'one specified head reachable from another a4, c3, no --topo-order' "list_duplicates git-rev-list a4 c3" <<EOF
+EOF
+
+test_output_expect_success 'one specified head reachable from another c3, a4, no --topo-order' "list_duplicates git-rev-list c3 a4" <<EOF
+EOF
+
+test_output_expect_success 'graph with c3 and a4 parents of head' "list_duplicates git-rev-list m1" <<EOF
+EOF
+
+test_output_expect_success 'graph with a4 and c3 parents of head' "list_duplicates git-rev-list m2" <<EOF
+EOF
+
+test_expect_success "head ^head --topo-order" 'git-rev-list --topo-order a3 ^a3' <<EOF
+EOF
+
+test_expect_success "head ^head no --topo-order" 'git-rev-list a3 ^a3' <<EOF
+EOF
+
+test_output_expect_success 'simple topo order (l5r1)' 'git-rev-list --topo-order l5r1' <<EOF
+l5r1
+r1
+r0
+alt_root
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'simple topo order (r1l5)' 'git-rev-list --topo-order r1l5' <<EOF
+r1l5
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+r1
+r0
+alt_root
+EOF
+
+test_output_expect_success "don't print things unreachable from one branch" "git-rev-list a3 ^b3 --topo-order" <<EOF
+a3
+a2
+a1
+EOF
+
+test_output_expect_success "--topo-order a4 l3" "git-rev-list --topo-order a4 l3" <<EOF
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+EOF
+
+#
+#
+
+test_done
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
new file mode 100755
index 0000000000..1dce123aec
--- /dev/null
+++ b/t/t6010-merge-base.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Merge base computation.
+'
+
+. ./test-lib.sh
+
+T=$(git-write-tree)
+
+M=1130000000
+Z=+0000
+
+export GIT_COMMITTER_EMAIL=git@comm.iter.xz
+export GIT_COMMITTER_NAME='C O Mmiter'
+export GIT_AUTHOR_NAME='A U Thor'
+export GIT_AUTHOR_EMAIL=git@au.thor.xz
+
+doit() {
+ OFFSET=$1; shift
+ NAME=$1; shift
+ PARENTS=
+ for P
+ do
+ PARENTS="${PARENTS}-p $P "
+ done
+ GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z"
+ GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+ commit=$(echo $NAME | git-commit-tree $T $PARENTS)
+ echo $commit >.git/refs/tags/$NAME
+ echo $commit
+}
+
+# Setup...
+E=$(doit 5 E)
+D=$(doit 4 D $E)
+F=$(doit 6 F $E)
+C=$(doit 3 C $D)
+B=$(doit 2 B $C)
+A=$(doit 1 A $B)
+G=$(doit 7 G $B $E)
+H=$(doit 8 H $A $F)
+
+test_expect_success 'compute merge-base (single)' \
+ 'MB=$(git-merge-base G H) &&
+ expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
+
+test_expect_success 'compute merge-base (all)' \
+ 'MB=$(git-merge-base --all G H) &&
+ expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
+
+test_expect_success 'compute merge-base with show-branch' \
+ 'MB=$(git-show-branch --merge-base G H) &&
+ expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
+
+test_done
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
new file mode 100755
index 0000000000..a19d49de28
--- /dev/null
+++ b/t/t6020-merge-df.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Fredrik Kuivinen
+#
+
+test_description='Test merge with directory/file conflicts'
+. ./test-lib.sh
+
+test_expect_success 'prepare repository' \
+'echo "Hello" > init &&
+git add init &&
+git commit -m "Initial commit" &&
+git branch B &&
+mkdir dir &&
+echo "foo" > dir/foo &&
+git add dir/foo &&
+git commit -m "File: dir/foo" &&
+git checkout B &&
+echo "file dir" > dir &&
+git add dir &&
+git commit -m "File: dir"'
+
+test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
+
+test_done
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
new file mode 100755
index 0000000000..262381379f
--- /dev/null
+++ b/t/t6021-merge-criss-cross.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Fredrik Kuivinen
+#
+
+# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a
+# nice decription of what this is about.
+
+
+test_description='Test criss-cross merge'
+. ./test-lib.sh
+
+if test "$no_python"; then
+ echo "Skipping: no python => no recursive merge"
+ test_done
+ exit 0
+fi
+
+test_expect_success 'prepare repository' \
+'echo "1
+2
+3
+4
+5
+6
+7
+8
+9" > file &&
+git add file &&
+git commit -m "Initial commit" file &&
+git branch A &&
+git branch B &&
+git checkout A &&
+echo "1
+2
+3
+4
+5
+6
+7
+8 changed in B8, branch A
+9" > file &&
+git commit -m "B8" file &&
+git checkout B &&
+echo "1
+2
+3 changed in C3, branch B
+4
+5
+6
+7
+8
+9
+" > file &&
+git commit -m "C3" file &&
+git branch C3 &&
+git merge "pre E3 merge" B A &&
+echo "1
+2
+3 changed in E3, branch B. New file size
+4
+5
+6
+7
+8 changed in B8, branch A
+9
+" > file &&
+git commit -m "E3" file &&
+git checkout A &&
+git merge "pre D8 merge" A C3 &&
+echo "1
+2
+3 changed in C3, branch B
+4
+5
+6
+7
+8 changed in D8, branch A. New file size 2
+9" > file &&
+git commit -m D8 file'
+
+test_expect_success 'Criss-cross merge' 'git merge "final merge" A B'
+
+cat > file-expect <<EOF
+1
+2
+3 changed in E3, branch B. New file size
+4
+5
+6
+7
+8 changed in D8, branch A. New file size 2
+9
+EOF
+
+test_expect_success 'Criss-cross merge result' 'cmp file file-expect'
+
+test_done
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
new file mode 100755
index 0000000000..5ac25647ab
--- /dev/null
+++ b/t/t6022-merge-rename.sh
@@ -0,0 +1,208 @@
+#!/bin/sh
+
+test_description='Merge-recursive merging renames'
+. ./test-lib.sh
+
+if test "$no_python"; then
+ echo "Skipping: no python => no recursive merge"
+ test_done
+ exit 0
+fi
+
+test_expect_success setup \
+'
+cat >A <<\EOF &&
+a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+c cccccccccccccccccccccccccccccccccccccccccccccccc
+d dddddddddddddddddddddddddddddddddddddddddddddddd
+e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+f ffffffffffffffffffffffffffffffffffffffffffffffff
+g gggggggggggggggggggggggggggggggggggggggggggggggg
+h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
+i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
+k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
+l llllllllllllllllllllllllllllllllllllllllllllllll
+m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
+o oooooooooooooooooooooooooooooooooooooooooooooooo
+EOF
+
+cat >M <<\EOF &&
+A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+EOF
+
+git add A M &&
+git commit -m initial &&
+git branch white &&
+git branch red &&
+git branch blue &&
+
+sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
+mv A+ A &&
+git commit -a -m "master updates A" &&
+
+git checkout white &&
+sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
+sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+rm -f A M &&
+git update-index --add --remove A B M N &&
+git commit -m "white renames A->B, M->N" &&
+
+git checkout red &&
+sed -e "/^g /s/.*/g : red changes a line/" <A >B &&
+sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+rm -f A M &&
+git update-index --add --remove A B M N &&
+git commit -m "red renames A->B, M->N" &&
+
+git checkout blue &&
+sed -e "/^g /s/.*/g : blue changes a line/" <A >C &&
+sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+rm -f A M &&
+git update-index --add --remove A C M N &&
+git commit -m "blue renames A->C, M->N" &&
+
+git checkout master'
+
+test_expect_success 'pull renaming branch into unrenaming one' \
+'
+ git show-branch
+ git pull . white && {
+ echo "BAD: should have conflicted"
+ exit 1
+ }
+ git ls-files -s
+ test "$(git ls-files -u B | wc -l)" -eq 3 || {
+ echo "BAD: should have left stages for B"
+ exit 1
+ }
+ test "$(git ls-files -s N | wc -l)" -eq 1 || {
+ echo "BAD: should have merged N"
+ exit 1
+ }
+ sed -ne "/^g/{
+ p
+ q
+ }" B | grep master || {
+ echo "BAD: should have listed our change first"
+ exit 1
+ }
+ test "$(git diff white N | wc -l)" -eq 0 || {
+ echo "BAD: should have taken colored branch"
+ exit 1
+ }
+'
+
+test_expect_success 'pull renaming branch into another renaming one' \
+'
+ rm -f B
+ git reset --hard
+ git checkout red
+ git pull . white && {
+ echo "BAD: should have conflicted"
+ exit 1
+ }
+ test "$(git ls-files -u B | wc -l)" -eq 3 || {
+ echo "BAD: should have left stages"
+ exit 1
+ }
+ test "$(git ls-files -s N | wc -l)" -eq 1 || {
+ echo "BAD: should have merged N"
+ exit 1
+ }
+ sed -ne "/^g/{
+ p
+ q
+ }" B | grep red || {
+ echo "BAD: should have listed our change first"
+ exit 1
+ }
+ test "$(git diff white N | wc -l)" -eq 0 || {
+ echo "BAD: should have taken colored branch"
+ exit 1
+ }
+'
+
+test_expect_success 'pull unrenaming branch into renaming one' \
+'
+ git reset --hard
+ git show-branch
+ git pull . master && {
+ echo "BAD: should have conflicted"
+ exit 1
+ }
+ test "$(git ls-files -u B | wc -l)" -eq 3 || {
+ echo "BAD: should have left stages"
+ exit 1
+ }
+ test "$(git ls-files -s N | wc -l)" -eq 1 || {
+ echo "BAD: should have merged N"
+ exit 1
+ }
+ sed -ne "/^g/{
+ p
+ q
+ }" B | grep red || {
+ echo "BAD: should have listed our change first"
+ exit 1
+ }
+ test "$(git diff white N | wc -l)" -eq 0 || {
+ echo "BAD: should have taken colored branch"
+ exit 1
+ }
+'
+
+test_expect_success 'pull conflicting renames' \
+'
+ git reset --hard
+ git show-branch
+ git pull . blue && {
+ echo "BAD: should have conflicted"
+ exit 1
+ }
+ test "$(git ls-files -u A | wc -l)" -eq 1 || {
+ echo "BAD: should have left a stage"
+ exit 1
+ }
+ test "$(git ls-files -u B | wc -l)" -eq 1 || {
+ echo "BAD: should have left a stage"
+ exit 1
+ }
+ test "$(git ls-files -u C | wc -l)" -eq 1 || {
+ echo "BAD: should have left a stage"
+ exit 1
+ }
+ test "$(git ls-files -s N | wc -l)" -eq 1 || {
+ echo "BAD: should have merged N"
+ exit 1
+ }
+ sed -ne "/^g/{
+ p
+ q
+ }" B | grep red || {
+ echo "BAD: should have listed our change first"
+ exit 1
+ }
+ test "$(git diff white N | wc -l)" -eq 0 || {
+ echo "BAD: should have taken colored branch"
+ exit 1
+ }
+'
+
+test_done
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
new file mode 100755
index 0000000000..7d354a1fae
--- /dev/null
+++ b/t/t6101-rev-parse-parents.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test git-rev-parse with different parent options'
+
+. ./test-lib.sh
+. ../t6000lib.sh # t6xxx specific functions
+
+date >path0
+git-update-index --add path0
+save_tag tree git-write-tree
+hide_error save_tag start unique_commit "start" tree
+save_tag second unique_commit "second" tree -p start
+hide_error save_tag start2 unique_commit "start2" tree
+save_tag two_parents unique_commit "next" tree -p second -p start2
+save_tag final unique_commit "final" tree -p two_parents
+
+test_expect_success 'start is valid' 'git-rev-parse start | grep "^[0-9a-f]\{40\}$"'
+test_expect_success 'start^0' "test $(cat .git/refs/tags/start) = $(git-rev-parse start^0)"
+test_expect_success 'start^1 not valid' "if git-rev-parse --verify start^1; then false; else :; fi"
+test_expect_success 'second^1 = second^' "test $(git-rev-parse second^1) = $(git-rev-parse second^)"
+test_expect_success 'final^1^1^1' "test $(git-rev-parse start) = $(git-rev-parse final^1^1^1)"
+test_expect_success 'final^1^1^1 = final^^^' "test $(git-rev-parse final^1^1^1) = $(git-rev-parse final^^^)"
+test_expect_success 'final^1^2' "test $(git-rev-parse start2) = $(git-rev-parse final^1^2)"
+test_expect_success 'final^1^2 != final^1^1' "test $(git-rev-parse final^1^2) != $(git-rev-parse final^1^1)"
+test_expect_success 'final^1^3 not valid' "if git-rev-parse --verify final^1^3; then false; else :; fi"
+test_expect_failure '--verify start2^1' 'git-rev-parse --verify start2^1'
+test_expect_success '--verify start2^0' 'git-rev-parse --verify start2^0'
+
+test_done
+
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
new file mode 100755
index 0000000000..811a4797a5
--- /dev/null
+++ b/t/t7001-mv.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='git-mv in subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'mkdir path0 path1 &&
+ cp ../../COPYING path0/COPYING &&
+ git-add path0/COPYING &&
+ git-commit -m add -a'
+
+test_expect_success \
+ 'moving the file out of subdirectory' \
+ 'cd path0 && git-mv COPYING ../path1/COPYING'
+
+# in path0 currently
+test_expect_success \
+ 'commiting the change' \
+ 'cd .. && git-commit -m move-out -a'
+
+test_expect_success \
+ 'checking the commit' \
+ 'git-diff-tree -r -M --name-status HEAD^ HEAD | \
+ grep -E "^R100.+path0/COPYING.+path1/COPYING"'
+
+test_expect_success \
+ 'moving the file back into subdirectory' \
+ 'cd path0 && git-mv ../path1/COPYING COPYING'
+
+# in path0 currently
+test_expect_success \
+ 'commiting the change' \
+ 'cd .. && git-commit -m move-in -a'
+
+test_expect_success \
+ 'checking the commit' \
+ 'git-diff-tree -r -M --name-status HEAD^ HEAD | \
+ grep -E "^R100.+path1/COPYING.+path0/COPYING"'
+
+test_done
diff --git a/t/t7101-reset.sh b/t/t7101-reset.sh
new file mode 100755
index 0000000000..a9191407f2
--- /dev/null
+++ b/t/t7101-reset.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='git-reset should cull empty subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+ 'creating initial files' \
+ 'mkdir path0 &&
+ cp ../../COPYING path0/COPYING &&
+ git-add path0/COPYING &&
+ git-commit -m add -a'
+
+test_expect_success \
+ 'creating second files' \
+ 'mkdir path1 &&
+ mkdir path1/path2 &&
+ cp ../../COPYING path1/path2/COPYING &&
+ cp ../../COPYING path1/COPYING &&
+ cp ../../COPYING COPYING &&
+ cp ../../COPYING path0/COPYING-TOO &&
+ git-add path1/path2/COPYING &&
+ git-add path1/COPYING &&
+ git-add COPYING &&
+ git-add path0/COPYING-TOO &&
+ git-commit -m change -a'
+
+test_expect_success \
+ 'resetting tree HEAD^' \
+ 'git-reset --hard HEAD^'
+
+test_expect_success \
+ 'checking initial files exist after rewind' \
+ 'test -d path0 &&
+ test -f path0/COPYING'
+
+test_expect_failure \
+ 'checking lack of path1/path2/COPYING' \
+ 'test -f path1/path2/COPYING'
+
+test_expect_failure \
+ 'checking lack of path1/COPYING' \
+ 'test -f path1/COPYING'
+
+test_expect_failure \
+ 'checking lack of COPYING' \
+ 'test -f COPYING'
+
+test_expect_failure \
+ 'checking checking lack of path1/COPYING-TOO' \
+ 'test -f path0/COPYING-TOO'
+
+test_expect_failure \
+ 'checking lack of path1/path2' \
+ 'test -d path1/path2'
+
+test_expect_failure \
+ 'checking lack of path1' \
+ 'test -d path1'
+
+test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
new file mode 100755
index 0000000000..2496397da3
--- /dev/null
+++ b/t/t8001-annotate.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+test_description='git-annotate'
+. ./test-lib.sh
+
+PROG='git annotate'
+. ../annotate-tests.sh
+
+test_done
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
new file mode 100755
index 0000000000..9777393996
--- /dev/null
+++ b/t/t8002-blame.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+test_description='git-blame'
+. ./test-lib.sh
+
+PROG='git blame -c'
+. ../annotate-tests.sh
+
+test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
new file mode 100755
index 0000000000..a61da1efbd
--- /dev/null
+++ b/t/t9001-send-email.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='git-send-email'
+. ./test-lib.sh
+
+PROG='git send-email'
+test_expect_success \
+ 'prepare reference tree' \
+ 'echo "1A quick brown fox jumps over the" >file &&
+ echo "lazy dog" >>file &&
+ git add file
+ GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
+
+test_expect_success \
+ 'Setup helper tool' \
+ '(echo "#!/bin/sh"
+ echo shift
+ echo for a
+ echo do
+ echo " echo \"!\$a!\""
+ echo "done >commandline"
+ echo "cat > msgtxt"
+ ) >fake.sendmail
+ chmod +x ./fake.sendmail
+ git add fake.sendmail
+ GIT_AUTHOR_NAME="A" git commit -a -m "Second."'
+
+test_expect_success \
+ 'Extract patches and send' \
+ 'git format-patch -n HEAD^1
+ git send-email -from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" ./0001*txt'
+
+cat >expected <<\EOF
+!nobody@example.com!
+!author@example.com!
+EOF
+test_expect_success \
+ 'Verify commandline' \
+ 'diff commandline expected'
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
new file mode 100755
index 0000000000..05f6e79560
--- /dev/null
+++ b/t/test-lib.sh
@@ -0,0 +1,217 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+# For repeatability, reset the environment to known value.
+LANG=C
+LC_ALL=C
+PAGER=cat
+TZ=UTC
+export LANG LC_ALL PAGER TZ
+unset AUTHOR_DATE
+unset AUTHOR_EMAIL
+unset AUTHOR_NAME
+unset COMMIT_AUTHOR_EMAIL
+unset COMMIT_AUTHOR_NAME
+unset GIT_ALTERNATE_OBJECT_DIRECTORIES
+unset GIT_AUTHOR_DATE
+GIT_AUTHOR_EMAIL=author@example.com
+GIT_AUTHOR_NAME='A U Thor'
+unset GIT_COMMITTER_DATE
+GIT_COMMITTER_EMAIL=committer@example.com
+GIT_COMMITTER_NAME='C O Mitter'
+unset GIT_DIFF_OPTS
+unset GIT_DIR
+unset GIT_EXTERNAL_DIFF
+unset GIT_INDEX_FILE
+unset GIT_OBJECT_DIRECTORY
+unset SHA1_FILE_DIRECTORIES
+unset SHA1_FILE_DIRECTORY
+export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
+export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
+
+# Each test should start with something like this, after copyright notices:
+#
+# test_description='Description of this test...
+# This test checks if command xyzzy does the right thing...
+# '
+# . ./test-lib.sh
+
+error () {
+ echo "* error: $*"
+ trap - exit
+ exit 1
+}
+
+say () {
+ echo "* $*"
+}
+
+test "${test_description}" != "" ||
+error "Test script did not set test_description."
+
+while test "$#" -ne 0
+do
+ case "$1" in
+ -d|--d|--de|--deb|--debu|--debug)
+ debug=t; shift ;;
+ -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
+ immediate=t; shift ;;
+ -h|--h|--he|--hel|--help)
+ echo "$test_description"
+ exit 0 ;;
+ -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+ verbose=t; shift ;;
+ --no-python)
+ no_python=t; shift ;;
+ *)
+ break ;;
+ esac
+done
+
+exec 5>&1
+if test "$verbose" = "t"
+then
+ exec 4>&2 3>&1
+else
+ exec 4>/dev/null 3>/dev/null
+fi
+
+test_failure=0
+test_count=0
+
+trap 'echo >&5 "FATAL: Unexpected exit with code $?"; exit 1' exit
+
+
+# You are not expected to call test_ok_ and test_failure_ directly, use
+# the text_expect_* functions instead.
+
+test_ok_ () {
+ test_count=$(expr "$test_count" + 1)
+ say " ok $test_count: $@"
+}
+
+test_failure_ () {
+ test_count=$(expr "$test_count" + 1)
+ test_failure=$(expr "$test_failure" + 1);
+ say "FAIL $test_count: $1"
+ shift
+ echo "$@" | sed -e 's/^/ /'
+ test "$immediate" = "" || { trap - exit; exit 1; }
+}
+
+
+test_debug () {
+ test "$debug" = "" || eval "$1"
+}
+
+test_run_ () {
+ eval >&3 2>&4 "$1"
+ eval_ret="$?"
+ return 0
+}
+
+test_expect_failure () {
+ test "$#" = 2 ||
+ error "bug in the test script: not 2 parameters to test-expect-failure"
+ say >&3 "expecting failure: $2"
+ test_run_ "$2"
+ if [ "$?" = 0 -a "$eval_ret" != 0 ]
+ then
+ test_ok_ "$1"
+ else
+ test_failure_ "$@"
+ fi
+}
+
+test_expect_success () {
+ test "$#" = 2 ||
+ error "bug in the test script: not 2 parameters to test-expect-success"
+ say >&3 "expecting success: $2"
+ test_run_ "$2"
+ if [ "$?" = 0 -a "$eval_ret" = 0 ]
+ then
+ test_ok_ "$1"
+ else
+ test_failure_ "$@"
+ fi
+}
+
+test_expect_code () {
+ test "$#" = 3 ||
+ error "bug in the test script: not 3 parameters to test-expect-code"
+ say >&3 "expecting exit code $1: $3"
+ test_run_ "$3"
+ if [ "$?" = 0 -a "$eval_ret" = "$1" ]
+ then
+ test_ok_ "$2"
+ else
+ test_failure_ "$@"
+ fi
+}
+
+# Most tests can use the created repository, but some amy need to create more.
+# Usage: test_create_repo <directory>
+test_create_repo () {
+ test "$#" = 1 ||
+ error "bug in the test script: not 1 parameter to test-create-repo"
+ owd=`pwd`
+ repo="$1"
+ mkdir "$repo"
+ cd "$repo" || error "Cannot setup test environment"
+ "$GIT_EXEC_PATH/git" init-db --template=$GIT_EXEC_PATH/templates/blt/ 2>/dev/null ||
+ error "cannot run git init-db -- have you built things yet?"
+ mv .git/hooks .git/hooks-disabled
+ cd "$owd"
+}
+
+test_done () {
+ trap - exit
+ case "$test_failure" in
+ 0)
+ # We could:
+ # cd .. && rm -fr trash
+ # but that means we forbid any tests that use their own
+ # subdirectory from calling test_done without coming back
+ # to where they started from.
+ # The Makefile provided will clean this test area so
+ # we will leave things as they are.
+
+ say "passed all $test_count test(s)"
+ exit 0 ;;
+
+ *)
+ say "failed $test_failure among $test_count test(s)"
+ exit 1 ;;
+
+ esac
+}
+
+# Test the binaries we have just built. The tests are kept in
+# t/ subdirectory and are run in trash subdirectory.
+PATH=$(pwd)/..:$PATH
+GIT_EXEC_PATH=$(pwd)/..
+export PATH GIT_EXEC_PATH
+
+# Similarly use ../compat/subprocess.py if our python does not
+# have subprocess.py on its own.
+PYTHON=`sed -e '1{
+ s/^#!//
+ q
+}' ../git-merge-recursive` || {
+ error "You haven't built things yet, have you?"
+}
+"$PYTHON" -c 'import subprocess' 2>/dev/null || {
+ PYTHONPATH=$(pwd)/../compat
+ export PYTHONPATH
+}
+test -d ../templates/blt || {
+ error "You haven't built things yet, have you?"
+}
+
+# Test repository
+test=trash
+rm -fr "$test"
+test_create_repo $test
+cd "$test"
diff --git a/t/test4012.png b/t/test4012.png
new file mode 100644
index 0000000000..7b181d15ce
--- /dev/null
+++ b/t/test4012.png
Binary files differ