summaryrefslogtreecommitdiff
path: root/t/t9300-fast-import.sh
diff options
context:
space:
mode:
Diffstat (limited to 't/t9300-fast-import.sh')
-rwxr-xr-xt/t9300-fast-import.sh1001
1 files changed, 993 insertions, 8 deletions
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index 131f032988..1a6c06631c 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -7,6 +7,23 @@ test_description='test git fast-import utility'
. ./test-lib.sh
. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+# Print $1 bytes from stdin to stdout.
+#
+# This could be written as "head -c $1", but IRIX "head" does not
+# support the -c option.
+head_c () {
+ perl -e '
+ my $len = $ARGV[1];
+ while ($len > 0) {
+ my $s;
+ my $nread = sysread(STDIN, $s, $len);
+ die "cannot read: $!" unless defined($nread);
+ print $s;
+ $len -= $nread;
+ }
+ ' - "$1"
+}
+
file2_data='file2
second line of EOF'
@@ -23,11 +40,26 @@ file5_data='an inline file.
file6_data='#!/bin/sh
echo "$@"'
+>empty
+
+test_expect_success 'setup: have pipes?' '
+ rm -f frob &&
+ if mkfifo frob
+ then
+ test_set_prereq PIPE
+ fi
+'
+
###
### series A
###
test_tick
+
+test_expect_success 'empty stream succeeds' '
+ git fast-import </dev/null
+'
+
cat >input <<INPUT_END
blob
mark :2
@@ -62,6 +94,12 @@ data <<EOF
An annotated tag without a tagger
EOF
+tag series-A-blob
+from :3
+data <<EOF
+An annotated tag that annotates a blob.
+EOF
+
INPUT_END
test_expect_success \
'A: create pack from stdin' \
@@ -120,6 +158,18 @@ test_expect_success 'A: verify tag/series-A' '
'
cat >expect <<EOF
+object $(git rev-parse refs/heads/master:file3)
+type blob
+tag series-A-blob
+
+An annotated tag that annotates a blob.
+EOF
+test_expect_success 'A: verify tag/series-A-blob' '
+ git cat-file tag tags/series-A-blob >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
:2 `git rev-parse --verify master:file2`
:3 `git rev-parse --verify master:file3`
:4 `git rev-parse --verify master:file4`
@@ -138,6 +188,55 @@ test_expect_success \
test_cmp expect marks.new'
test_tick
+new_blob=$(echo testing | git hash-object --stdin)
+cat >input <<INPUT_END
+tag series-A-blob-2
+from $(git rev-parse refs/heads/master:file3)
+data <<EOF
+Tag blob by sha1.
+EOF
+
+blob
+mark :6
+data <<EOF
+testing
+EOF
+
+commit refs/heads/new_blob
+committer <> 0 +0000
+data 0
+M 644 :6 new_blob
+#pretend we got sha1 from fast-import
+ls "new_blob"
+
+tag series-A-blob-3
+from $new_blob
+data <<EOF
+Tag new_blob.
+EOF
+INPUT_END
+
+cat >expect <<EOF
+object $(git rev-parse refs/heads/master:file3)
+type blob
+tag series-A-blob-2
+
+Tag blob by sha1.
+object $new_blob
+type blob
+tag series-A-blob-3
+
+Tag new_blob.
+EOF
+
+test_expect_success \
+ 'A: tag blob by sha1' \
+ 'git fast-import <input &&
+ git cat-file tag tags/series-A-blob-2 >actual &&
+ git cat-file tag tags/series-A-blob-3 >>actual &&
+ test_cmp expect actual'
+
+test_tick
cat >input <<INPUT_END
commit refs/heads/verify--import-marks
committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
@@ -166,6 +265,63 @@ test_expect_success \
test `git rev-parse --verify master:file2` \
= `git rev-parse --verify verify--import-marks:copy-of-file2`'
+test_tick
+mt=$(git hash-object --stdin < /dev/null)
+: >input.blob
+: >marks.exp
+: >tree.exp
+
+cat >input.commit <<EOF
+commit refs/heads/verify--dump-marks
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+test the sparse array dumping routines with exponentially growing marks
+COMMIT
+EOF
+
+i=0
+l=4
+m=6
+n=7
+while test "$i" -lt 27; do
+ cat >>input.blob <<EOF
+blob
+mark :$l
+data 0
+blob
+mark :$m
+data 0
+blob
+mark :$n
+data 0
+EOF
+ echo "M 100644 :$l l$i" >>input.commit
+ echo "M 100644 :$m m$i" >>input.commit
+ echo "M 100644 :$n n$i" >>input.commit
+
+ echo ":$l $mt" >>marks.exp
+ echo ":$m $mt" >>marks.exp
+ echo ":$n $mt" >>marks.exp
+
+ printf "100644 blob $mt\tl$i\n" >>tree.exp
+ printf "100644 blob $mt\tm$i\n" >>tree.exp
+ printf "100644 blob $mt\tn$i\n" >>tree.exp
+
+ l=$(($l + $l))
+ m=$(($m + $m))
+ n=$(($l + $n))
+
+ i=$((1 + $i))
+done
+
+sort tree.exp > tree.exp_s
+
+test_expect_success 'A: export marks with large values' '
+ cat input.blob input.commit | git fast-import --export-marks=marks.large &&
+ git ls-tree refs/heads/verify--dump-marks >tree.out &&
+ test_cmp tree.exp_s tree.out &&
+ test_cmp marks.exp marks.large'
+
###
### series B
###
@@ -235,6 +391,105 @@ test_expect_success \
test `git rev-parse master` = `git rev-parse TEMP_TAG^`'
rm -f .git/TEMP_TAG
+git gc 2>/dev/null >/dev/null
+git prune 2>/dev/null >/dev/null
+
+cat >input <<INPUT_END
+commit refs/heads/empty-committer-1
+committer <> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: accept empty committer' '
+ git fast-import <input &&
+ out=$(git fsck) &&
+ echo "$out" &&
+ test -z "$out"
+'
+git update-ref -d refs/heads/empty-committer-1 || true
+
+git gc 2>/dev/null >/dev/null
+git prune 2>/dev/null >/dev/null
+
+cat >input <<INPUT_END
+commit refs/heads/empty-committer-2
+committer <a@b.com> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: accept and fixup committer with no name' '
+ git fast-import <input &&
+ out=$(git fsck) &&
+ echo "$out" &&
+ test -z "$out"
+'
+git update-ref -d refs/heads/empty-committer-2 || true
+
+git gc 2>/dev/null >/dev/null
+git prune 2>/dev/null >/dev/null
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name email> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (1)' '
+ test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name <e<mail> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (2)' '
+ test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name <email>> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (3)' '
+ test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name <email $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (4)' '
+ test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
+cat >input <<INPUT_END
+commit refs/heads/invalid-committer
+committer Name<email> $GIT_COMMITTER_DATE
+data <<COMMIT
+empty commit
+COMMIT
+INPUT_END
+test_expect_success 'B: fail on invalid committer (5)' '
+ test_must_fail git fast-import <input
+'
+git update-ref -d refs/heads/invalid-committer || true
+
###
### series C
###
@@ -264,7 +519,7 @@ test_expect_success \
'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
test_expect_success \
'C: validate reuse existing blob' \
- 'test $newf = `git rev-parse --verify branch:file2/newf`
+ 'test $newf = `git rev-parse --verify branch:file2/newf` &&
test $oldf = `git rev-parse --verify branch:file2/oldf`'
cat >expect <<EOF
@@ -645,6 +900,47 @@ test_expect_success \
git diff-tree --abbrev --raw L^ L >output &&
test_cmp expect output'
+cat >input <<INPUT_END
+blob
+mark :1
+data <<EOF
+the data
+EOF
+
+commit refs/heads/L2
+committer C O Mitter <committer@example.com> 1112912473 -0700
+data <<COMMIT
+init L2
+COMMIT
+M 644 :1 a/b/c
+M 644 :1 a/b/d
+M 644 :1 a/e/f
+
+commit refs/heads/L2
+committer C O Mitter <committer@example.com> 1112912473 -0700
+data <<COMMIT
+update L2
+COMMIT
+C a g
+C a/e g/b
+M 644 :1 g/b/h
+INPUT_END
+
+cat <<EOF >expect
+g/b/f
+g/b/h
+EOF
+
+test_expect_success \
+ 'L: nested tree copy does not corrupt deltas' \
+ 'git fast-import <input &&
+ git ls-tree L2 g/b/ >tmp &&
+ cat tmp | cut -f 2 >actual &&
+ test_cmp expect actual &&
+ git fsck `git rev-parse L2`'
+
+git update-ref -d refs/heads/L2
+
###
### series M
###
@@ -796,6 +1092,302 @@ test_expect_success \
'git fast-import <input &&
test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
+test_expect_success \
+ 'N: copy directory by id' \
+ 'cat >expect <<-\EOF &&
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N4
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+ compare_diff_raw expect actual'
+
+test_expect_success PIPE 'N: read and copy directory' '
+ cat >expect <<-\EOF
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ git update-ref -d refs/heads/N4 &&
+ rm -f backflow &&
+ mkfifo backflow &&
+ (
+ exec <backflow &&
+ cat <<-EOF &&
+ commit refs/heads/N4
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash, part 2
+ COMMIT
+
+ from refs/heads/branch^0
+ ls "file2"
+ EOF
+ read mode type tree filename &&
+ echo "M 040000 $tree file3"
+ ) |
+ git fast-import --cat-blob-fd=3 3>backflow &&
+ git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+ compare_diff_raw expect actual
+'
+
+test_expect_success PIPE 'N: empty directory reads as missing' '
+ cat <<-\EOF >expect &&
+ OBJNAME
+ :000000 100644 OBJNAME OBJNAME A unrelated
+ EOF
+ echo "missing src" >expect.response &&
+ git update-ref -d refs/heads/read-empty &&
+ rm -f backflow &&
+ mkfifo backflow &&
+ (
+ exec <backflow &&
+ cat <<-EOF &&
+ commit refs/heads/read-empty
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ read "empty" (missing) directory
+ COMMIT
+
+ M 100644 inline src/greeting
+ data <<BLOB
+ hello
+ BLOB
+ C src/greeting dst1/non-greeting
+ C src/greeting unrelated
+ # leave behind "empty" src directory
+ D src/greeting
+ ls "src"
+ EOF
+ read -r line &&
+ printf "%s\n" "$line" >response &&
+ cat <<-\EOF
+ D dst1
+ D dst2
+ EOF
+ ) |
+ git fast-import --cat-blob-fd=3 3>backflow &&
+ test_cmp expect.response response &&
+ git rev-list read-empty |
+ git diff-tree -r --root --stdin |
+ sed "s/$_x40/OBJNAME/g" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'N: copy root directory by tree hash' \
+ 'cat >expect <<-\EOF &&
+ :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D file3/newf
+ :100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D file3/oldf
+ EOF
+ root=$(git rev-parse refs/heads/branch^0^{tree}) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N6
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy root directory by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $root ""
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
+ compare_diff_raw expect actual'
+
+test_expect_success \
+ 'N: delete directory by copying' \
+ 'cat >expect <<-\EOF &&
+ OBJID
+ :100644 000000 OBJID OBJID D foo/bar/qux
+ OBJID
+ :000000 100644 OBJID OBJID A foo/bar/baz
+ :000000 100644 OBJID OBJID A foo/bar/qux
+ EOF
+ empty_tree=$(git mktree </dev/null) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N-delete
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ collect data to be deleted
+ COMMIT
+
+ deleteall
+ M 100644 inline foo/bar/baz
+ data <<DATA_END
+ hello
+ DATA_END
+ C "foo/bar/baz" "foo/bar/qux"
+ C "foo/bar/baz" "foo/bar/quux/1"
+ C "foo/bar/baz" "foo/bar/quuux"
+ M 040000 $empty_tree foo/bar/quux
+ M 040000 $empty_tree foo/bar/quuux
+
+ commit refs/heads/N-delete
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ delete subdirectory
+ COMMIT
+
+ M 040000 $empty_tree foo/bar/qux
+ INPUT_END
+ git fast-import <input &&
+ git rev-list N-delete |
+ git diff-tree -r --stdin --root --always |
+ sed -e "s/$_x40/OBJID/g" >actual &&
+ test_cmp expect actual'
+
+test_expect_success \
+ 'N: modify copied tree' \
+ 'cat >expect <<-\EOF &&
+ :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N5
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3
+
+ commit refs/heads/N5
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ modify directory copy
+ COMMIT
+
+ M 644 inline file3/file5
+ data <<EOF
+ $file5_data
+ EOF
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
+ compare_diff_raw expect actual'
+
+test_expect_success \
+ 'N: reject foo/ syntax' \
+ 'subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ test_must_fail git fast-import <<-INPUT_END
+ commit refs/heads/N5B
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy with invalid syntax
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3/
+ INPUT_END'
+
+test_expect_success \
+ 'N: copy to root by id and modify' \
+ 'echo "hello, world" >expect.foo &&
+ echo hello >expect.bar &&
+ git fast-import <<-SETUP_END &&
+ commit refs/heads/N7
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ hello, tree
+ COMMIT
+
+ deleteall
+ M 644 inline foo/bar
+ data <<EOF
+ hello
+ EOF
+ SETUP_END
+
+ tree=$(git rev-parse --verify N7:) &&
+ git fast-import <<-INPUT_END &&
+ commit refs/heads/N8
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy to root by id and modify
+ COMMIT
+
+ M 040000 $tree ""
+ M 644 inline foo/foo
+ data <<EOF
+ hello, world
+ EOF
+ INPUT_END
+ git show N8:foo/foo >actual.foo &&
+ git show N8:foo/bar >actual.bar &&
+ test_cmp expect.foo actual.foo &&
+ test_cmp expect.bar actual.bar'
+
+test_expect_success \
+ 'N: extract subtree' \
+ 'branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N9
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ extract subtree branch:newdir
+ COMMIT
+
+ M 040000 $branch ""
+ C "newdir" ""
+ INPUT_END
+ git fast-import <input &&
+ git diff --exit-code branch:newdir N9'
+
+test_expect_success \
+ 'N: modify subtree, extract it, and modify again' \
+ 'echo hello >expect.baz &&
+ echo hello, world >expect.qux &&
+ git fast-import <<-SETUP_END &&
+ commit refs/heads/N10
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ hello, tree
+ COMMIT
+
+ deleteall
+ M 644 inline foo/bar/baz
+ data <<EOF
+ hello
+ EOF
+ SETUP_END
+
+ tree=$(git rev-parse --verify N10:) &&
+ git fast-import <<-INPUT_END &&
+ commit refs/heads/N11
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy to root by id and modify
+ COMMIT
+
+ M 040000 $tree ""
+ M 100644 inline foo/bar/qux
+ data <<EOF
+ hello, world
+ EOF
+ R "foo" ""
+ C "bar/qux" "bar/quux"
+ INPUT_END
+ git show N11:bar/baz >actual.baz &&
+ git show N11:bar/qux >actual.qux &&
+ git show N11:bar/quux >actual.quux &&
+ test_cmp expect.baz actual.baz &&
+ test_cmp expect.qux actual.qux &&
+ test_cmp expect.qux actual.quux'
+
###
### series O
###
@@ -999,11 +1591,10 @@ test_expect_success \
'P: supermodule & submodule mix' \
'git fast-import <input &&
git checkout subuse1 &&
- rm -rf sub && mkdir sub && cd sub &&
+ rm -rf sub && mkdir sub && (cd sub &&
git init &&
git fetch --update-head-ok .. refs/heads/sub:refs/heads/master &&
- git checkout master &&
- cd .. &&
+ git checkout master) &&
git submodule init &&
git submodule update'
@@ -1443,6 +2034,108 @@ test_expect_success \
'cat input | git fast-import --export-marks=other.marks &&
grep :1 other.marks'
+test_expect_success 'R: catch typo in marks file name' '
+ test_must_fail git fast-import --import-marks=nonexistent.marks </dev/null &&
+ echo "feature import-marks=nonexistent.marks" |
+ test_must_fail git fast-import
+'
+
+test_expect_success 'R: import and output marks can be the same file' '
+ rm -f io.marks &&
+ blob=$(echo hi | git hash-object --stdin) &&
+ cat >expect <<-EOF &&
+ :1 $blob
+ :2 $blob
+ EOF
+ git fast-import --export-marks=io.marks <<-\EOF &&
+ blob
+ mark :1
+ data 3
+ hi
+
+ EOF
+ git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF &&
+ blob
+ mark :2
+ data 3
+ hi
+
+ EOF
+ test_cmp expect io.marks
+'
+
+test_expect_success 'R: --import-marks=foo --output-marks=foo to create foo fails' '
+ rm -f io.marks &&
+ test_must_fail git fast-import --import-marks=io.marks --export-marks=io.marks <<-\EOF
+ blob
+ mark :1
+ data 3
+ hi
+
+ EOF
+'
+
+test_expect_success 'R: --import-marks-if-exists' '
+ rm -f io.marks &&
+ blob=$(echo hi | git hash-object --stdin) &&
+ echo ":1 $blob" >expect &&
+ git fast-import --import-marks-if-exists=io.marks --export-marks=io.marks <<-\EOF &&
+ blob
+ mark :1
+ data 3
+ hi
+
+ EOF
+ test_cmp expect io.marks
+'
+
+test_expect_success 'R: feature import-marks-if-exists' '
+ rm -f io.marks &&
+ >expect &&
+
+ git fast-import --export-marks=io.marks <<-\EOF &&
+ feature import-marks-if-exists=not_io.marks
+ EOF
+ test_cmp expect io.marks &&
+
+ blob=$(echo hi | git hash-object --stdin) &&
+
+ echo ":1 $blob" >io.marks &&
+ echo ":1 $blob" >expect &&
+ echo ":2 $blob" >>expect &&
+
+ git fast-import --export-marks=io.marks <<-\EOF &&
+ feature import-marks-if-exists=io.marks
+ blob
+ mark :2
+ data 3
+ hi
+
+ EOF
+ test_cmp expect io.marks &&
+
+ echo ":3 $blob" >>expect &&
+
+ git fast-import --import-marks=io.marks \
+ --export-marks=io.marks <<-\EOF &&
+ feature import-marks-if-exists=not_io.marks
+ blob
+ mark :3
+ data 3
+ hi
+
+ EOF
+ test_cmp expect io.marks &&
+
+ >expect &&
+
+ git fast-import --import-marks-if-exists=not_io.marks \
+ --export-marks=io.marks <<-\EOF
+ feature import-marks-if-exists=io.marks
+ EOF
+ test_cmp expect io.marks
+'
+
cat >input << EOF
feature import-marks=marks.out
feature export-marks=marks.new
@@ -1454,7 +2147,7 @@ test_expect_success \
test_cmp marks.out marks.new'
cat >input <<EOF
-feature import-marks=nonexistant.marks
+feature import-marks=nonexistent.marks
feature export-marks=marks.new
EOF
@@ -1465,7 +2158,7 @@ test_expect_success \
cat >input <<EOF
-feature import-marks=nonexistant.marks
+feature import-marks=nonexistent.marks
feature export-marks=combined.marks
EOF
@@ -1501,6 +2194,250 @@ test_expect_success 'R: feature no-relative-marks should be honoured' '
test_cmp marks.new non-relative.out
'
+test_expect_success 'R: feature ls supported' '
+ echo "feature ls" |
+ git fast-import
+'
+
+test_expect_success 'R: feature cat-blob supported' '
+ echo "feature cat-blob" |
+ git fast-import
+'
+
+test_expect_success 'R: cat-blob-fd must be a nonnegative integer' '
+ test_must_fail git fast-import --cat-blob-fd=-1 </dev/null
+'
+
+test_expect_success 'R: print old blob' '
+ blob=$(echo "yes it can" | git hash-object -w --stdin) &&
+ cat >expect <<-EOF &&
+ ${blob} blob 11
+ yes it can
+
+ EOF
+ echo "cat-blob $blob" |
+ git fast-import --cat-blob-fd=6 6>actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'R: in-stream cat-blob-fd not respected' '
+ echo hello >greeting &&
+ blob=$(git hash-object -w greeting) &&
+ cat >expect <<-EOF &&
+ ${blob} blob 6
+ hello
+
+ EOF
+ git fast-import --cat-blob-fd=3 3>actual.3 >actual.1 <<-EOF &&
+ cat-blob $blob
+ EOF
+ test_cmp expect actual.3 &&
+ test_cmp empty actual.1 &&
+ git fast-import 3>actual.3 >actual.1 <<-EOF &&
+ option cat-blob-fd=3
+ cat-blob $blob
+ EOF
+ test_cmp empty actual.3 &&
+ test_cmp expect actual.1
+'
+
+test_expect_success 'R: print new blob' '
+ blob=$(echo "yep yep yep" | git hash-object --stdin) &&
+ cat >expect <<-EOF &&
+ ${blob} blob 12
+ yep yep yep
+
+ EOF
+ git fast-import --cat-blob-fd=6 6>actual <<-\EOF &&
+ blob
+ mark :1
+ data <<BLOB_END
+ yep yep yep
+ BLOB_END
+ cat-blob :1
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'R: print new blob by sha1' '
+ blob=$(echo "a new blob named by sha1" | git hash-object --stdin) &&
+ cat >expect <<-EOF &&
+ ${blob} blob 25
+ a new blob named by sha1
+
+ EOF
+ git fast-import --cat-blob-fd=6 6>actual <<-EOF &&
+ blob
+ data <<BLOB_END
+ a new blob named by sha1
+ BLOB_END
+ cat-blob $blob
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'setup: big file' '
+ (
+ echo "the quick brown fox jumps over the lazy dog" >big &&
+ for i in 1 2 3
+ do
+ cat big big big big >bigger &&
+ cat bigger bigger bigger bigger >big ||
+ exit
+ done
+ )
+'
+
+test_expect_success 'R: print two blobs to stdout' '
+ blob1=$(git hash-object big) &&
+ blob1_len=$(wc -c <big) &&
+ blob2=$(echo hello | git hash-object --stdin) &&
+ {
+ echo ${blob1} blob $blob1_len &&
+ cat big &&
+ cat <<-EOF
+
+ ${blob2} blob 6
+ hello
+
+ EOF
+ } >expect &&
+ {
+ cat <<-\END_PART1 &&
+ blob
+ mark :1
+ data <<data_end
+ END_PART1
+ cat big &&
+ cat <<-\EOF
+ data_end
+ blob
+ mark :2
+ data <<data_end
+ hello
+ data_end
+ cat-blob :1
+ cat-blob :2
+ EOF
+ } |
+ git fast-import >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success PIPE 'R: copy using cat-file' '
+ expect_id=$(git hash-object big) &&
+ expect_len=$(wc -c <big) &&
+ echo $expect_id blob $expect_len >expect.response &&
+
+ rm -f blobs &&
+ cat >frontend <<-\FRONTEND_END &&
+ #!/bin/sh
+ FRONTEND_END
+
+ mkfifo blobs &&
+ (
+ export GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_COMMITTER_DATE &&
+ cat <<-\EOF &&
+ feature cat-blob
+ blob
+ mark :1
+ data <<BLOB
+ EOF
+ cat big &&
+ cat <<-\EOF &&
+ BLOB
+ cat-blob :1
+ EOF
+
+ read blob_id type size <&3 &&
+ echo "$blob_id $type $size" >response &&
+ head_c $size >blob <&3 &&
+ read newline <&3 &&
+
+ cat <<-EOF &&
+ commit refs/heads/copied
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy big file as file3
+ COMMIT
+ M 644 inline file3
+ data <<BLOB
+ EOF
+ cat blob &&
+ echo BLOB
+ ) 3<blobs |
+ git fast-import --cat-blob-fd=3 3>blobs &&
+ git show copied:file3 >actual &&
+ test_cmp expect.response response &&
+ test_cmp big actual
+'
+
+test_expect_success PIPE 'R: print blob mid-commit' '
+ rm -f blobs &&
+ echo "A blob from _before_ the commit." >expect &&
+ mkfifo blobs &&
+ (
+ exec 3<blobs &&
+ cat <<-EOF &&
+ feature cat-blob
+ blob
+ mark :1
+ data <<BLOB
+ A blob from _before_ the commit.
+ BLOB
+ commit refs/heads/temporary
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ Empty commit
+ COMMIT
+ cat-blob :1
+ EOF
+
+ read blob_id type size <&3 &&
+ head_c $size >actual <&3 &&
+ read newline <&3 &&
+
+ echo
+ ) |
+ git fast-import --cat-blob-fd=3 3>blobs &&
+ test_cmp expect actual
+'
+
+test_expect_success PIPE 'R: print staged blob within commit' '
+ rm -f blobs &&
+ echo "A blob from _within_ the commit." >expect &&
+ mkfifo blobs &&
+ (
+ exec 3<blobs &&
+ cat <<-EOF &&
+ feature cat-blob
+ commit refs/heads/within
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ Empty commit
+ COMMIT
+ M 644 inline within
+ data <<BLOB
+ A blob from _within_ the commit.
+ BLOB
+ EOF
+
+ to_get=$(
+ echo "A blob from _within_ the commit." |
+ git hash-object --stdin
+ ) &&
+ echo "cat-blob $to_get" &&
+
+ read blob_id type size <&3 &&
+ head_c $size >actual <&3 &&
+ read newline <&3 &&
+
+ echo deleteall
+ ) |
+ git fast-import --cat-blob-fd=3 3>blobs &&
+ test_cmp expect actual
+'
+
cat >input << EOF
option git quiet
blob
@@ -1509,13 +2446,53 @@ hi
EOF
-touch empty
-
test_expect_success 'R: quiet option results in no stats being output' '
cat input | git fast-import 2> output &&
test_cmp empty output
'
+test_expect_success 'R: feature done means terminating "done" is mandatory' '
+ echo feature done | test_must_fail git fast-import &&
+ test_must_fail git fast-import --done </dev/null
+'
+
+test_expect_success 'R: terminating "done" with trailing gibberish is ok' '
+ git fast-import <<-\EOF &&
+ feature done
+ done
+ trailing gibberish
+ EOF
+ git fast-import <<-\EOF
+ done
+ more trailing gibberish
+ EOF
+'
+
+test_expect_success 'R: terminating "done" within commit' '
+ cat >expect <<-\EOF &&
+ OBJID
+ :000000 100644 OBJID OBJID A hello.c
+ :000000 100644 OBJID OBJID A hello2.c
+ EOF
+ git fast-import <<-EOF &&
+ commit refs/heads/done-ends
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<EOT
+ Commit terminated by "done" command
+ EOT
+ M 100644 inline hello.c
+ data <<EOT
+ Hello, world.
+ EOT
+ C hello.c hello2.c
+ done
+ EOF
+ git rev-list done-ends |
+ git diff-tree -r --stdin --root --always |
+ sed -e "s/$_x40/OBJID/g" >actual &&
+ test_cmp expect actual
+'
+
cat >input <<EOF
option git non-existing-option
EOF
@@ -1528,6 +2505,14 @@ test_expect_success 'R: unknown commandline options are rejected' '\
test_must_fail git fast-import --non-existing-option < /dev/null
'
+test_expect_success 'R: die on invalid option argument' '
+ echo "option git active-branches=-5" |
+ test_must_fail git fast-import &&
+ echo "option git depth=" |
+ test_must_fail git fast-import &&
+ test_must_fail git fast-import --depth="5 elephants" </dev/null
+'
+
cat >input <<EOF
option non-existing-vcs non-existing-option
EOF