diff options
Diffstat (limited to 't')
505 files changed, 37950 insertions, 6967 deletions
diff --git a/t/.gitattributes b/t/.gitattributes index 1b97c5465b..2d44088f56 100644 --- a/t/.gitattributes +++ b/t/.gitattributes @@ -1 +1,2 @@ t[0-9][0-9][0-9][0-9]/* -whitespace +t0110/url-* binary diff --git a/t/Git-SVN/00compile.t b/t/Git-SVN/00compile.t new file mode 100644 index 0000000000..c92fee453f --- /dev/null +++ b/t/Git-SVN/00compile.t @@ -0,0 +1,14 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More tests => 7; + +require_ok 'Git::SVN'; +require_ok 'Git::SVN::Utils'; +require_ok 'Git::SVN::Ra'; +require_ok 'Git::SVN::Log'; +require_ok 'Git::SVN::Migration'; +require_ok 'Git::IndexInfo'; +require_ok 'Git::SVN::GlobSpec'; diff --git a/t/Git-SVN/Utils/add_path_to_url.t b/t/Git-SVN/Utils/add_path_to_url.t new file mode 100644 index 0000000000..bfbd87845f --- /dev/null +++ b/t/Git-SVN/Utils/add_path_to_url.t @@ -0,0 +1,27 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More 'no_plan'; + +use Git::SVN::Utils qw( + add_path_to_url +); + +# A reference cannot be a hash key, so we use an array. +my @tests = ( + ["http://x.com", "bar"] => 'http://x.com/bar', + ["http://x.com", ""] => 'http://x.com', + ["http://x.com/foo/", undef] => 'http://x.com/foo/', + ["http://x.com/foo/", "/bar/baz/"] => 'http://x.com/foo/bar/baz/', + ["http://x.com", 'per%cent'] => 'http://x.com/per%25cent', +); + +while(@tests) { + my($have, $want) = splice @tests, 0, 2; + + my $args = join ", ", map { qq['$_'] } map { defined($_) ? $_ : 'undef' } @$have; + my $name = "add_path_to_url($args) eq $want"; + is add_path_to_url(@$have), $want, $name; +} diff --git a/t/Git-SVN/Utils/can_compress.t b/t/Git-SVN/Utils/can_compress.t new file mode 100644 index 0000000000..d7b49b8d54 --- /dev/null +++ b/t/Git-SVN/Utils/can_compress.t @@ -0,0 +1,11 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More 'no_plan'; + +use Git::SVN::Utils qw(can_compress); + +# !! is the "convert this to boolean" operator. +is !!can_compress(), !!eval { require Compress::Zlib }; diff --git a/t/Git-SVN/Utils/canonicalize_url.t b/t/Git-SVN/Utils/canonicalize_url.t new file mode 100644 index 0000000000..05795ab636 --- /dev/null +++ b/t/Git-SVN/Utils/canonicalize_url.t @@ -0,0 +1,26 @@ +#!/usr/bin/env perl + +# Test our own home rolled URL canonicalizer. Test the private one +# directly because we can't predict what the SVN API is doing to do. + +use strict; +use warnings; + +use Test::More 'no_plan'; + +use Git::SVN::Utils; +my $canonicalize_url = \&Git::SVN::Utils::_canonicalize_url_ourselves; + +my %tests = ( + "http://x.com" => "http://x.com", + "http://x.com/" => "http://x.com", + "http://x.com/foo/bar" => "http://x.com/foo/bar", + "http://x.com//foo//bar//" => "http://x.com/foo/bar", + "http://x.com/ /%/" => "http://x.com/%20%20/%25", +); + +for my $arg (keys %tests) { + my $want = $tests{$arg}; + + is $canonicalize_url->($arg), $want, "canonicalize_url('$arg') => $want"; +} diff --git a/t/Git-SVN/Utils/collapse_dotdot.t b/t/Git-SVN/Utils/collapse_dotdot.t new file mode 100644 index 0000000000..1da1cce156 --- /dev/null +++ b/t/Git-SVN/Utils/collapse_dotdot.t @@ -0,0 +1,23 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More 'no_plan'; + +use Git::SVN::Utils; +my $collapse_dotdot = \&Git::SVN::Utils::_collapse_dotdot; + +my %tests = ( + "foo/bar/baz" => "foo/bar/baz", + ".." => "..", + "foo/.." => "", + "/foo/bar/../../baz" => "/baz", + "deeply/.././deeply/nested" => "./deeply/nested", +); + +for my $arg (keys %tests) { + my $want = $tests{$arg}; + + is $collapse_dotdot->($arg), $want, "_collapse_dotdot('$arg') => $want"; +} diff --git a/t/Git-SVN/Utils/fatal.t b/t/Git-SVN/Utils/fatal.t new file mode 100644 index 0000000000..49e1438295 --- /dev/null +++ b/t/Git-SVN/Utils/fatal.t @@ -0,0 +1,34 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Test::More 'no_plan'; + +BEGIN { + # Override exit at BEGIN time before Git::SVN::Utils is loaded + # so it will see our local exit later. + *CORE::GLOBAL::exit = sub(;$) { + return @_ ? CORE::exit($_[0]) : CORE::exit(); + }; +} + +use Git::SVN::Utils qw(fatal); + +# fatal() +{ + # Capture the exit code and prevent exit. + my $exit_status; + no warnings 'redefine'; + local *CORE::GLOBAL::exit = sub { $exit_status = $_[0] || 0 }; + + # Trap fatal's message to STDERR + my $stderr; + close STDERR; + ok open STDERR, ">", \$stderr; + + fatal "Some", "Stuff", "Happened"; + + is $stderr, "Some Stuff Happened\n"; + is $exit_status, 1; +} diff --git a/t/Git-SVN/Utils/join_paths.t b/t/Git-SVN/Utils/join_paths.t new file mode 100644 index 0000000000..d4488e7162 --- /dev/null +++ b/t/Git-SVN/Utils/join_paths.t @@ -0,0 +1,32 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +use Test::More 'no_plan'; + +use Git::SVN::Utils qw( + join_paths +); + +# A reference cannot be a hash key, so we use an array. +my @tests = ( + [] => '', + ["/x.com", "bar"] => '/x.com/bar', + ["x.com", ""] => 'x.com', + ["/x.com/foo/", undef, "bar"] => '/x.com/foo/bar', + ["x.com/foo/", "/bar/baz/"] => 'x.com/foo/bar/baz/', + ["foo", "bar"] => 'foo/bar', + ["/foo/bar", "baz", "/biff"] => '/foo/bar/baz/biff', + ["", undef, "."] => '.', + [] => '', + +); + +while(@tests) { + my($have, $want) = splice @tests, 0, 2; + + my $args = join ", ", map { qq['$_'] } map { defined($_) ? $_ : 'undef' } @$have; + my $name = "join_paths($args) eq '$want'"; + is join_paths(@$have), $want, $name; +} diff --git a/t/Makefile b/t/Makefile index b5048ab77b..2373a04f7a 100644 --- a/t/Makefile +++ b/t/Makefile @@ -6,16 +6,25 @@ -include ../config.mak.autogen -include ../config.mak -#GIT_TEST_OPTS=--verbose --debug +#GIT_TEST_OPTS = --verbose --debug SHELL_PATH ?= $(SHELL) PERL_PATH ?= /usr/bin/perl TAR ?= $(TAR) RM ?= rm -f PROVE ?= prove DEFAULT_TEST_TARGET ?= test +TEST_LINT ?= test-lint-duplicates test-lint-executable + +ifdef TEST_OUTPUT_DIRECTORY +TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results +else +TEST_RESULTS_DIRECTORY = test-results +endif # Shell quote; SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) +PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH)) +TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY)) T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)) TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh)) @@ -28,20 +37,22 @@ test: pre-clean $(TEST_LINT) prove: pre-clean $(TEST_LINT) @echo "*** prove ***"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS) - $(MAKE) clean + $(MAKE) clean-except-prove-cache $(T): @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS) pre-clean: - $(RM) -r test-results + $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)' -clean: - $(RM) -r 'trash directory'.* test-results +clean-except-prove-cache: + $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)' $(RM) -r valgrind/bin + +clean: clean-except-prove-cache $(RM) .prove -test-lint: test-lint-duplicates test-lint-executable +test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax test-lint-duplicates: @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \ @@ -53,12 +64,15 @@ test-lint-executable: test -z "$$bad" || { \ echo >&2 "non-executable tests:" $$bad; exit 1; } +test-lint-shell-syntax: + @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) + aggregate-results-and-cleanup: $(T) $(MAKE) aggregate-results $(MAKE) clean aggregate-results: - for f in test-results/t*-*.counts; do \ + for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \ echo "$$f"; \ done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh @@ -73,4 +87,7 @@ gitweb-test: valgrind: $(MAKE) GIT_TEST_OPTS="$(GIT_TEST_OPTS) --valgrind" -.PHONY: pre-clean $(T) aggregate-results clean valgrind +perf: + $(MAKE) -C perf/ all + +.PHONY: pre-clean $(T) aggregate-results clean valgrind perf @@ -76,6 +76,11 @@ appropriately before running "make". command being run and their output if any are also output. +--verbose-only=<pattern>:: + Like --verbose, but the effect is limited to tests with + numbers matching <pattern>. The number matched against is + simply the running count of the test within the file. + --debug:: This may help the person who is developing a new test. It causes the command defined with test_debug to run. @@ -86,29 +91,46 @@ appropriately before running "make". --immediate:: This causes the test to immediately exit upon the first - failed test. + failed test. Cleanup commands requested with + test_when_finished are not executed if the test failed, + in order to keep the state for inspection by the tester + to diagnose the bug. --long-tests:: This causes additional long-running tests to be run (where available), for more exhaustive testing. ---valgrind:: - Execute all Git binaries with valgrind and exit with status - 126 on errors (just like regular tests, this will only stop - the test script when running under -i). Valgrind errors - go to stderr, so you might want to pass the -v option, too. +--valgrind=<tool>:: + Execute all Git binaries under valgrind tool <tool> and exit + with status 126 on errors (just like regular tests, this will + only stop the test script when running under -i). Since it makes no sense to run the tests with --valgrind and not see any output, this option implies --verbose. For convenience, it also implies --tee. - Note that valgrind is run with the option --leak-check=no, + <tool> defaults to 'memcheck', just like valgrind itself. + Other particularly useful choices include 'helgrind' and + 'drd', but you may use any tool recognized by your valgrind + installation. + + As a special case, <tool> can be 'memcheck-fast', which uses + memcheck but disables --track-origins. Use this if you are + running tests in bulk, to see if there are _any_ memory + issues. + + Note that memcheck is run with the option --leak-check=no, as the git process is short-lived and some errors are not interesting. In order to run a single command under the same conditions manually, you should set GIT_VALGRIND to point to the 't/valgrind/' directory and use the commands under 't/valgrind/bin/'. +--valgrind-only=<pattern>:: + Like --valgrind, but the effect is limited to tests with + numbers matching <pattern>. The number matched against is + simply the running count of the test within the file. + --tee:: In addition to printing the test output to the terminal, write it to files named 't/test-results/$TEST_NAME.out'. @@ -307,6 +329,32 @@ Don't: Use test_done instead if you need to stop the tests early (see "Skipping tests" below). + - use '! git cmd' when you want to make sure the git command exits + with failure in a controlled way by calling "die()". Instead, + use 'test_must_fail git cmd'. This will signal a failure if git + dies in an unexpected way (e.g. segfault). + + On the other hand, don't use test_must_fail for running regular + platform commands; just use '! cmd'. + + - use perl without spelling it as "$PERL_PATH". This is to help our + friends on Windows where the platform Perl often adds CR before + the end of line, and they bundle Git with a version of Perl that + does not do so, whose path is specified with $PERL_PATH. Note that we + provide a "perl" function which uses $PERL_PATH under the hood, so + you do not need to worry when simply running perl in the test scripts + (but you do, for example, on a shebang line or in a sub script + created via "write_script"). + + - use sh without spelling it as "$SHELL_PATH", when the script can + be misinterpreted by broken platform shell (e.g. Solaris). + + - chdir around in tests. It is not sufficient to chdir to + somewhere and then chdir back to the original location later in + the test, as any intermediate step can fail and abort the test, + causing the next test to start in an unexpected directory. Do so + inside a subshell if necessary. + - Break the TAP output The raw output from your test may be interpreted by a TAP harness. TAP @@ -342,9 +390,9 @@ If you need to skip tests you should do so by using the three-arg form of the test_* functions (see the "Test harness library" section below), e.g.: - test_expect_success PERL 'I need Perl' " - '$PERL_PATH' -e 'hlagh() if unf_unf()' - " + test_expect_success PERL 'I need Perl' ' + perl -e "hlagh() if unf_unf()" + ' The advantage of skipping tests like this is that platforms that don't have the PERL and other optional dependencies get an indication of how @@ -476,7 +524,7 @@ library for your script to use. test_external \ 'GitwebCache::*FileCache*' \ - "$PERL_PATH" "$TEST_DIRECTORY"/t9503/test_cache_interface.pl + perl "$TEST_DIRECTORY"/t9503/test_cache_interface.pl If the test is outputting its own TAP you should set the test_external_has_tap variable somewhere before calling the first @@ -492,7 +540,7 @@ library for your script to use. test_external_without_stderr \ 'Perl API' \ - "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl + perl "$TEST_DIRECTORY"/t9700/test.pl - test_expect_code <exit-code> <command> @@ -548,6 +596,33 @@ library for your script to use. ... ' + - test_pause + + This command is useful for writing and debugging tests and must be + removed before submitting. It halts the execution of the test and + spawns a shell in the trash directory. Exit the shell to continue + the test. Example: + + test_expect_success 'test' ' + git do-something >actual && + test_pause && + test_cmp expected actual + ' + + - test_ln_s_add <path1> <path2> + + This function helps systems whose filesystem does not support symbolic + links. Use it to add a symbolic link entry to the index when it is not + important that the file system entry is a symbolic link, i.e., instead + of the sequence + + ln -s foo bar && + git add bar + + Sometimes it is possible to split a test in a part that does not need + the symbolic link in the file system and a part that does; then only + the latter part need be protected by a SYMLINKS prerequisite (see below). + Prerequisites ------------- @@ -558,11 +633,18 @@ See the prereq argument to the test_* functions in the "Test harness library" section above and the "test_have_prereq" function for how to use these, and "test_set_prereq" for how to define your own. - - PERL & PYTHON + - PYTHON + + Git wasn't compiled with NO_PYTHON=YesPlease. Wrap any tests that + need Python with this. + + - PERL + + Git wasn't compiled with NO_PERL=YesPlease. - Git wasn't compiled with NO_PERL=YesPlease or - NO_PYTHON=YesPlease. Wrap any tests that need Perl or Python in - these. + Even without the PERL prerequisite, tests can assume there is a + usable perl interpreter at $PERL_PATH, though it need not be + particularly modern. - POSIXPERM @@ -578,6 +660,11 @@ use these, and "test_set_prereq" for how to define your own. The process retains the same pid across exec(2). See fb9a2bea for details. + - PIPE + + The filesystem we're on supports creation of FIFOs (named pipes) + via mkfifo(1). + - SYMLINKS The filesystem we're on supports symbolic links. E.g. a FAT @@ -593,6 +680,15 @@ use these, and "test_set_prereq" for how to define your own. Git was compiled with USE_LIBPCRE=YesPlease. Wrap any tests that use git-grep --perl-regexp or git-grep -P in these. + - CASE_INSENSITIVE_FS + + Test is run on a case insensitive file system. + + - UTF8_NFD_TO_NFC + + Test is run on a filesystem which converts decomposed utf-8 (nfd) + to precomposed utf-8 (nfc). + Tips for Writing Tests ---------------------- diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh index c56a77d237..c9d105d707 100644 --- a/t/annotate-tests.sh +++ b/t/annotate-tests.sh @@ -2,11 +2,21 @@ # sourced from t8001-annotate.sh and t8002-blame.sh. check_count () { - head= - case "$1" in -h) head="$2"; shift; shift ;; esac - echo "$PROG file $head" >&4 - $PROG file $head >.result || return 1 - cat .result | perl -e ' + head= && + file='file' && + options= && + while : + do + case "$1" in + -h) head="$2"; shift; shift ;; + -f) file="$2"; shift; shift ;; + -*) options="$options $1"; shift ;; + *) break ;; + esac + done && + echo "$PROG $options $file $head" >&4 && + $PROG $options $file $head >actual && + perl -e ' my %expect = (@ARGV); my %count = map { $_ => 0 } keys %expect; while (<STDIN>) { @@ -31,107 +41,492 @@ check_count () { print STDERR "Author $author (expected $value, attributed $count) $ok\n"; } exit($bad); - ' "$@" + ' "$@" <actual } -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_AUTHOR_EMAIL="A@test.git" 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_AUTHOR_EMAIL="B@test.git" 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_AUTHOR_EMAIL="B1@test.git" 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_AUTHOR_EMAIL="B2@test.git" 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 && - 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_AUTHOR_EMAIL="C@test.git" 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 file.orig && - { - cat file.orig && - echo - } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" > file && - echo "incomplete" | tr -d "\\012" >>file && - GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" 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' - -test_expect_success \ - 'an obfuscated email added' \ - 'echo "No robots allowed" > file.new && - cat file >> file.new && - mv file.new file && - GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" git commit -a -m "norobots"' - -test_expect_success \ - 'obfuscated email parsed' \ - 'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1' +test_expect_success 'setup A lines' ' + echo "1A quick brown fox jumps over the" >file && + echo "lazy dog" >>file && + git add file && + GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \ + git commit -a -m "Initial." +' + +test_expect_success 'blame 1 author' ' + check_count A 2 +' + +test_expect_success 'setup B lines' ' + echo "2A quick brown fox jumps over the" >>file && + echo "lazy dog" >>file && + GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \ + git commit -a -m "Second." +' + +test_expect_success 'blame 2 authors' ' + check_count A 2 B 2 +' + +test_expect_success 'setup B1 lines (branch1)' ' + git checkout -b branch1 master && + echo "3A slow green fox jumps into the" >>file && + echo "well." >>file && + GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" \ + git commit -a -m "Branch1-1" +' + +test_expect_success 'blame 2 authors + 1 branch1 author' ' + check_count A 2 B 2 B1 2 +' + +test_expect_success 'setup B2 lines (branch2)' ' + 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_AUTHOR_EMAIL="B2@test.git" \ + git commit -a -m "Branch2-1" +' + +test_expect_success 'blame 2 authors + 1 branch2 author' ' + check_count A 2 B 1 B2 1 +' + +test_expect_success 'merge branch1 & branch2' ' + git merge branch1 +' + +test_expect_success 'blame 2 authors + 2 merged-in authors' ' + check_count A 2 B 1 B1 2 B2 1 +' + +test_expect_success 'blame ancestor' ' + check_count -h master A 2 B 2 +' + +test_expect_success 'blame great-ancestor' ' + check_count -h master^ A 2 +' + +test_expect_success 'setup evil merge' ' + echo "evil merge." >>file && + git commit -a --amend +' + +test_expect_success 'blame evil merge' ' + check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 +' + +test_expect_success 'setup incomplete line' ' + echo "incomplete" | tr -d "\\012" >>file && + GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \ + git commit -a -m "Incomplete" +' + +test_expect_success 'blame incomplete line' ' + check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1 +' + +test_expect_success 'setup edits' ' + mv file file.orig && + { + cat file.orig && + echo + } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" >file && + echo "incomplete" | tr -d "\\012" >>file && + GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" \ + git commit -a -m "edit" +' + +test_expect_success 'blame edits' ' + check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 +' + +test_expect_success 'setup obfuscated email' ' + echo "No robots allowed" >file.new && + cat file >>file.new && + mv file.new file && + GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" \ + git commit -a -m "norobots" +' + +test_expect_success 'blame obfuscated email' ' + check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1 +' + +test_expect_success 'blame -L 1 (all)' ' + check_count -L1 A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1 +' + +test_expect_success 'blame -L , (all)' ' + check_count -L, A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1 +' + +test_expect_success 'blame -L X (X to end)' ' + check_count -L5 B1 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L X, (X to end)' ' + check_count -L5, B1 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L ,Y (up to Y)' ' + check_count -L,3 A 1 B2 1 E 1 +' + +test_expect_success 'blame -L X,X' ' + check_count -L3,3 B2 1 +' + +test_expect_success 'blame -L X,Y' ' + check_count -L3,6 B 1 B1 1 B2 1 D 1 +' + +test_expect_success 'blame -L Y,X (undocumented)' ' + check_count -L6,3 B 1 B1 1 B2 1 D 1 +' + +test_expect_success 'blame -L -X' ' + test_must_fail $PROG -L-1 file +' + +test_expect_success 'blame -L 0' ' + test_must_fail $PROG -L0 file +' + +test_expect_success 'blame -L ,0' ' + test_must_fail $PROG -L,0 file +' + +test_expect_success 'blame -L ,+0' ' + test_must_fail $PROG -L,+0 file +' + +test_expect_success 'blame -L X,+0' ' + test_must_fail $PROG -L1,+0 file +' + +test_expect_success 'blame -L X,+1' ' + check_count -L3,+1 B2 1 +' + +test_expect_success 'blame -L X,+N' ' + check_count -L3,+4 B 1 B1 1 B2 1 D 1 +' + +test_expect_success 'blame -L ,-0' ' + test_must_fail $PROG -L,-0 file +' + +test_expect_success 'blame -L X,-0' ' + test_must_fail $PROG -L1,-0 file +' + +test_expect_success 'blame -L X,-1' ' + check_count -L3,-1 B2 1 +' + +test_expect_success 'blame -L X,-N' ' + check_count -L6,-4 B 1 B1 1 B2 1 D 1 +' + +test_expect_success 'blame -L /RE/ (RE to end)' ' + check_count -L/evil/ C 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/,/RE2/' ' + check_count -L/robot/,/green/ A 1 B 1 B2 1 D 1 E 1 +' + +test_expect_success 'blame -L X,/RE/' ' + check_count -L5,/evil/ B1 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/,Y' ' + check_count -L/99/,7 B1 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/,+N' ' + check_count -L/99/,+3 B1 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/,-N' ' + check_count -L/99/,-3 B 1 B2 1 D 1 +' + +# 'file' ends with an incomplete line, so 'wc' reports one fewer lines than +# git-blame sees, hence the last line is actually $(wc...)+1. +test_expect_success 'blame -L X (X == nlines)' ' + n=$(expr $(wc -l <file) + 1) && + check_count -L$n C 1 +' + +test_expect_success 'blame -L X (X == nlines + 1)' ' + n=$(expr $(wc -l <file) + 2) && + test_must_fail $PROG -L$n file +' + +test_expect_success 'blame -L X (X > nlines)' ' + test_must_fail $PROG -L12345 file +' + +test_expect_success 'blame -L ,Y (Y == nlines)' ' + n=$(expr $(wc -l <file) + 1) && + check_count -L,$n A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1 +' + +test_expect_success 'blame -L ,Y (Y == nlines + 1)' ' + n=$(expr $(wc -l <file) + 2) && + test_must_fail $PROG -L,$n file +' + +test_expect_success 'blame -L ,Y (Y > nlines)' ' + test_must_fail $PROG -L,12345 file +' + +test_expect_success 'blame -L multiple (disjoint)' ' + check_count -L2,3 -L6,7 A 1 B1 1 B2 1 "A U Thor" 1 +' + +test_expect_success 'blame -L multiple (disjoint: unordered)' ' + check_count -L6,7 -L2,3 A 1 B1 1 B2 1 "A U Thor" 1 +' + +test_expect_success 'blame -L multiple (adjacent)' ' + check_count -L2,3 -L4,5 A 1 B 1 B2 1 D 1 +' + +test_expect_success 'blame -L multiple (adjacent: unordered)' ' + check_count -L4,5 -L2,3 A 1 B 1 B2 1 D 1 +' + +test_expect_success 'blame -L multiple (overlapping)' ' + check_count -L2,4 -L3,5 A 1 B 1 B2 1 D 1 +' + +test_expect_success 'blame -L multiple (overlapping: unordered)' ' + check_count -L3,5 -L2,4 A 1 B 1 B2 1 D 1 +' + +test_expect_success 'blame -L multiple (superset/subset)' ' + check_count -L2,8 -L3,5 A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L multiple (superset/subset: unordered)' ' + check_count -L3,5 -L2,8 A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/ (relative)' ' + check_count -L3,3 -L/fox/ B1 1 B2 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/ (relative: no preceding range)' ' + check_count -L/dog/ A 1 B 1 B1 1 B2 1 C 1 D 1 "A U Thor" 1 +' + +test_expect_success 'blame -L /RE/ (relative: adjacent)' ' + check_count -L1,1 -L/dog/,+1 A 1 E 1 +' + +test_expect_success 'blame -L /RE/ (relative: not found)' ' + test_must_fail $PROG -L4,4 -L/dog/ file +' + +test_expect_success 'blame -L /RE/ (relative: end-of-file)' ' + test_must_fail $PROG -L, -L/$/ file +' + +test_expect_success 'blame -L ^/RE/ (absolute)' ' + check_count -L3,3 -L^/dog/,+2 A 1 B2 1 +' + +test_expect_success 'blame -L ^/RE/ (absolute: no preceding range)' ' + check_count -L^/dog/,+2 A 1 B2 1 +' + +test_expect_success 'blame -L ^/RE/ (absolute: not found)' ' + test_must_fail $PROG -L4,4 -L^/tambourine/ file +' + +test_expect_success 'blame -L ^/RE/ (absolute: end-of-file)' ' + n=$(expr $(wc -l <file) + 1) && + check_count -L$n -L^/$/,+2 A 1 C 1 E 1 +' + +test_expect_success 'setup -L :regex' ' + tr Q "\\t" >hello.c <<-\EOF && + int main(int argc, const char *argv[]) + { + Qputs("hello"); + } + EOF + git add hello.c && + GIT_AUTHOR_NAME="F" GIT_AUTHOR_EMAIL="F@test.git" \ + git commit -m "hello" && + + mv hello.c hello.orig && + sed -e "/}/ {x; s/$/Qputs(\"goodbye\");/; G;}" <hello.orig | + tr Q "\\t" >hello.c && + GIT_AUTHOR_NAME="G" GIT_AUTHOR_EMAIL="G@test.git" \ + git commit -a -m "goodbye" && + + mv hello.c hello.orig && + echo "#include <stdio.h>" >hello.c && + cat hello.orig >>hello.c && + tr Q "\\t" >>hello.c <<-\EOF + void mail() + { + Qputs("mail"); + } + EOF + GIT_AUTHOR_NAME="H" GIT_AUTHOR_EMAIL="H@test.git" \ + git commit -a -m "mail" +' + +test_expect_success 'blame -L :literal' ' + check_count -f hello.c -L:main F 4 G 1 +' + +test_expect_success 'blame -L :regex' ' + check_count -f hello.c "-L:m[a-z][a-z]l" H 4 +' + +test_expect_success 'blame -L :nomatch' ' + test_must_fail $PROG -L:nomatch hello.c +' + +test_expect_success 'blame -L :RE (relative)' ' + check_count -f hello.c -L3,3 -L:ma.. F 1 H 4 +' + +test_expect_success 'blame -L :RE (relative: no preceding range)' ' + check_count -f hello.c -L:ma.. F 4 G 1 +' + +test_expect_success 'blame -L :RE (relative: not found)' ' + test_must_fail $PROG -L3,3 -L:tambourine hello.c +' + +test_expect_success 'blame -L :RE (relative: end-of-file)' ' + test_must_fail $PROG -L, -L:main hello.c +' + +test_expect_success 'blame -L ^:RE (absolute)' ' + check_count -f hello.c -L3,3 -L^:ma.. F 4 G 1 +' + +test_expect_success 'blame -L ^:RE (absolute: no preceding range)' ' + check_count -f hello.c -L^:ma.. F 4 G 1 +' + +test_expect_success 'blame -L ^:RE (absolute: not found)' ' + test_must_fail $PROG -L4,4 -L^:tambourine hello.c +' + +test_expect_success 'blame -L ^:RE (absolute: end-of-file)' ' + n=$(printf "%d" $(wc -l <hello.c)) && + check_count -f hello.c -L$n -L^:ma.. F 4 G 1 H 1 +' + +test_expect_success 'setup incremental' ' + ( + GIT_AUTHOR_NAME=I && + export GIT_AUTHOR_NAME && + GIT_AUTHOR_EMAIL=I@test.git && + export GIT_AUTHOR_EMAIL && + >incremental && + git add incremental && + git commit -m "step 0" && + printf "partial" >>incremental && + git commit -a -m "step 0.5" && + echo >>incremental && + git commit -a -m "step 1" + ) +' + +test_expect_success 'blame empty' ' + check_count -h HEAD^^ -f incremental +' + +test_expect_success 'blame -L 0 empty' ' + test_must_fail $PROG -L0 incremental HEAD^^ +' + +test_expect_success 'blame -L 1 empty' ' + test_must_fail $PROG -L1 incremental HEAD^^ +' + +test_expect_success 'blame -L 2 empty' ' + test_must_fail $PROG -L2 incremental HEAD^^ +' + +test_expect_success 'blame half' ' + check_count -h HEAD^ -f incremental I 1 +' + +test_expect_success 'blame -L 0 half' ' + test_must_fail $PROG -L0 incremental HEAD^ +' + +test_expect_success 'blame -L 1 half' ' + check_count -h HEAD^ -f incremental -L1 I 1 +' + +test_expect_success 'blame -L 2 half' ' + test_must_fail $PROG -L2 incremental HEAD^ +' + +test_expect_success 'blame -L 3 half' ' + test_must_fail $PROG -L3 incremental HEAD^ +' + +test_expect_success 'blame full' ' + check_count -f incremental I 1 +' + +test_expect_success 'blame -L 0 full' ' + test_must_fail $PROG -L0 incremental +' + +test_expect_success 'blame -L 1 full' ' + check_count -f incremental -L1 I 1 +' + +test_expect_success 'blame -L 2 full' ' + test_must_fail $PROG -L2 incremental +' + +test_expect_success 'blame -L 3 full' ' + test_must_fail $PROG -L3 incremental +' + +test_expect_success 'blame -L' ' + test_must_fail $PROG -L file +' + +test_expect_success 'blame -L X,+' ' + test_must_fail $PROG -L1,+ file +' + +test_expect_success 'blame -L X,-' ' + test_must_fail $PROG -L1,- file +' + +test_expect_success 'blame -L X (non-numeric X)' ' + test_must_fail $PROG -LX file +' + +test_expect_success 'blame -L X,Y (non-numeric Y)' ' + test_must_fail $PROG -L1,Y file +' + +test_expect_success 'blame -L X,+N (non-numeric N)' ' + test_must_fail $PROG -L1,+N file +' + +test_expect_success 'blame -L X,-N (non-numeric N)' ' + test_must_fail $PROG -L1,-N file +' + +test_expect_success 'blame -L ,^/RE/' ' + test_must_fail $PROG -L1,^/99/ file +' diff --git a/t/check-non-portable-shell.pl b/t/check-non-portable-shell.pl new file mode 100755 index 0000000000..45971f43b7 --- /dev/null +++ b/t/check-non-portable-shell.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl + +# Test t0000..t9999.sh for non portable shell scripts +# This script can be called with one or more filenames as parameters + +use strict; +use warnings; + +my $exit_code=0; + +sub err { + my $msg = shift; + print "$ARGV:$.: error: $msg: $_\n"; + $exit_code = 1; +} + +while (<>) { + chomp; + /^\s*sed\s+-i/ and err 'sed -i is not portable'; + /^\s*echo\s+-n/ and err 'echo -n is not portable (please use printf)'; + /^\s*declare\s+/ and err 'arrays/declare not portable'; + /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)'; + /test\s+[^=]*==/ and err '"test a == b" is not portable (please use =)'; + /^\s*export\s+[^=]*=/ and err '"export FOO=bar" is not portable (please use FOO=bar && export FOO)'; + # this resets our $. for each file + close ARGV if eof; +} +exit $exit_code; diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh index 21d11d6c2d..8cf909a6c5 100644 --- a/t/gitweb-lib.sh +++ b/t/gitweb-lib.sh @@ -36,7 +36,7 @@ EOF # You can set the GITWEB_TEST_INSTALLED environment variable to # the gitwebdir (the directory where gitweb is installed / deployed to) - # of an existing gitweb instalation to test that installation, + # of an existing gitweb installation to test that installation, # or simply to pathname of installed gitweb script. if test -n "$GITWEB_TEST_INSTALLED" ; then if test -d $GITWEB_TEST_INSTALLED; then diff --git a/t/lib-bash.sh b/t/lib-bash.sh new file mode 100644 index 0000000000..11397f747b --- /dev/null +++ b/t/lib-bash.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# +# Ensures that tests are run under Bash; primarily intended for running tests +# of the completion script. + +if test -n "$BASH" && test -z "$POSIXLY_CORRECT"; then + # we are in full-on bash mode + true +elif type bash >/dev/null 2>&1; then + # execute in full-on bash mode + unset POSIXLY_CORRECT + exec bash "$0" "$@" +else + echo '1..0 #SKIP skipping bash completion tests; bash not available' + exit 0 +fi + +. ./test-lib.sh diff --git a/t/lib-credential.sh b/t/lib-credential.sh index 4a37cd79e5..957ae936e8 100755 --- a/t/lib-credential.sh +++ b/t/lib-credential.sh @@ -4,10 +4,20 @@ # stdout and stderr should be provided on stdin, # separated by "--". check() { + credential_opts= + credential_cmd=$1 + shift + for arg in "$@"; do + credential_opts="$credential_opts -c credential.helper='$arg'" + done read_chunk >stdin && read_chunk >expect-stdout && read_chunk >expect-stderr && - test-credential "$@" <stdin >stdout 2>stderr && + if ! eval "git $credential_opts credential $credential_cmd <stdin >stdout 2>stderr"; then + echo "git credential failed with code $?" && + cat stderr && + false + fi && test_cmp expect-stdout stdout && test_cmp expect-stderr stderr } @@ -41,7 +51,7 @@ reject() { echo protocol=$2 echo host=$3 echo username=$4 - ) | test-credential reject $1 + ) | git -c credential.helper=$1 credential reject } helper_test() { @@ -52,6 +62,8 @@ helper_test() { protocol=https host=example.com -- + protocol=https + host=example.com username=askpass-username password=askpass-password -- @@ -74,6 +86,8 @@ helper_test() { protocol=https host=example.com -- + protocol=https + host=example.com username=store-user password=store-pass -- @@ -85,6 +99,8 @@ helper_test() { protocol=http host=example.com -- + protocol=http + host=example.com username=askpass-username password=askpass-password -- @@ -98,6 +114,8 @@ helper_test() { protocol=https host=other.tld -- + protocol=https + host=other.tld username=askpass-username password=askpass-password -- @@ -112,6 +130,8 @@ helper_test() { host=example.com username=other -- + protocol=https + host=example.com username=other password=askpass-password -- @@ -133,6 +153,9 @@ helper_test() { host=path.tld path=bar.git -- + protocol=http + host=path.tld + path=bar.git username=askpass-username password=askpass-password -- @@ -150,6 +173,8 @@ helper_test() { protocol=https host=example.com -- + protocol=https + host=example.com username=askpass-username password=askpass-password -- @@ -176,6 +201,8 @@ helper_test() { host=example.com username=user1 -- + protocol=https + host=example.com username=user1 password=pass1 EOF @@ -184,6 +211,8 @@ helper_test() { host=example.com username=user2 -- + protocol=https + host=example.com username=user2 password=pass2 EOF @@ -200,6 +229,8 @@ helper_test() { host=example.com username=user1 -- + protocol=https + host=example.com username=user1 password=askpass-password -- @@ -213,6 +244,8 @@ helper_test() { host=example.com username=user2 -- + protocol=https + host=example.com username=user2 password=pass2 EOF @@ -234,6 +267,8 @@ helper_test_timeout() { protocol=https host=timeout.tld -- + protocol=https + host=timeout.tld username=askpass-username password=askpass-password -- diff --git a/t/lib-gettext.sh b/t/lib-gettext.sh index 0f76f6cdc0..ae8883a075 100644 --- a/t/lib-gettext.sh +++ b/t/lib-gettext.sh @@ -14,12 +14,14 @@ export GIT_TEXTDOMAINDIR GIT_PO_PATH if test_have_prereq GETTEXT && ! test_have_prereq GETTEXT_POISON then # is_IS.UTF-8 on Solaris and FreeBSD, is_IS.utf8 on Debian - is_IS_locale=$(locale -a | sed -n '/^is_IS\.[uU][tT][fF]-*8$/{ + is_IS_locale=$(locale -a 2>/dev/null | + sed -n '/^is_IS\.[uU][tT][fF]-*8$/{ p q }') # is_IS.ISO8859-1 on Solaris and FreeBSD, is_IS.iso88591 on Debian - is_IS_iso_locale=$(locale -a | sed -n '/^is_IS\.[iI][sS][oO]8859-*1$/{ + is_IS_iso_locale=$(locale -a 2>/dev/null | + sed -n '/^is_IS\.[iI][sS][oO]8859-*1$/{ p q }') diff --git a/t/lib-git-daemon.sh b/t/lib-git-daemon.sh new file mode 100644 index 0000000000..87f0ad8f41 --- /dev/null +++ b/t/lib-git-daemon.sh @@ -0,0 +1,69 @@ +#!/bin/sh + +if test -z "$GIT_TEST_GIT_DAEMON" +then + skip_all="git-daemon testing disabled (define GIT_TEST_GIT_DAEMON to enable)" + test_done +fi + +LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-'8121'} + +GIT_DAEMON_PID= +GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo +GIT_DAEMON_URL=git://127.0.0.1:$LIB_GIT_DAEMON_PORT + +start_git_daemon() { + if test -n "$GIT_DAEMON_PID" + then + error "start_git_daemon already called" + fi + + mkdir -p "$GIT_DAEMON_DOCUMENT_ROOT_PATH" + + trap 'code=$?; stop_git_daemon; (exit $code); die' EXIT + + say >&3 "Starting git daemon ..." + mkfifo git_daemon_output + git daemon --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \ + --reuseaddr --verbose \ + --base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \ + "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \ + >&3 2>git_daemon_output & + GIT_DAEMON_PID=$! + { + read line <&7 + echo >&4 "$line" + cat <&7 >&4 & + } 7<git_daemon_output && + + # Check expected output + if test x"$(expr "$line" : "\[[0-9]*\] \(.*\)")" != x"Ready to rumble" + then + kill "$GIT_DAEMON_PID" + wait "$GIT_DAEMON_PID" + trap 'die' EXIT + error "git daemon failed to start" + fi +} + +stop_git_daemon() { + if test -z "$GIT_DAEMON_PID" + then + return + fi + + trap 'die' EXIT + + # kill git-daemon child of git + say >&3 "Stopping git daemon ..." + kill "$GIT_DAEMON_PID" + wait "$GIT_DAEMON_PID" >&3 2>&4 + ret=$? + # expect exit with status 143 = 128+15 for signal TERM=15 + if test $ret -ne 143 + then + error "git daemon exited with status: $ret" + fi + GIT_DAEMON_PID= + rm -f git_daemon_output +} diff --git a/t/lib-git-p4.sh b/t/lib-git-p4.sh index a870f9a5d2..ccd918e79e 100644 --- a/t/lib-git-p4.sh +++ b/t/lib-git-p4.sh @@ -1,19 +1,40 @@ # -# Library code for git-p4 tests +# Library code for git p4 tests # +# p4 tests never use the top-level repo; always build/clone into +# a subdirectory called "$git" +TEST_NO_CREATE_REPO=NoThanks + . ./test-lib.sh -if ! test_have_prereq PYTHON; then - skip_all='skipping git-p4 tests; python not available' +if ! test_have_prereq PYTHON +then + skip_all='skipping git p4 tests; python not available' test_done fi ( p4 -h && p4d -h ) >/dev/null 2>&1 || { - skip_all='skipping git-p4 tests; no p4 or p4d' + skip_all='skipping git p4 tests; no p4 or p4d' test_done } -GITP4="$GIT_BUILD_DIR/contrib/fast-import/git-p4" +# On cygwin, the NT version of Perforce can be used. When giving +# it paths, either on the command-line or in client specifications, +# be sure to use the native windows form. +# +# Older versions of perforce were available compiled natively for +# cygwin. Those do not accept native windows paths, so make sure +# not to convert for them. +native_path() { + path="$1" && + if test_have_prereq CYGWIN && ! p4 -V | grep -q CYGWIN + then + path=$(cygpath --windows "$path") + else + path=$(test-path-utils real_path "$path") + fi && + echo "$path" +} # Try to pick a unique port: guess a large number, then hope # no more than one of each test is running. @@ -24,8 +45,11 @@ testid=${this_test#t} git_p4_test_start=9800 P4DPORT=$((10669 + ($testid - $git_p4_test_start))) -export P4PORT=localhost:$P4DPORT -export P4CLIENT=client +P4PORT=localhost:$P4DPORT +P4CLIENT=client +P4EDITOR=: +unset P4CHARSET +export P4PORT P4CLIENT P4EDITOR P4CHARSET db="$TRASH_DIRECTORY/db" cli="$TRASH_DIRECTORY/cli" @@ -34,26 +58,48 @@ pidfile="$TRASH_DIRECTORY/p4d.pid" start_p4d() { mkdir -p "$db" "$cli" "$git" && + rm -f "$pidfile" && ( - p4d -q -r "$db" -p $P4DPORT & - echo $! >"$pidfile" + cd "$db" && + { + p4d -q -p $P4DPORT & + echo $! >"$pidfile" + } ) && - for i in 1 2 3 4 5 ; do - p4 info >/dev/null 2>&1 && break || true && - echo waiting for p4d to start && + + # This gives p4d a long time to start up, as it can be + # quite slow depending on the machine. Set this environment + # variable to something smaller to fail faster in, say, + # an automated test setup. If the p4d process dies, that + # will be caught with the "kill -0" check below. + i=${P4D_START_PATIENCE:-300} + pid=$(cat "$pidfile") + ready= + while test $i -gt 0 + do + # succeed when p4 client commands start to work + if p4 info >/dev/null 2>&1 + then + ready=true + break + fi + # fail if p4d died + kill -0 $pid 2>/dev/null || break + echo waiting for p4d to start sleep 1 - done && - # complain if it never started - p4 info >/dev/null && - ( - cd "$cli" && - p4 client -i <<-EOF - Client: client - Description: client - Root: $cli - View: //depot/... //client/... - EOF - ) + i=$(( $i - 1 )) + done + + if test -z "$ready" + then + # p4d failed to start + return 1 + fi + + # build a client + client_view "//depot/... //client/..." && + + return 0 } kill_p4d() { @@ -70,5 +116,49 @@ kill_p4d() { } cleanup_git() { - rm -rf "$git" + rm -rf "$git" && + mkdir "$git" +} + +marshal_dump() { + what=$1 && + line=${2:-1} && + cat >"$TRASH_DIRECTORY/marshal-dump.py" <<-EOF && + import marshal + import sys + for i in range($line): + d = marshal.load(sys.stdin) + print d['$what'] + EOF + "$PYTHON_PATH" "$TRASH_DIRECTORY/marshal-dump.py" +} + +# +# Construct a client with this list of View lines +# +client_view() { + ( + cat <<-EOF && + Client: $P4CLIENT + Description: $P4CLIENT + Root: $cli + AltRoots: $(native_path "$cli") + LineEnd: unix + View: + EOF + printf "\t%s\n" "$@" + ) | p4 client -i +} + +is_cli_file_writeable() { + # cygwin version of p4 does not set read-only attr, + # will be marked 444 but -w is true + file="$1" && + if test_have_prereq CYGWIN && p4 -V | grep -q CYGWIN + then + stat=$(stat --format=%a "$file") && + test $stat = 644 + else + test -w "$file" + fi } diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh index 199f22c231..b0ec12ff6c 100644 --- a/t/lib-git-svn.sh +++ b/t/lib-git-svn.sh @@ -29,7 +29,7 @@ export svnrepo svnconf=$PWD/svnconf export svnconf -"$PERL_PATH" -w -e " +perl -w -e " use SVN::Core; use SVN::Repos; \$SVN::Core::VERSION gt '1.1.0' or exit(42); @@ -146,9 +146,9 @@ stop_httpd () { } convert_to_rev_db () { - "$PERL_PATH" -w -- - "$@" <<\EOF + perl -w -- - "$@" <<\EOF use strict; -@ARGV == 2 or die "Usage: convert_to_rev_db <input> <output>"; +@ARGV == 2 or die "usage: convert_to_rev_db <input> <output>"; open my $wr, '+>', $ARGV[1] or die "$!: couldn't open: $ARGV[1]"; open my $rd, '<', $ARGV[0] or die "$!: couldn't open: $ARGV[0]"; my $size = (stat($rd))[7]; diff --git a/t/lib-gpg/pubring.gpg b/t/lib-gpg/pubring.gpg Binary files differindex 83855fa4e1..1a3c2d487c 100644 --- a/t/lib-gpg/pubring.gpg +++ b/t/lib-gpg/pubring.gpg diff --git a/t/lib-gpg/random_seed b/t/lib-gpg/random_seed Binary files differindex 8fed1339ed..95d249f15f 100644 --- a/t/lib-gpg/random_seed +++ b/t/lib-gpg/random_seed diff --git a/t/lib-gpg/secring.gpg b/t/lib-gpg/secring.gpg Binary files differindex d831cd9eb3..82dca8f80b 100644 --- a/t/lib-gpg/secring.gpg +++ b/t/lib-gpg/secring.gpg diff --git a/t/lib-gpg/trustdb.gpg b/t/lib-gpg/trustdb.gpg Binary files differindex abace962b8..4879ae9a84 100644 --- a/t/lib-gpg/trustdb.gpg +++ b/t/lib-gpg/trustdb.gpg diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh index f7dc0781d5..ad8f1ef71e 100644 --- a/t/lib-httpd.sh +++ b/t/lib-httpd.sh @@ -43,6 +43,10 @@ TEST_PATH="$TEST_DIRECTORY"/lib-httpd HTTPD_ROOT_PATH="$PWD"/httpd HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www +# hack to suppress apache PassEnv warnings +GIT_VALGRIND=$GIT_VALGRIND; export GIT_VALGRIND +GIT_VALGRIND_OPTIONS=$GIT_VALGRIND_OPTIONS; export GIT_VALGRIND_OPTIONS + if ! test -x "$LIB_HTTPD_PATH" then skip_all="skipping test, no web server found at '$LIB_HTTPD_PATH'" @@ -76,6 +80,7 @@ fi prepare_httpd() { mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH" cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH" + cp "$TEST_PATH"/broken-smart-http.sh "$HTTPD_ROOT_PATH" ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules" @@ -136,10 +141,11 @@ stop_httpd() { -f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop } -test_http_push_nonff() { +test_http_push_nonff () { REMOTE_REPO=$1 LOCAL_REPO=$2 BRANCH=$3 + EXPECT_CAS_RESULT=${4-failure} test_expect_success 'non-fast-forward push fails' ' cd "$REMOTE_REPO" && @@ -160,6 +166,62 @@ test_http_push_nonff() { ' test_expect_success 'non-fast-forward push shows help message' ' - test_i18ngrep "To prevent you from losing history, non-fast-forward updates were rejected" output + test_i18ngrep "Updates were rejected because" output + ' + + test_expect_${EXPECT_CAS_RESULT} 'force with lease aka cas' ' + HEAD=$( cd "$REMOTE_REPO" && git rev-parse --verify HEAD ) && + test_when_finished '\'' + (cd "$REMOTE_REPO" && git update-ref HEAD "$HEAD") + '\'' && + ( + cd "$LOCAL_REPO" && + git push -v --force-with-lease=$BRANCH:$HEAD origin + ) && + git rev-parse --verify "$BRANCH" >expect && + ( + cd "$REMOTE_REPO" && git rev-parse --verify HEAD + ) >actual && + test_cmp expect actual + ' +} + +setup_askpass_helper() { + test_expect_success 'setup askpass helper' ' + write_script "$TRASH_DIRECTORY/askpass" <<-\EOF && + echo >>"$TRASH_DIRECTORY/askpass-query" "askpass: $*" && + cat "$TRASH_DIRECTORY/askpass-response" + EOF + GIT_ASKPASS="$TRASH_DIRECTORY/askpass" && + export GIT_ASKPASS && + export TRASH_DIRECTORY ' } + +set_askpass() { + >"$TRASH_DIRECTORY/askpass-query" && + echo "$*" >"$TRASH_DIRECTORY/askpass-response" +} + +expect_askpass() { + dest=$HTTPD_DEST${3+/$3} + + { + case "$1" in + none) + ;; + pass) + echo "askpass: Password for 'http://$2@$dest': " + ;; + both) + echo "askpass: Username for 'http://$dest': " + echo "askpass: Password for 'http://$2@$dest': " + ;; + *) + false + ;; + esac + } >"$TRASH_DIRECTORY/askpass-expect" && + test_cmp "$TRASH_DIRECTORY/askpass-expect" \ + "$TRASH_DIRECTORY/askpass-query" +} diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index 3c12b05d60..3a03e8263d 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -1,5 +1,4 @@ ServerName dummy -LockFile accept.lock PidFile httpd.pid DocumentRoot www LogFormat "%h %l %u %t \"%r\" %>s %b" common @@ -23,6 +22,13 @@ ErrorLog error.log <IfModule !mod_version.c> LoadModule version_module modules/mod_version.so </IfModule> +<IfModule !mod_headers.c> + LoadModule headers_module modules/mod_headers.so +</IfModule> + +<IfVersion < 2.4> +LockFile accept.lock +</IfVersion> <IfVersion < 2.1> <IfModule !mod_auth.c> @@ -40,23 +46,63 @@ ErrorLog error.log <IfModule !mod_authz_user.c> LoadModule authz_user_module modules/mod_authz_user.so </IfModule> +<IfModule !mod_authz_host.c> + LoadModule authz_host_module modules/mod_authz_host.so +</IfModule> </IfVersion> +<IfVersion >= 2.4> +<IfModule !mod_authn_core.c> + LoadModule authn_core_module modules/mod_authn_core.so +</IfModule> +<IfModule !mod_authz_core.c> + LoadModule authz_core_module modules/mod_authz_core.so +</IfModule> +<IfModule !mod_access_compat.c> + LoadModule access_compat_module modules/mod_access_compat.so +</IfModule> +<IfModule !mod_mpm_prefork.c> + LoadModule mpm_prefork_module modules/mod_mpm_prefork.so +</IfModule> +</IfVersion> + +PassEnv GIT_VALGRIND +PassEnv GIT_VALGRIND_OPTIONS + Alias /dumb/ www/ -Alias /auth/ www/auth/ +Alias /auth/dumb/ www/auth/dumb/ -<Location /smart/> +<LocationMatch /smart/> SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} SetEnv GIT_HTTP_EXPORT_ALL -</Location> -<Location /smart_noexport/> +</LocationMatch> +<LocationMatch /smart_noexport/> SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} -</Location> -ScriptAlias /smart/ ${GIT_EXEC_PATH}/git-http-backend/ -ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/ +</LocationMatch> +<LocationMatch /smart_custom_env/> + SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} + SetEnv GIT_HTTP_EXPORT_ALL + SetEnv GIT_COMMITTER_NAME "Custom User" + SetEnv GIT_COMMITTER_EMAIL custom@example.com +</LocationMatch> +<LocationMatch /smart_namespace/> + SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} + SetEnv GIT_HTTP_EXPORT_ALL + SetEnv GIT_NAMESPACE ns +</LocationMatch> +<LocationMatch /smart_cookies/> + SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH} + SetEnv GIT_HTTP_EXPORT_ALL + Header set Set-Cookie name=value +</LocationMatch> +ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1 +ScriptAlias /broken_smart/ broken-smart-http.sh/ <Directory ${GIT_EXEC_PATH}> - Options None + Options FollowSymlinks </Directory> +<Files broken-smart-http.sh> + Options ExecCGI +</Files> <Files ${GIT_EXEC_PATH}/git-http-backend> Options ExecCGI </Files> @@ -64,6 +110,8 @@ ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/ RewriteEngine on RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301] RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302] +RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301] +RewriteRule ^/smart-redir-limited/(.*)/info/refs$ /smart/$1/info/refs [R=301] <IfDefine SSL> LoadModule ssl_module modules/mod_ssl.so @@ -84,6 +132,35 @@ SSLEngine On Require valid-user </Location> +<LocationMatch "^/auth-push/.*/git-receive-pack$"> + AuthType Basic + AuthName "git-auth" + AuthUserFile passwd + Require valid-user +</LocationMatch> + +<LocationMatch "^/auth-fetch/.*/git-upload-pack$"> + AuthType Basic + AuthName "git-auth" + AuthUserFile passwd + Require valid-user +</LocationMatch> + +RewriteCond %{QUERY_STRING} service=git-receive-pack [OR] +RewriteCond %{REQUEST_URI} /git-receive-pack$ +RewriteRule ^/half-auth-complete/ - [E=AUTHREQUIRED:yes] + +<Location /half-auth-complete/> + Order Deny,Allow + Deny from env=AUTHREQUIRED + + AuthType Basic + AuthName "Git Access" + AuthUserFile passwd + Require valid-user + Satisfy Any +</Location> + <IfDefine DAV> LoadModule dav_module modules/mod_dav.so LoadModule dav_fs_module modules/mod_dav_fs.so diff --git a/t/lib-httpd/broken-smart-http.sh b/t/lib-httpd/broken-smart-http.sh new file mode 100755 index 0000000000..f7ebfffa80 --- /dev/null +++ b/t/lib-httpd/broken-smart-http.sh @@ -0,0 +1,11 @@ +#!/bin/sh +printf "Content-Type: text/%s\n" "html" +echo +printf "%s\n" "001e# service=git-upload-pack" +printf "%s" "0000" +printf "%s%c%s%s\n" \ + "00a58681d9f286a48b08f37b3a095330da16689e3693 HEAD" \ + 0 \ + " include-tag multi_ack_detailed multi_ack ofs-delta" \ + " side-band side-band-64k thin-pack no-progress shallow no-done " +printf "%s" "0000" diff --git a/t/lib-pack.sh b/t/lib-pack.sh new file mode 100644 index 0000000000..b96e1254dd --- /dev/null +++ b/t/lib-pack.sh @@ -0,0 +1,100 @@ +#!/bin/sh +# +# Support routines for hand-crafting weird or malicious packs. +# +# You can make a complete pack like: +# +# pack_header 2 >foo.pack && +# pack_obj e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 >>foo.pack && +# pack_obj e68fe8129b546b101aee9510c5328e7f21ca1d18 >>foo.pack && +# pack_trailer foo.pack + +# Print the big-endian 4-byte octal representation of $1 +uint32_octal () { + n=$1 + printf '\\%o' $(($n / 16777216)); n=$((n % 16777216)) + printf '\\%o' $(($n / 65536)); n=$((n % 65536)) + printf '\\%o' $(($n / 256)); n=$((n % 256)) + printf '\\%o' $(($n )); +} + +# Print the big-endian 4-byte binary representation of $1 +uint32_binary () { + printf "$(uint32_octal "$1")" +} + +# Print a pack header, version 2, for a pack with $1 objects +pack_header () { + printf 'PACK' && + printf '\0\0\0\2' && + uint32_binary "$1" +} + +# Print the pack data for object $1, as a delta against object $2 (or as a full +# object if $2 is missing or empty). The output is suitable for including +# directly in the packfile, and represents the entirety of the object entry. +# Doing this on the fly (especially picking your deltas) is quite tricky, so we +# have hardcoded some well-known objects. See the case statements below for the +# complete list. +pack_obj () { + case "$1" in + # empty blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391) + case "$2" in + '') + printf '\060\170\234\003\0\0\0\0\1' + return + ;; + esac + ;; + + # blob containing "\7\76" + e68fe8129b546b101aee9510c5328e7f21ca1d18) + case "$2" in + '') + printf '\062\170\234\143\267\3\0\0\116\0\106' + return + ;; + 01d7713666f4de822776c7622c10f1b07de280dc) + printf '\165\1\327\161\66\146\364\336\202\47\166' && + printf '\307\142\54\20\361\260\175\342\200\334\170' && + printf '\234\143\142\142\142\267\003\0\0\151\0\114' + return + ;; + esac + ;; + + # blob containing "\7\0" + 01d7713666f4de822776c7622c10f1b07de280dc) + case "$2" in + '') + printf '\062\170\234\143\147\0\0\0\20\0\10' + return + ;; + e68fe8129b546b101aee9510c5328e7f21ca1d18) + printf '\165\346\217\350\22\233\124\153\20\32\356' && + printf '\225\20\305\62\216\177\41\312\35\30\170\234' && + printf '\143\142\142\142\147\0\0\0\53\0\16' + return + ;; + esac + ;; + esac + + echo >&2 "BUG: don't know how to print $1${2:+ (from $2)}" + return 1 +} + +# Compute and append pack trailer to "$1" +pack_trailer () { + test-sha1 -b <"$1" >trailer.tmp && + cat trailer.tmp >>"$1" && + rm -f trailer.tmp +} + +# Remove any existing packs to make sure that +# whatever we index next will be the pack that we +# actually use. +clear_packs () { + rm -f .git/objects/pack/* +} diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh index 6ccf797091..8ff87fb3f9 100644 --- a/t/lib-rebase.sh +++ b/t/lib-rebase.sh @@ -17,51 +17,98 @@ # ("squash", "fixup", "edit", or "reword") and the SHA1 taken # from the specified line. # +# "exec_cmd_with_args" -- add an "exec cmd with args" line. +# # "#" -- Add a comment line. # # ">" -- Add a blank line. set_fake_editor () { - echo "#!$SHELL_PATH" >fake-editor.sh - cat >> fake-editor.sh <<\EOF -case "$1" in -*/COMMIT_EDITMSG) - test -z "$EXPECT_HEADER_COUNT" || - test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" || + write_script fake-editor.sh <<-\EOF + case "$1" in + */COMMIT_EDITMSG) + test -z "$EXPECT_HEADER_COUNT" || + test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" || + exit + test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1" + test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1" exit - test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1" - test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1" - exit - ;; -esac -test -z "$EXPECT_COUNT" || - test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) || - exit -test -z "$FAKE_LINES" && exit -grep -v '^#' < "$1" > "$1".tmp -rm -f "$1" -echo 'rebase -i script before editing:' -cat "$1".tmp -action=pick -for line in $FAKE_LINES; do - case $line in - squash|fixup|edit|reword) - action="$line";; - exec*) - echo "$line" | sed 's/_/ /g' >> "$1";; - "#") - echo '# comment' >> "$1";; - ">") - echo >> "$1";; - *) - sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1" - action=pick;; + ;; esac -done -echo 'rebase -i script after editing:' -cat "$1" -EOF + test -z "$EXPECT_COUNT" || + test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) || + exit + test -z "$FAKE_LINES" && exit + grep -v '^#' < "$1" > "$1".tmp + rm -f "$1" + echo 'rebase -i script before editing:' + cat "$1".tmp + action=pick + for line in $FAKE_LINES; do + case $line in + squash|fixup|edit|reword) + action="$line";; + exec*) + echo "$line" | sed 's/_/ /g' >> "$1";; + "#") + echo '# comment' >> "$1";; + ">") + echo >> "$1";; + *) + sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1" + action=pick;; + esac + done + echo 'rebase -i script after editing:' + cat "$1" + EOF + + test_set_editor "$(pwd)/fake-editor.sh" +} + +# After set_cat_todo_editor, rebase -i will write the todo list (ignoring +# blank lines and comments) to stdout, and exit failure (so you should run +# it with test_must_fail). This can be used to verify the expected user +# experience, for todo list changes that do not affect the outcome of +# rebase; or as an extra check in addition to checking the outcome. +set_cat_todo_editor () { + write_script fake-editor.sh <<-\EOF + grep "^[^#]" "$1" + exit 1 + EOF test_set_editor "$(pwd)/fake-editor.sh" - chmod a+x fake-editor.sh +} + +# checks that the revisions in "$2" represent a linear range with the +# subjects in "$1" +test_linear_range () { + revlist_merges=$(git rev-list --merges "$2") && + test -z "$revlist_merges" && + expected=$1 + set -- $(git log --reverse --format=%s "$2") + test "$expected" = "$*" +} + +reset_rebase () { + test_might_fail git rebase --abort && + git reset --hard && + git clean -f +} + +cherry_pick () { + git cherry-pick -n "$2" && + git commit -m "$1" && + git tag "$1" +} + +revert () { + git revert -n "$2" && + git commit -m "$1" && + git tag "$1" +} + +make_empty () { + git commit --allow-empty -m "$1" && + git tag "$1" } diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh index ea25dd89e5..3f2d873fec 100644 --- a/t/lib-t6000.sh +++ b/t/lib-t6000.sh @@ -1,55 +1,50 @@ : included from 6002 and others -[ -d .git/refs/tags ] || mkdir -p .git/refs/tags +mkdir -p .git/refs/tags -:> sed.script +>sed.script -# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags -tag() -{ +# Answer the sha1 has associated with the tag. The tag must exist in .git/refs/tags +tag () { _tag=$1 - [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist" - cat .git/refs/tags/$_tag + test -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() -{ +unique_commit () { _text=$1 - _tree=$2 + _tree=$2 shift 2 - echo $_text | git commit-tree $(tag $_tree) "$@" + 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() -{ +save_tag () { _tag=$1 - [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..." + test -n "$_tag" || error "usage: save_tag tag commit-args ..." shift 1 - "$@" >.git/refs/tags/$_tag + "$@" >".git/refs/tags/$_tag" - echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp - cat sed.script >> sed.script.tmp + 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() -{ +# Replace unhelpful sha1 hashes 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() -{ +as_author () { _author=$1 shift 1 - _save=$GIT_AUTHOR_EMAIL + _save=$GIT_AUTHOR_EMAIL GIT_AUTHOR_EMAIL="$_author" export GIT_AUTHOR_EMAIL @@ -63,45 +58,58 @@ as_author() fi } -commit_date() -{ - _commit=$1 - git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p" +commit_date () { + _commit=$1 + git cat-file commit $_commit | + sed -n "s/^committer .*> \([0-9]*\) .*/\1/p" } -on_committer_date() -{ - _date=$1 - shift 1 - GIT_COMMITTER_DATE="$_date" - export GIT_COMMITTER_DATE - "$@" - unset GIT_COMMITTER_DATE +# Assign the value of fake date to a variable, but +# allow fairly common "1971-08-16 00:00" to be omittd +assign_fake_date () { + case "$2" in + ??:??:??) eval "$1='1971-08-16 $2'" ;; + ??:??) eval "$1='1971-08-16 00:$2'" ;; + ??) eval "$1='1971-08-16 00:00:$2'" ;; + *) eval "$1='$2'" ;; + esac +} + +on_committer_date () { + assign_fake_date GIT_COMMITTER_DATE "$1" + export GIT_COMMITTER_DATE + shift 1 + "$@" +} + +on_dates () { + assign_fake_date GIT_COMMITTER_DATE "$1" + assign_fake_date GIT_AUTHOR_DATE "$2" + export GIT_COMMITTER_DATE GIT_AUTHOR_DATE + shift 2 + "$@" } # Execute a command and suppress any error output. -hide_error() -{ +hide_error () { "$@" 2>/dev/null } -check_output() -{ +check_output () { _name=$1 shift 1 - if eval "$*" | entag > $_name.actual + if eval "$*" | entag >"$_name.actual" then - test_cmp $_name.expected $_name.actual + test_cmp "$_name.expected" "$_name.actual" else - return 1; + 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() -{ +name_from_description () { perl -pe ' s/[^A-Za-z0-9.]/-/g; s/-+/-/g; @@ -119,9 +127,11 @@ name_from_description() 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=$2 + test $# -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/lib-terminal.sh b/t/lib-terminal.sh index 58d911d21b..737df289a1 100644 --- a/t/lib-terminal.sh +++ b/t/lib-terminal.sh @@ -19,7 +19,7 @@ test_expect_success PERL 'set up terminal for tests' ' then : elif - "$PERL_PATH" "$TEST_DIRECTORY"/test-terminal.perl \ + perl "$TEST_DIRECTORY"/test-terminal.perl \ sh -c "test -t 1 && test -t 2" then test_set_prereq TTY && @@ -29,7 +29,7 @@ test_expect_success PERL 'set up terminal for tests' ' echo >&4 "test_terminal: need to declare TTY prerequisite" return 127 fi - "$PERL_PATH" "$TEST_DIRECTORY"/test-terminal.perl "$@" + perl "$TEST_DIRECTORY"/test-terminal.perl "$@" } fi ' diff --git a/t/perf/.gitignore b/t/perf/.gitignore new file mode 100644 index 0000000000..982eb8e3a9 --- /dev/null +++ b/t/perf/.gitignore @@ -0,0 +1,3 @@ +/build/ +/test-results/ +/trash directory*/ diff --git a/t/perf/Makefile b/t/perf/Makefile new file mode 100644 index 0000000000..8c47155a7c --- /dev/null +++ b/t/perf/Makefile @@ -0,0 +1,15 @@ +-include ../../config.mak +export GIT_TEST_OPTIONS + +all: perf + +perf: pre-clean + ./run + +pre-clean: + rm -rf test-results + +clean: + rm -rf build "trash directory".* test-results + +.PHONY: all perf pre-clean clean diff --git a/t/perf/README b/t/perf/README new file mode 100644 index 0000000000..8848c14619 --- /dev/null +++ b/t/perf/README @@ -0,0 +1,146 @@ +Git performance tests +===================== + +This directory holds performance testing scripts for git tools. The +first part of this document describes the various ways in which you +can run them. + +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 on the current git repository. + + === Running 2 tests in this tree === + [...] + Test this tree + --------------------------------------------------------- + 0001.1: rev-list --all 0.54(0.51+0.02) + 0001.2: rev-list --all --objects 6.14(5.99+0.11) + 7810.1: grep worktree, cheap regex 0.16(0.16+0.35) + 7810.2: grep worktree, expensive regex 7.90(29.75+0.37) + 7810.3: grep --cached, cheap regex 3.07(3.02+0.25) + 7810.4: grep --cached, expensive regex 9.39(30.57+0.24) + +You can compare multiple repositories and even git revisions with the +'run' script: + + $ ./run . origin/next /path/to/git-tree p0001-rev-list.sh + +where . stands for the current git tree. The full invocation is + + ./run [<revision|directory>...] [--] [<test-script>...] + +A '.' argument is implied if you do not pass any other +revisions/directories. + +You can also manually test this or another git build tree, and then +call the aggregation script to summarize the results: + + $ ./p0001-rev-list.sh + [...] + $ GIT_BUILD_DIR=/path/to/other/git ./p0001-rev-list.sh + [...] + $ ./aggregate.perl . /path/to/other/git ./p0001-rev-list.sh + +aggregate.perl has the same invocation as 'run', it just does not run +anything beforehand. + +You can set the following variables (also in your config.mak): + + GIT_PERF_REPEAT_COUNT + Number of times a test should be repeated for best-of-N + measurements. Defaults to 3. + + GIT_PERF_MAKE_OPTS + Options to use when automatically building a git tree for + performance testing. E.g., -j6 would be useful. + + GIT_PERF_REPO + GIT_PERF_LARGE_REPO + Repositories to copy for the performance tests. The normal + repo should be at least git.git size. The large repo should + probably be about linux.git size for optimal results. + Both default to the git.git you are running from. + +You can also pass the options taken by ordinary git tests; the most +useful one is: + +--root=<directory>:: + Create "trash" directories used to store all temporary data during + testing under <directory>, instead of the t/ directory. + Using this option with a RAM-based filesystem (such as tmpfs) + can massively speed up the test suite. + + +Naming Tests +------------ + +The performance test files are named as: + + pNNNN-commandname-details.sh + +where N is a decimal digit. The same conventions for choosing NNNN as +for normal tests apply. + + +Writing Tests +------------- + +The perf script starts much like a normal test script, except it +sources perf-lib.sh: + + #!/bin/sh + # + # Copyright (c) 2005 Junio C Hamano + # + + test_description='xxx performance test' + . ./perf-lib.sh + +After that you will want to use some of the following: + + test_perf_default_repo # sets up a "normal" repository + test_perf_large_repo # sets up a "large" repository + + test_perf_default_repo sub # ditto, in a subdir "sub" + + test_checkout_worktree # if you need the worktree too + +At least one of the first two is required! + +You can use test_expect_success as usual. For actual performance +tests, use + + test_perf 'descriptive string' ' + command1 && + command2 + ' + +test_perf spawns a subshell, for lack of better options. This means +that + +* you _must_ export all variables that you need in the subshell + +* you _must_ flag all variables that you want to persist from the + subshell with 'test_export': + + test_perf 'descriptive string' ' + foo=$(git rev-parse HEAD) && + test_export foo + ' + + The so-exported variables are automatically marked for export in the + shell executing the perf test. For your convenience, test_export is + the same as export in the main shell. + + This feature relies on a bit of magic using 'set' and 'source'. + While we have tried to make sure that it can cope with embedded + whitespace and other special characters, it will not work with + multi-line data. diff --git a/t/perf/aggregate.perl b/t/perf/aggregate.perl new file mode 100755 index 0000000000..15f7fc1b80 --- /dev/null +++ b/t/perf/aggregate.perl @@ -0,0 +1,166 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use Git; + +sub get_times { + my $name = shift; + open my $fh, "<", $name or return undef; + my $line = <$fh>; + return undef if not defined $line; + close $fh or die "cannot close $name: $!"; + $line =~ /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/ + or die "bad input line: $line"; + my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3; + return ($rt, $4, $5); +} + +sub format_times { + my ($r, $u, $s, $firstr) = @_; + if (!defined $r) { + return "<missing>"; + } + my $out = sprintf "%.2f(%.2f+%.2f)", $r, $u, $s; + if (defined $firstr) { + if ($firstr > 0) { + $out .= sprintf " %+.1f%%", 100.0*($r-$firstr)/$firstr; + } elsif ($r == 0) { + $out .= " ="; + } else { + $out .= " +inf"; + } + } + return $out; +} + +my (@dirs, %dirnames, %dirabbrevs, %prefixes, @tests); +while (scalar @ARGV) { + my $arg = $ARGV[0]; + my $dir; + last if -f $arg or $arg eq "--"; + if (! -d $arg) { + my $rev = Git::command_oneline(qw(rev-parse --verify), $arg); + $dir = "build/".$rev; + } else { + $arg =~ s{/*$}{}; + $dir = $arg; + $dirabbrevs{$dir} = $dir; + } + push @dirs, $dir; + $dirnames{$dir} = $arg; + my $prefix = $dir; + $prefix =~ tr/^a-zA-Z0-9/_/c; + $prefixes{$dir} = $prefix . '.'; + shift @ARGV; +} + +if (not @dirs) { + @dirs = ('.'); +} +$dirnames{'.'} = $dirabbrevs{'.'} = "this tree"; +$prefixes{'.'} = ''; + +shift @ARGV if scalar @ARGV and $ARGV[0] eq "--"; + +@tests = @ARGV; +if (not @tests) { + @tests = glob "p????-*.sh"; +} + +my @subtests; +my %shorttests; +for my $t (@tests) { + $t =~ s{(?:.*/)?(p(\d+)-[^/]+)\.sh$}{$1} or die "bad test name: $t"; + my $n = $2; + my $fname = "test-results/$t.subtests"; + open my $fp, "<", $fname or die "cannot open $fname: $!"; + for (<$fp>) { + chomp; + /^(\d+)$/ or die "malformed subtest line: $_"; + push @subtests, "$t.$1"; + $shorttests{"$t.$1"} = "$n.$1"; + } + close $fp or die "cannot close $fname: $!"; +} + +sub read_descr { + my $name = shift; + open my $fh, "<", $name or return "<error reading description>"; + my $line = <$fh>; + close $fh or die "cannot close $name"; + chomp $line; + return $line; +} + +my %descrs; +my $descrlen = 4; # "Test" +for my $t (@subtests) { + $descrs{$t} = $shorttests{$t}.": ".read_descr("test-results/$t.descr"); + $descrlen = length $descrs{$t} if length $descrs{$t}>$descrlen; +} + +sub have_duplicate { + my %seen; + for (@_) { + return 1 if exists $seen{$_}; + $seen{$_} = 1; + } + return 0; +} +sub have_slash { + for (@_) { + return 1 if m{/}; + } + return 0; +} + +my %newdirabbrevs = %dirabbrevs; +while (!have_duplicate(values %newdirabbrevs)) { + %dirabbrevs = %newdirabbrevs; + last if !have_slash(values %dirabbrevs); + %newdirabbrevs = %dirabbrevs; + for (values %newdirabbrevs) { + s{^[^/]*/}{}; + } +} + +my %times; +my @colwidth = ((0)x@dirs); +for my $i (0..$#dirs) { + my $d = $dirs[$i]; + my $w = length (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d}); + $colwidth[$i] = $w if $w > $colwidth[$i]; +} +for my $t (@subtests) { + my $firstr; + for my $i (0..$#dirs) { + my $d = $dirs[$i]; + $times{$prefixes{$d}.$t} = [get_times("test-results/$prefixes{$d}$t.times")]; + my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}}; + my $w = length format_times($r,$u,$s,$firstr); + $colwidth[$i] = $w if $w > $colwidth[$i]; + $firstr = $r unless defined $firstr; + } +} +my $totalwidth = 3*@dirs+$descrlen; +$totalwidth += $_ for (@colwidth); + +printf "%-${descrlen}s", "Test"; +for my $i (0..$#dirs) { + my $d = $dirs[$i]; + printf " %-$colwidth[$i]s", (exists $dirabbrevs{$d} ? $dirabbrevs{$d} : $dirnames{$d}); +} +print "\n"; +print "-"x$totalwidth, "\n"; +for my $t (@subtests) { + printf "%-${descrlen}s", $descrs{$t}; + my $firstr; + for my $i (0..$#dirs) { + my $d = $dirs[$i]; + my ($r,$u,$s) = @{$times{$prefixes{$d}.$t}}; + printf " %-$colwidth[$i]s", format_times($r,$u,$s,$firstr); + $firstr = $r unless defined $firstr; + } + print "\n"; +} diff --git a/t/perf/min_time.perl b/t/perf/min_time.perl new file mode 100755 index 0000000000..c1a2717e07 --- /dev/null +++ b/t/perf/min_time.perl @@ -0,0 +1,21 @@ +#!/usr/bin/perl + +my $minrt = 1e100; +my $min; + +while (<>) { + # [h:]m:s.xx U.xx S.xx + /^(?:(\d+):)?(\d+):(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)$/ + or die "bad input line: $_"; + my $rt = ((defined $1 ? $1 : 0.0)*60+$2)*60+$3; + if ($rt < $minrt) { + $min = $_; + $minrt = $rt; + } +} + +if (!defined $min) { + die "no input found"; +} + +print $min; diff --git a/t/perf/p0000-perf-lib-sanity.sh b/t/perf/p0000-perf-lib-sanity.sh new file mode 100755 index 0000000000..cf8e1efce7 --- /dev/null +++ b/t/perf/p0000-perf-lib-sanity.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +test_description='Tests whether perf-lib facilities work' +. ./perf-lib.sh + +test_perf_default_repo + +test_perf 'test_perf_default_repo works' ' + foo=$(git rev-parse HEAD) && + test_export foo +' + +test_checkout_worktree + +test_perf 'test_checkout_worktree works' ' + wt=$(find . | wc -l) && + idx=$(git ls-files | wc -l) && + test $wt -gt $idx +' + +baz=baz +test_export baz + +test_expect_success 'test_export works' ' + echo "$foo" && + test "$foo" = "$(git rev-parse HEAD)" && + echo "$baz" && + test "$baz" = baz +' + +test_perf 'export a weird var' ' + bar="weird # variable" && + test_export bar +' + +test_expect_success 'test_export works with weird vars' ' + echo "$bar" && + test "$bar" = "weird # variable" +' + +test_perf 'important variables available in subshells' ' + test -n "$HOME" && + test -n "$TEST_DIRECTORY" && + test -n "$TRASH_DIRECTORY" && + test -n "$GIT_BUILD_DIR" +' + +test_perf 'test-lib-functions correctly loaded in subshells' ' + : >a && + test_path_is_file a && + : >b && + test_cmp a b +' + +test_done diff --git a/t/perf/p0001-rev-list.sh b/t/perf/p0001-rev-list.sh new file mode 100755 index 0000000000..4f71a63b0a --- /dev/null +++ b/t/perf/p0001-rev-list.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +test_description="Tests history walking performance" + +. ./perf-lib.sh + +test_perf_default_repo + +test_perf 'rev-list --all' ' + git rev-list --all >/dev/null +' + +test_perf 'rev-list --all --objects' ' + git rev-list --all --objects >/dev/null +' + +test_done diff --git a/t/perf/p0002-read-cache.sh b/t/perf/p0002-read-cache.sh new file mode 100755 index 0000000000..9180ae9343 --- /dev/null +++ b/t/perf/p0002-read-cache.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +test_description="Tests performance of reading the index" + +. ./perf-lib.sh + +test_perf_default_repo + +count=1000 +test_perf "read_cache/discard_cache $count times" " + test-read-cache $count +" + +test_done diff --git a/t/perf/p4000-diff-algorithms.sh b/t/perf/p4000-diff-algorithms.sh new file mode 100755 index 0000000000..7e00c9da47 --- /dev/null +++ b/t/perf/p4000-diff-algorithms.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +test_description="Tests diff generation performance" + +. ./perf-lib.sh + +test_perf_default_repo + +test_perf 'log -3000 (baseline)' ' + git log -3000 >/dev/null +' + +test_perf 'log --raw -3000 (tree-only)' ' + git log --raw -3000 >/dev/null +' + +test_perf 'log -p -3000 (Myers)' ' + git log -p -3000 >/dev/null +' + +test_perf 'log -p -3000 --histogram' ' + git log -p -3000 --histogram >/dev/null +' + +test_perf 'log -p -3000 --patience' ' + git log -p -3000 --patience >/dev/null +' + +test_done diff --git a/t/perf/p4211-line-log.sh b/t/perf/p4211-line-log.sh new file mode 100755 index 0000000000..3d074b0e41 --- /dev/null +++ b/t/perf/p4211-line-log.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +test_description='Tests log -L performance' +. ./perf-lib.sh + +test_perf_default_repo + +# Pick a file to log pseudo-randomly. The sort key is the blob hash, +# so it is stable. +test_expect_success 'select a file' ' + git ls-tree HEAD | grep ^100644 | + sort -k 3 | head -1 | cut -f 2 >filelist +' + +file=$(cat filelist) +export file + +test_perf 'git rev-list --topo-order (baseline)' ' + git rev-list --topo-order HEAD >/dev/null +' + +test_perf 'git log --follow (baseline for -M)' ' + git log --oneline --follow -- "$file" >/dev/null +' + +test_perf 'git log -L' ' + git log -L 1:"$file" >/dev/null +' + +test_perf 'git log -M -L' ' + git log -M -L 1:"$file" >/dev/null +' + +test_done diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh new file mode 100755 index 0000000000..6cb5b0d55b --- /dev/null +++ b/t/perf/p5302-pack-index.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +test_description="Tests index-pack performance" + +. ./perf-lib.sh + +test_perf_large_repo + +test_expect_success 'repack' ' + git repack -ad && + PACK=`ls .git/objects/pack/*.pack | head -n1` && + test -f "$PACK" && + export PACK +' + +test_perf 'index-pack 0 threads' ' + GIT_DIR=t1 git index-pack --threads=1 --stdin < $PACK +' + +test_perf 'index-pack 1 thread ' ' + GIT_DIR=t2 GIT_FORCE_THREADS=1 git index-pack --threads=1 --stdin < $PACK +' + +test_perf 'index-pack 2 threads' ' + GIT_DIR=t3 git index-pack --threads=2 --stdin < $PACK +' + +test_perf 'index-pack 4 threads' ' + GIT_DIR=t4 git index-pack --threads=4 --stdin < $PACK +' + +test_perf 'index-pack 8 threads' ' + GIT_DIR=t5 git index-pack --threads=8 --stdin < $PACK +' + +test_perf 'index-pack default number of threads' ' + GIT_DIR=t6 git index-pack --stdin < $PACK +' + +test_done diff --git a/t/perf/p7810-grep.sh b/t/perf/p7810-grep.sh new file mode 100755 index 0000000000..9f4ade639f --- /dev/null +++ b/t/perf/p7810-grep.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +test_description="git-grep performance in various modes" + +. ./perf-lib.sh + +test_perf_large_repo +test_checkout_worktree + +test_perf 'grep worktree, cheap regex' ' + git grep some_nonexistent_string || : +' +test_perf 'grep worktree, expensive regex' ' + git grep "^.* *some_nonexistent_string$" || : +' +test_perf 'grep --cached, cheap regex' ' + git grep --cached some_nonexistent_string || : +' +test_perf 'grep --cached, expensive regex' ' + git grep --cached "^.* *some_nonexistent_string$" || : +' + +test_done diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh new file mode 100644 index 0000000000..f4eecaa171 --- /dev/null +++ b/t/perf/perf-lib.sh @@ -0,0 +1,204 @@ +#!/bin/sh +# +# Copyright (c) 2011 Thomas Rast +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/ . + +# do the --tee work early; it otherwise confuses our careful +# GIT_BUILD_DIR mangling +case "$GIT_TEST_TEE_STARTED, $* " in +done,*) + # do not redirect again + ;; +*' --tee '*|*' --va'*) + mkdir -p test-results + BASE=test-results/$(basename "$0" .sh) + (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1; + echo $? > $BASE.exit) | tee $BASE.out + test "$(cat $BASE.exit)" = 0 + exit + ;; +esac + +TEST_DIRECTORY=$(pwd)/.. +TEST_OUTPUT_DIRECTORY=$(pwd) +if test -z "$GIT_TEST_INSTALLED"; then + perf_results_prefix= +else + perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"." + # make the tested dir absolute + GIT_TEST_INSTALLED=$(cd "$GIT_TEST_INSTALLED" && pwd) +fi + +TEST_NO_CREATE_REPO=t +TEST_NO_MALLOC_CHECK=t + +. ../test-lib.sh + +# Variables from test-lib that are normally internal to the tests; we +# need to export them for test_perf subshells +export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP + +perf_results_dir=$TEST_OUTPUT_DIRECTORY/test-results +mkdir -p "$perf_results_dir" +rm -f "$perf_results_dir"/$(basename "$0" .sh).subtests + +if test -z "$GIT_PERF_REPEAT_COUNT"; then + GIT_PERF_REPEAT_COUNT=3 +fi +die_if_build_dir_not_repo () { + if ! ( cd "$TEST_DIRECTORY/.." && + git rev-parse --build-dir >/dev/null 2>&1 ); then + error "No $1 defined, and your build directory is not a repo" + fi +} + +if test -z "$GIT_PERF_REPO"; then + die_if_build_dir_not_repo '$GIT_PERF_REPO' + GIT_PERF_REPO=$TEST_DIRECTORY/.. +fi +if test -z "$GIT_PERF_LARGE_REPO"; then + die_if_build_dir_not_repo '$GIT_PERF_LARGE_REPO' + GIT_PERF_LARGE_REPO=$TEST_DIRECTORY/.. +fi + +test_perf_create_repo_from () { + test "$#" = 2 || + error "bug in the test script: not 2 parameters to test-create-repo" + repo="$1" + source="$2" + source_git=$source/$(cd "$source" && git rev-parse --git-dir) + mkdir -p "$repo/.git" + ( + cd "$repo/.git" && + { cp -Rl "$source_git/objects" . 2>/dev/null || + cp -R "$source_git/objects" .; } && + for stuff in "$source_git"/*; do + case "$stuff" in + */objects|*/hooks|*/config) + ;; + *) + cp -R "$stuff" . || break + ;; + esac + done && + cd .. && + git init -q && + mv .git/hooks .git/hooks-disabled 2>/dev/null + ) || error "failed to copy repository '$source' to '$repo'" +} + +# call at least one of these to establish an appropriately-sized repository +test_perf_default_repo () { + test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_REPO" +} +test_perf_large_repo () { + if test "$GIT_PERF_LARGE_REPO" = "$GIT_BUILD_DIR"; then + echo "warning: \$GIT_PERF_LARGE_REPO is \$GIT_BUILD_DIR." >&2 + echo "warning: This will work, but may not be a sufficiently large repo" >&2 + echo "warning: for representative measurements." >&2 + fi + test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_LARGE_REPO" +} +test_checkout_worktree () { + git checkout-index -u -a || + error "git checkout-index failed" +} + +# Performance tests should never fail. If they do, stop immediately +immediate=t + +test_run_perf_ () { + test_cleanup=: + test_export_="test_cleanup" + export test_cleanup test_export_ + /usr/bin/time -f "%E %U %S" -o test_time.$i "$SHELL" -c ' +. '"$TEST_DIRECTORY"/test-lib-functions.sh' +test_export () { + [ $# != 0 ] || return 0 + test_export_="$test_export_\\|$1" + shift + test_export "$@" +} +'"$1"' +ret=$? +set | sed -n "s'"/'/'\\\\''/g"';s/^\\($test_export_\\)/export '"'&'"'/p" >test_vars +exit $ret' >&3 2>&4 + eval_ret=$? + + if test $eval_ret = 0 || test -n "$expecting_failure" + then + test_eval_ "$test_cleanup" + . ./test_vars || error "failed to load updated environment" + fi + if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then + echo "" + fi + return "$eval_ret" +} + + +test_perf () { + test_start_ + test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= + test "$#" = 2 || + error "bug in the test script: not 2 or 3 parameters to test-expect-success" + export test_prereq + if ! test_skip "$@" + then + base=$(basename "$0" .sh) + echo "$test_count" >>"$perf_results_dir"/$base.subtests + echo "$1" >"$perf_results_dir"/$base.$test_count.descr + if test -z "$verbose"; then + printf "%s" "perf $test_count - $1:" + else + echo "perf $test_count - $1:" + fi + for i in $(test_seq 1 $GIT_PERF_REPEAT_COUNT); do + say >&3 "running: $2" + if test_run_perf_ "$2" + then + if test -z "$verbose"; then + printf " %s" "$i" + else + echo "* timing run $i/$GIT_PERF_REPEAT_COUNT:" + fi + else + test -z "$verbose" && echo + test_failure_ "$@" + break + fi + done + if test -z "$verbose"; then + echo " ok" + else + test_ok_ "$1" + fi + base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count" + "$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times + fi + test_finish_ +} + +# We extend test_done to print timings at the end (./run disables this +# and does it after running everything) +test_at_end_hook_ () { + if test -z "$GIT_PERF_AGGREGATING_LATER"; then + ( cd "$TEST_DIRECTORY"/perf && ./aggregate.perl $(basename "$0") ) + fi +} + +test_export () { + export "$@" +} diff --git a/t/perf/run b/t/perf/run new file mode 100755 index 0000000000..cfd70129bb --- /dev/null +++ b/t/perf/run @@ -0,0 +1,82 @@ +#!/bin/sh + +case "$1" in + --help) + echo "usage: $0 [other_git_tree...] [--] [test_scripts]" + exit 0 + ;; +esac + +die () { + echo >&2 "error: $*" + exit 1 +} + +run_one_dir () { + if test $# -eq 0; then + set -- p????-*.sh + fi + echo "=== Running $# tests in ${GIT_TEST_INSTALLED:-this tree} ===" + for t in "$@"; do + ./$t $GIT_TEST_OPTS + done +} + +unpack_git_rev () { + rev=$1 + mkdir -p build/$rev + (cd "$(git rev-parse --show-cdup)" && git archive --format=tar $rev) | + (cd build/$rev && tar x) +} +build_git_rev () { + rev=$1 + cp ../../config.mak build/$rev/config.mak + (cd build/$rev && make $GIT_PERF_MAKE_OPTS) || + die "failed to build revision '$mydir'" +} + +run_dirs_helper () { + mydir=${1%/} + shift + while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do + shift + done + if test $# -gt 0 -a "$1" = --; then + shift + fi + if [ ! -d "$mydir" ]; then + rev=$(git rev-parse --verify "$mydir" 2>/dev/null) || + die "'$mydir' is neither a directory nor a valid revision" + if [ ! -d build/$rev ]; then + unpack_git_rev $rev + fi + build_git_rev $rev + mydir=build/$rev + fi + if test "$mydir" = .; then + unset GIT_TEST_INSTALLED + else + GIT_TEST_INSTALLED="$mydir/bin-wrappers" + export GIT_TEST_INSTALLED + fi + run_one_dir "$@" +} + +run_dirs () { + while test $# -gt 0 -a "$1" != -- -a ! -f "$1"; do + run_dirs_helper "$@" + shift + done +} + +GIT_PERF_AGGREGATING_LATER=t +export GIT_PERF_AGGREGATING_LATER + +cd "$(dirname $0)" +. ../../GIT-BUILD-OPTIONS + +if test $# = 0 -o "$1" = -- -o -f "$1"; then + set -- . "$@" +fi +run_dirs "$@" +./aggregate.perl "$@" diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index f4e8f43bae..10be52beed 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -18,85 +18,272 @@ 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 try to run tests without building... - -../git >/dev/null -if test $? != 1 -then - echo >&2 'You do not seem to have built git yet.' - exit 1 -fi - . ./test-lib.sh ################################################################ # git init 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 in an empty repo.' \ - 'cmp -s /dev/null should-be-empty' +test_expect_success '.git/objects should be empty after git init in an empty repo' ' + find .git/objects -type f -print >should-be-empty && + test_line_count = 0 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' +test_expect_success '.git/objects should have 3 subdirectories' ' + find .git/objects -type d -print >full-of-directories && + test_line_count = 3 full-of-directories +' ################################################################ # Test harness test_expect_success 'success is reported like this' ' - : + : ' test_expect_failure 'pretend we have a known breakage' ' - false + false ' -test_expect_success 'pretend we have fixed a known breakage (run in sub test-lib)' " - mkdir passing-todo && - (cd passing-todo && - cat >passing-todo.sh <<EOF && -#!$SHELL_PATH +run_sub_test_lib_test () { + name="$1" descr="$2" # stdin is the body of the test code + shift 2 + mkdir "$name" && + ( + # Pretend we're a test harness. This prevents + # test-lib from writing the counts to a file that will + # later be summarized, showing spurious "failed" tests + HARNESS_ACTIVE=t && + export HARNESS_ACTIVE && + cd "$name" && + cat >"$name.sh" <<-EOF && + #!$SHELL_PATH + + test_description='$descr (run in sub test-lib) + + This is run in a sub test-lib so that we do not get incorrect + passing metrics + ' + + # Point to the t/test-lib.sh, which isn't in ../ as usual + . "\$TEST_DIRECTORY"/test-lib.sh + EOF + cat >>"$name.sh" && + chmod +x "$name.sh" && + export TEST_DIRECTORY && + ./"$name.sh" "$@" >out 2>err + ) +} + +check_sub_test_lib_test () { + name="$1" # stdin is the expected output from the test + ( + cd "$name" && + ! test -s err && + sed -e 's/^> //' -e 's/Z$//' >expect && + test_cmp expect out + ) +} + +test_expect_success 'pretend we have a fully passing test suite' " + run_sub_test_lib_test full-pass '3 passing tests' <<-\\EOF && + for i in 1 2 3 + do + test_expect_success \"passing test #\$i\" 'true' + done + test_done + EOF + check_sub_test_lib_test full-pass <<-\\EOF + > ok 1 - passing test #1 + > ok 2 - passing test #2 + > ok 3 - passing test #3 + > # passed all 3 test(s) + > 1..3 + EOF +" + +test_expect_success 'pretend we have a partially passing test suite' " + test_must_fail run_sub_test_lib_test \ + partial-pass '2/3 tests passing' <<-\\EOF && + test_expect_success 'passing test #1' 'true' + test_expect_success 'failing test #2' 'false' + test_expect_success 'passing test #3' 'true' + test_done + EOF + check_sub_test_lib_test partial-pass <<-\\EOF + > ok 1 - passing test #1 + > not ok 2 - failing test #2 + # false + > ok 3 - passing test #3 + > # failed 1 among 3 test(s) + > 1..3 + EOF +" -test_description='A passing TODO test +test_expect_success 'pretend we have a known breakage' " + run_sub_test_lib_test failing-todo 'A failing TODO test' <<-\\EOF && + test_expect_success 'passing test' 'true' + test_expect_failure 'pretend we have a known breakage' 'false' + test_done + EOF + check_sub_test_lib_test failing-todo <<-\\EOF + > ok 1 - passing test + > not ok 2 - pretend we have a known breakage # TODO known breakage + > # still have 1 known breakage(s) + > # passed all remaining 1 test(s) + > 1..2 + EOF +" -This is run in a sub test-lib so that we do not get incorrect passing -metrics -' +test_expect_success 'pretend we have fixed a known breakage' " + run_sub_test_lib_test passing-todo 'A passing TODO test' <<-\\EOF && + test_expect_failure 'pretend we have fixed a known breakage' 'true' + test_done + EOF + check_sub_test_lib_test passing-todo <<-\\EOF + > ok 1 - pretend we have fixed a known breakage # TODO known breakage vanished + > # 1 known breakage(s) vanished; please update test(s) + > 1..1 + EOF +" -# Point to the t/test-lib.sh, which isn't in ../ as usual -TEST_DIRECTORY=\"$TEST_DIRECTORY\" -. \"\$TEST_DIRECTORY\"/test-lib.sh +test_expect_success 'pretend we have fixed one of two known breakages (run in sub test-lib)' " + run_sub_test_lib_test partially-passing-todos \ + '2 TODO tests, one passing' <<-\\EOF && + test_expect_failure 'pretend we have a known breakage' 'false' + test_expect_success 'pretend we have a passing test' 'true' + test_expect_failure 'pretend we have fixed another known breakage' 'true' + test_done + EOF + check_sub_test_lib_test partially-passing-todos <<-\\EOF + > not ok 1 - pretend we have a known breakage # TODO known breakage + > ok 2 - pretend we have a passing test + > ok 3 - pretend we have fixed another known breakage # TODO known breakage vanished + > # 1 known breakage(s) vanished; please update test(s) + > # still have 1 known breakage(s) + > # passed all remaining 1 test(s) + > 1..3 + EOF +" -test_expect_failure 'pretend we have fixed a known breakage' ' - : -' +test_expect_success 'pretend we have a pass, fail, and known breakage' " + test_must_fail run_sub_test_lib_test \ + mixed-results1 'mixed results #1' <<-\\EOF && + test_expect_success 'passing test' 'true' + test_expect_success 'failing test' 'false' + test_expect_failure 'pretend we have a known breakage' 'false' + test_done + EOF + check_sub_test_lib_test mixed-results1 <<-\\EOF + > ok 1 - passing test + > not ok 2 - failing test + > # false + > not ok 3 - pretend we have a known breakage # TODO known breakage + > # still have 1 known breakage(s) + > # failed 1 among remaining 2 test(s) + > 1..3 + EOF +" -test_done -EOF - chmod +x passing-todo.sh && - ./passing-todo.sh >out 2>err && - ! test -s err && -sed -e 's/^> //' >expect <<EOF && -> ok 1 - pretend we have fixed a known breakage # TODO known breakage -> # fixed 1 known breakage(s) -> # passed all 1 test(s) -> 1..1 -EOF - test_cmp expect out) +test_expect_success 'pretend we have a mix of all possible results' " + test_must_fail run_sub_test_lib_test \ + mixed-results2 'mixed results #2' <<-\\EOF && + test_expect_success 'passing test' 'true' + test_expect_success 'passing test' 'true' + test_expect_success 'passing test' 'true' + test_expect_success 'passing test' 'true' + test_expect_success 'failing test' 'false' + test_expect_success 'failing test' 'false' + test_expect_success 'failing test' 'false' + test_expect_failure 'pretend we have a known breakage' 'false' + test_expect_failure 'pretend we have a known breakage' 'false' + test_expect_failure 'pretend we have fixed a known breakage' 'true' + test_done + EOF + check_sub_test_lib_test mixed-results2 <<-\\EOF + > ok 1 - passing test + > ok 2 - passing test + > ok 3 - passing test + > ok 4 - passing test + > not ok 5 - failing test + > # false + > not ok 6 - failing test + > # false + > not ok 7 - failing test + > # false + > not ok 8 - pretend we have a known breakage # TODO known breakage + > not ok 9 - pretend we have a known breakage # TODO known breakage + > ok 10 - pretend we have fixed a known breakage # TODO known breakage vanished + > # 1 known breakage(s) vanished; please update test(s) + > # still have 2 known breakage(s) + > # failed 3 among remaining 7 test(s) + > 1..10 + EOF " + +test_expect_success 'test --verbose' ' + test_must_fail run_sub_test_lib_test \ + test-verbose "test verbose" --verbose <<-\EOF && + test_expect_success "passing test" true + test_expect_success "test with output" "echo foo" + test_expect_success "failing test" false + test_done + EOF + mv test-verbose/out test-verbose/out+ + grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out && + check_sub_test_lib_test test-verbose <<-\EOF + > expecting success: true + > Z + > ok 1 - passing test + > Z + > expecting success: echo foo + > foo + > Z + > ok 2 - test with output + > Z + > expecting success: false + > Z + > not ok 3 - failing test + > # false + > Z + > # failed 1 among 3 test(s) + > 1..3 + EOF +' + +test_expect_success 'test --verbose-only' ' + test_must_fail run_sub_test_lib_test \ + test-verbose-only-2 "test verbose-only=2" \ + --verbose-only=2 <<-\EOF && + test_expect_success "passing test" true + test_expect_success "test with output" "echo foo" + test_expect_success "failing test" false + test_done + EOF + check_sub_test_lib_test test-verbose-only-2 <<-\EOF + > ok 1 - passing test + > Z + > expecting success: echo foo + > foo + > Z + > ok 2 - test with output + > Z + > not ok 3 - failing test + > # false + > # failed 1 among 3 test(s) + > 1..3 + EOF +' + test_set_prereq HAVEIT haveit=no test_expect_success HAVEIT 'test runs if prerequisite is satisfied' ' - test_have_prereq HAVEIT && - haveit=yes + test_have_prereq HAVEIT && + haveit=yes ' donthaveit=yes test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' ' - donthaveit=no + donthaveit=no ' if test $haveit$donthaveit != yesyes then @@ -107,17 +294,17 @@ fi test_set_prereq HAVETHIS haveit=no test_expect_success HAVETHIS,HAVEIT 'test runs if prerequisites are satisfied' ' - test_have_prereq HAVEIT && - test_have_prereq HAVETHIS && - haveit=yes + test_have_prereq HAVEIT && + test_have_prereq HAVETHIS && + haveit=yes ' donthaveit=yes test_expect_success HAVEIT,DONTHAVEIT 'unmet prerequisites causes test to be skipped' ' - donthaveit=no + donthaveit=no ' donthaveiteither=yes test_expect_success DONTHAVEIT,HAVEIT 'unmet prerequisites causes test to be skipped' ' - donthaveiteither=no + donthaveiteither=no ' if test $haveit$donthaveit$donthaveiteither != yesyesyes then @@ -125,258 +312,265 @@ then exit 1 fi -clean=no -test_expect_success 'tests clean up after themselves' ' - test_when_finished clean=yes +test_lazy_prereq LAZY_TRUE true +havetrue=no +test_expect_success LAZY_TRUE 'test runs if lazy prereq is satisfied' ' + havetrue=yes +' +donthavetrue=yes +test_expect_success !LAZY_TRUE 'missing lazy prereqs skip tests' ' + donthavetrue=no ' -if test $clean != yes +if test "$havetrue$donthavetrue" != yesyes then - say "bug in test framework: basic cleanup command does not work reliably" + say 'bug in test framework: lazy prerequisites do not work' exit 1 fi -test_expect_success 'tests clean up even on failures' " - mkdir failing-cleanup && - (cd failing-cleanup && - cat >failing-cleanup.sh <<EOF && -#!$SHELL_PATH - -test_description='Failing tests with cleanup commands' +test_lazy_prereq LAZY_FALSE false +nothavefalse=no +test_expect_success !LAZY_FALSE 'negative lazy prereqs checked' ' + nothavefalse=yes +' +havefalse=yes +test_expect_success LAZY_FALSE 'missing negative lazy prereqs will skip' ' + havefalse=no +' -# Point to the t/test-lib.sh, which isn't in ../ as usual -TEST_DIRECTORY=\"$TEST_DIRECTORY\" -. \"\$TEST_DIRECTORY\"/test-lib.sh +if test "$nothavefalse$havefalse" != yesyes +then + say 'bug in test framework: negative lazy prerequisites do not work' + exit 1 +fi -test_expect_success 'tests clean up even after a failure' ' - touch clean-after-failure && - test_when_finished rm clean-after-failure && - (exit 1) +clean=no +test_expect_success 'tests clean up after themselves' ' + test_when_finished clean=yes ' -test_expect_success 'failure to clean up causes the test to fail' ' - test_when_finished \"(exit 2)\" -' +if test $clean != yes +then + say "bug in test framework: basic cleanup command does not work reliably" + exit 1 +fi -test_done -EOF - chmod +x failing-cleanup.sh && - test_must_fail ./failing-cleanup.sh >out 2>err && - ! test -s err && - ! test -f \"trash directory.failing-cleanup/clean-after-failure\" && -sed -e 's/Z$//' -e 's/^> //' >expect <<\EOF && -> not ok - 1 tests clean up even after a failure -> # Z -> # touch clean-after-failure && -> # test_when_finished rm clean-after-failure && -> # (exit 1) -> # Z -> not ok - 2 failure to clean up causes the test to fail -> # Z -> # test_when_finished \"(exit 2)\" -> # Z -> # failed 2 among 2 test(s) -> 1..2 -EOF - test_cmp expect out) +test_expect_success 'tests clean up even on failures' " + test_must_fail run_sub_test_lib_test \ + failing-cleanup 'Failing tests with cleanup commands' <<-\\EOF && + test_expect_success 'tests clean up even after a failure' ' + touch clean-after-failure && + test_when_finished rm clean-after-failure && + (exit 1) + ' + test_expect_success 'failure to clean up causes the test to fail' ' + test_when_finished \"(exit 2)\" + ' + test_done + EOF + check_sub_test_lib_test failing-cleanup <<-\\EOF + > not ok 1 - tests clean up even after a failure + > # Z + > # touch clean-after-failure && + > # test_when_finished rm clean-after-failure && + > # (exit 1) + > # Z + > not ok 2 - failure to clean up causes the test to fail + > # Z + > # test_when_finished \"(exit 2)\" + > # Z + > # failed 2 among 2 test(s) + > 1..2 + EOF " ################################################################ # Basics of the basics # updating a new file without --add should fail. -test_expect_success 'git update-index without --add should fail adding.' ' - test_must_fail git update-index should-be-empty +test_expect_success 'git update-index without --add should fail adding' ' + test_must_fail 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 '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)' +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' +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_success 'git update-index without --remove should fail removing.' ' - test_must_fail git update-index should-be-empty +test_expect_success 'git update-index without --remove should fail removing' ' + rm -f should-be-empty full-of-directories && + test_must_fail 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' +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 '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' +test_expect_success 'validate object ID of a known tree' ' + test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 +' # Various types of objects -# Some filesystems do not support symblic links; on such systems -# some expected values are different -mkdir path2 path3 path3/subp3 -paths='path0 path2/file2 path3/file3 path3/subp3/file3' -for p in $paths -do - echo "hello $p" >$p -done -if test_have_prereq SYMLINKS -then - for p in $paths - do - ln -s "hello $p" ${p}sym - done - expectfilter=cat - expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b - expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3 - expectedptree2=3c5e5399f3a333eddecce7a9b9465b63f65f51e2 -else - expectfilter='grep -v sym' - expectedtree=8e18edf7d7edcf4371a3ac6ae5f07c2641db7c46 - expectedptree1=cfb8591b2f65de8b8cc1020cd7d9e67e7793b325 - expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f -fi -test_expect_success \ - 'adding various types of objects with git update-index --add.' \ - 'find path* ! -type d -print | xargs git update-index --add' +test_expect_success 'adding various types of objects with git update-index --add' ' + mkdir path2 path3 path3/subp3 && + paths="path0 path2/file2 path3/file3 path3/subp3/file3" && + ( + for p in $paths + do + echo "hello $p" >$p || exit 1 + test_ln_s_add "hello $p" ${p}sym || exit 1 + done + ) && + 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' - -$expectfilter >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.' \ - 'test_cmp expected current' - -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" = "$expectedtree"' - -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 SYMLINKS \ - 'git ls-tree output for a known tree.' \ - 'test_cmp expected current' +test_expect_success 'showing stage with git ls-files --stage' ' + git ls-files --stage >current +' + +test_expect_success 'validate git ls-files output for a known tree' ' + 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_cmp expected current +' + +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 +' + +test_expect_success 'git ls-tree output for a known tree' ' + cat >expected <<-\EOF && + 100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0 + 120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym + 040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2 + 040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3 + EOF + test_cmp expected current +' # 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' -$expectfilter >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.' \ - 'test_cmp expected current' +test_expect_success 'showing tree with git ls-tree -r' ' + git ls-tree -r $tree >current +' + +test_expect_success 'git ls-tree -r output for a known tree' ' + 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_cmp expected current +' # 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 SYMLINKS \ - 'git ls-tree -r output for a known tree.' \ - 'test_cmp expected current' - -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" = "$expectedptree1"' - -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" = "$expectedptree2"' - -cat >badobjects <<EOF -100644 blob 1000000000000000000000000000000000000000 dir/file1 -100644 blob 2000000000000000000000000000000000000000 dir/file2 -100644 blob 3000000000000000000000000000000000000000 dir/file3 -100644 blob 4000000000000000000000000000000000000000 dir/file4 -100644 blob 5000000000000000000000000000000000000000 dir/file5 -EOF +test_expect_success 'showing tree with git ls-tree -r -t' ' + git ls-tree -r -t $tree >current +' + +test_expect_success 'git ls-tree -r output for a known tree' ' + 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_cmp expected current +' + +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) +' -rm .git/index -test_expect_success \ - 'put invalid objects into the index.' \ - 'git update-index --index-info < badobjects' +test_expect_success 'validate object ID for a known tree' ' + test "$ptree" = 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 +' -test_expect_success 'writing this tree without --missing-ok.' ' - test_must_fail git write-tree +test_expect_success 'put invalid objects into the index' ' + rm -f .git/index && + cat >badobjects <<-\EOF && + 100644 blob 1000000000000000000000000000000000000000 dir/file1 + 100644 blob 2000000000000000000000000000000000000000 dir/file2 + 100644 blob 3000000000000000000000000000000000000000 dir/file3 + 100644 blob 4000000000000000000000000000000000000000 dir/file4 + 100644 blob 5000000000000000000000000000000000000000 dir/file5 + EOF + git update-index --index-info <badobjects ' -test_expect_success \ - 'writing this tree with --missing-ok.' \ - 'git write-tree --missing-ok' +test_expect_success 'writing this tree without --missing-ok' ' + test_must_fail git write-tree +' + +test_expect_success 'writing this tree with --missing-ok' ' + git write-tree --missing-ok +' ################################################################ -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"' - -$expectfilter >expected <<\EOF +test_expect_success 'git read-tree followed by write-tree should be idempotent' ' + rm -f .git/index + git read-tree $tree && + test -f .git/index && + newtree=$(git write-tree) && + test "$newtree" = "$tree" +' + +test_expect_success 'validate git diff-files output for a know cache/work tree state' ' + cat >expected <<\EOF && :100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M path0 :120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M path0sym :100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M path2/file2 @@ -386,45 +580,47 @@ $expectfilter >expected <<\EOF :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 && test_cmp current expected >/dev/null' + git diff-files >current && + test_cmp current expected +' -test_expect_success \ - 'git update-index --refresh should succeed.' \ - 'git update-index --refresh' +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' +test_expect_success 'no diff after checkout and git update-index --refresh' ' + git diff-files >current && + cmp -s current /dev/null +' ################################################################ -P=$expectedtree -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' +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_expect_success 'update-index D/F conflict' ' mv path0 tmp && @@ -435,24 +631,6 @@ test_expect_success 'update-index D/F conflict' ' test $numpath0 = 1 ' -test_expect_success SYMLINKS 'real path works as expected' ' - mkdir first && - ln -s ../.git first/.git && - mkdir second && - ln -s ../first second/other && - mkdir third && - dir="$(cd .git; pwd -P)" && - dir2=third/../second/other/.git && - test "$dir" = "$(test-path-utils real_path $dir2)" && - file="$dir"/index && - test "$file" = "$(test-path-utils real_path $dir2/index)" && - basename=blub && - test "$dir/$basename" = "$(cd .git && test-path-utils real_path "$basename")" && - ln -s ../first/file .git/syml && - sym="$(cd first; pwd -P)"/file && - test "$sym" = "$(test-path-utils real_path "$dir2/syml")" -' - test_expect_success 'very long name in the index handled sanely' ' a=a && # 1 diff --git a/t/t0001-init.sh b/t/t0001-init.sh index ad66410564..9fb582b192 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -379,6 +379,10 @@ test_expect_success 'init with separate gitdir' ' test -d realgitdir/refs ' +test_expect_success 're-init on .git file' ' + ( cd newdir && git init ) +' + test_expect_success 're-init to update git link' ' ( cd newdir && diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index 51f3045ba4..0b98b6f8d0 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -123,16 +123,6 @@ test_expect_success 'attribute matching is case insensitive when core.ignorecase ' -test_expect_success 'check whether FS is case-insensitive' ' - mkdir junk && - echo good >junk/CamelCase && - echo bad >junk/camelcase && - if test "$(cat junk/CamelCase)" != good - then - test_set_prereq CASE_INSENSITIVE_FS - fi -' - test_expect_success CASE_INSENSITIVE_FS 'additional case insensitivity tests' ' test_must_fail attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=0" && test_must_fail attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=0" && @@ -206,6 +196,54 @@ test_expect_success 'root subdir attribute test' ' attr_check subdir/a/i unspecified ' +test_expect_success 'negative patterns' ' + echo "!f test=bar" >.gitattributes && + git check-attr test -- '"'"'!f'"'"' 2>errors && + test_i18ngrep "Negative patterns are ignored" errors +' + +test_expect_success 'patterns starting with exclamation' ' + echo "\!f test=foo" >.gitattributes && + attr_check "!f" foo +' + +test_expect_success '"**" test' ' + echo "**/f foo=bar" >.gitattributes && + cat <<\EOF >expect && +f: foo: bar +a/f: foo: bar +a/b/f: foo: bar +a/b/c/f: foo: bar +EOF + git check-attr foo -- "f" >actual 2>err && + git check-attr foo -- "a/f" >>actual 2>>err && + git check-attr foo -- "a/b/f" >>actual 2>>err && + git check-attr foo -- "a/b/c/f" >>actual 2>>err && + test_cmp expect actual && + test_line_count = 0 err +' + +test_expect_success '"**" with no slashes test' ' + echo "a**f foo=bar" >.gitattributes && + git check-attr foo -- "f" >actual && + cat <<\EOF >expect && +f: foo: unspecified +af: foo: bar +axf: foo: bar +a/f: foo: unspecified +a/b/f: foo: unspecified +a/b/c/f: foo: unspecified +EOF + git check-attr foo -- "f" >actual 2>err && + git check-attr foo -- "af" >>actual 2>err && + git check-attr foo -- "axf" >>actual 2>err && + git check-attr foo -- "a/f" >>actual 2>>err && + git check-attr foo -- "a/b/f" >>actual 2>>err && + git check-attr foo -- "a/b/c/f" >>actual 2>>err && + test_cmp expect actual && + test_line_count = 0 err +' + test_expect_success 'setup bare' ' git clone --bare . bare.git && cd bare.git diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh index 93e58c00e8..981437b3a8 100755 --- a/t/t0005-signals.sh +++ b/t/t0005-signals.sh @@ -20,4 +20,11 @@ test_expect_success 'sigchain works' ' test_cmp expect actual ' +test_expect_success !MINGW 'signals are propagated using shell convention' ' + # we use exec here to avoid any sub-shell interpretation + # of the exit code + git config alias.sigterm "!exec test-sigchain" && + test_expect_code 143 git sigterm +' + test_done diff --git a/t/t0006-date.sh b/t/t0006-date.sh index 1d29810a7a..e53cf6d36d 100755 --- a/t/t0006-date.sh +++ b/t/t0006-date.sh @@ -11,7 +11,7 @@ check_show() { echo "$t -> $2" >expect test_expect_${3:-success} "relative date ($2)" " test-date show $t >actual && - test_cmp expect actual + test_i18ncmp expect actual " } diff --git a/t/t0007-git-var.sh b/t/t0007-git-var.sh new file mode 100755 index 0000000000..5868a87352 --- /dev/null +++ b/t/t0007-git-var.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +test_description='basic sanity checks for git var' +. ./test-lib.sh + +test_expect_success 'get GIT_AUTHOR_IDENT' ' + test_tick && + echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect && + git var GIT_AUTHOR_IDENT >actual && + test_cmp expect actual +' + +test_expect_success 'get GIT_COMMITTER_IDENT' ' + test_tick && + echo "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE" >expect && + git var GIT_COMMITTER_IDENT >actual && + test_cmp expect actual +' + +test_expect_success !AUTOIDENT 'requested identites are strict' ' + ( + sane_unset GIT_COMMITTER_NAME && + sane_unset GIT_COMMITTER_EMAIL && + test_must_fail git var GIT_COMMITTER_IDENT + ) +' + +# For git var -l, we check only a representative variable; +# testing the whole output would make our test too brittle with +# respect to unrelated changes in the test suite's environment. +test_expect_success 'git var -l lists variables' ' + git var -l >actual && + echo "$GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect && + sed -n s/GIT_AUTHOR_IDENT=//p <actual >actual.author && + test_cmp expect actual.author +' + +test_expect_success 'git var -l lists config' ' + git var -l >actual && + echo false >expect && + sed -n s/core\\.bare=//p <actual >actual.bare && + test_cmp expect actual.bare +' + +test_expect_success 'listing and asking for variables are exclusive' ' + test_must_fail git var -l GIT_COMMITTER_IDENT +' + +test_done diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh new file mode 100755 index 0000000000..b4d98e602f --- /dev/null +++ b/t/t0008-ignores.sh @@ -0,0 +1,778 @@ +#!/bin/sh + +test_description=check-ignore + +. ./test-lib.sh + +init_vars () { + global_excludes="$(pwd)/global-excludes" +} + +enable_global_excludes () { + init_vars && + git config core.excludesfile "$global_excludes" +} + +expect_in () { + dest="$HOME/expected-$1" text="$2" + if test -z "$text" + then + >"$dest" # avoid newline + else + echo "$text" >"$dest" + fi +} + +expect () { + expect_in stdout "$1" +} + +expect_from_stdin () { + cat >"$HOME/expected-stdout" +} + +test_stderr () { + expected="$1" + expect_in stderr "$1" && + test_cmp "$HOME/expected-stderr" "$HOME/stderr" +} + +broken_c_unquote () { + "$PERL_PATH" -pe 's/^"//; s/\\//; s/"$//; tr/\n/\0/' "$@" +} + +broken_c_unquote_verbose () { + "$PERL_PATH" -pe 's/ "/ /; s/\\//; s/"$//; tr/:\t\n/\0/' "$@" +} + +stderr_contains () { + regexp="$1" + if grep "$regexp" "$HOME/stderr" + then + return 0 + else + echo "didn't find /$regexp/ in $HOME/stderr" + cat "$HOME/stderr" + return 1 + fi +} + +stderr_empty_on_success () { + expect_code="$1" + if test $expect_code = 0 + then + test_stderr "" + else + # If we expect failure then stderr might or might not be empty + # due to --quiet - the caller can check its contents + return 0 + fi +} + +test_check_ignore () { + args="$1" expect_code="${2:-0}" global_args="$3" + + init_vars && + rm -f "$HOME/stdout" "$HOME/stderr" "$HOME/cmd" && + echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $no_index_opt $args \ + >"$HOME/cmd" && + echo "$expect_code" >"$HOME/expected-exit-code" && + test_expect_code "$expect_code" \ + git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $no_index_opt $args \ + >"$HOME/stdout" 2>"$HOME/stderr" && + test_cmp "$HOME/expected-stdout" "$HOME/stdout" && + stderr_empty_on_success "$expect_code" +} + +# Runs the same code with 4 different levels of output verbosity: +# +# 1. with -q / --quiet +# 2. with default verbosity +# 3. with -v / --verbose +# 4. with -v / --verbose, *and* -n / --non-matching +# +# expecting success each time. Takes advantage of the fact that +# check-ignore --verbose output is the same as normal output except +# for the extra first column. +# +# A parameter is used to determine if the tests are run with the +# normal case (using the index), or with the --no-index option. +# +# Arguments: +# - (optional) prereqs for this test, e.g. 'SYMLINKS' +# - test name +# - output to expect from the fourth verbosity mode (the output +# from the other verbosity modes is automatically inferred +# from this value) +# - code to run (should invoke test_check_ignore) +# - index option: --index or --no-index +test_expect_success_multiple () { + prereq= + if test $# -eq 5 + then + prereq=$1 + shift + fi + if test "$4" = "--index" + then + no_index_opt= + else + no_index_opt=$4 + fi + testname="$1" expect_all="$2" code="$3" + + expect_verbose=$( echo "$expect_all" | grep -v '^:: ' ) + expect=$( echo "$expect_verbose" | sed -e 's/.* //' ) + + test_expect_success $prereq "$testname${no_index_opt:+ with $no_index_opt}" ' + expect "$expect" && + eval "$code" + ' + + # --quiet is only valid when a single pattern is passed + if test $( echo "$expect_all" | wc -l ) = 1 + then + for quiet_opt in '-q' '--quiet' + do + opts="${no_index_opt:+$no_index_opt }$quiet_opt" + test_expect_success $prereq "$testname${opts:+ with $opts}" " + expect '' && + $code + " + done + quiet_opt= + fi + + for verbose_opt in '-v' '--verbose' + do + for non_matching_opt in '' '-n' '--non-matching' + do + if test -n "$non_matching_opt" + then + my_expect="$expect_all" + else + my_expect="$expect_verbose" + fi + + test_code=" + expect '$my_expect' && + $code + " + opts="${no_index_opt:+$no_index_opt }$verbose_opt${non_matching_opt:+ $non_matching_opt}" + test_expect_success $prereq "$testname${opts:+ with $opts}" "$test_code" + done + done + verbose_opt= + non_matching_opt= + no_index_opt= +} + +test_expect_success_multi () { + test_expect_success_multiple "$@" "--index" +} + +test_expect_success_no_index_multi () { + test_expect_success_multiple "$@" "--no-index" +} + +test_expect_success 'setup' ' + init_vars && + mkdir -p a/b/ignored-dir a/submodule b && + if test_have_prereq SYMLINKS + then + ln -s b a/symlink + fi && + ( + cd a/submodule && + git init && + echo a >a && + git add a && + git commit -m"commit in submodule" + ) && + git add a/submodule && + cat <<-\EOF >.gitignore && + one + ignored-* + top-level-dir/ + EOF + for dir in . a + do + : >$dir/not-ignored && + : >$dir/ignored-and-untracked && + : >$dir/ignored-but-in-index + done && + git add -f ignored-but-in-index a/ignored-but-in-index && + cat <<-\EOF >a/.gitignore && + two* + *three + EOF + cat <<-\EOF >a/b/.gitignore && + four + five + # this comment should affect the line numbers + six + ignored-dir/ + # and so should this blank line: + + !on* + !two + EOF + echo "seven" >a/b/ignored-dir/.gitignore && + test -n "$HOME" && + cat <<-\EOF >"$global_excludes" && + globalone + !globaltwo + globalthree + EOF + cat <<-\EOF >>.git/info/exclude + per-repo + EOF +' + +############################################################################ +# +# test invalid inputs + +test_expect_success_multi '. corner-case' ':: .' ' + test_check_ignore . 1 +' + +test_expect_success_multi 'empty command line' '' ' + test_check_ignore "" 128 && + stderr_contains "fatal: no path specified" +' + +test_expect_success_multi '--stdin with empty STDIN' '' ' + test_check_ignore "--stdin" 1 </dev/null && + test_stderr "" +' + +test_expect_success '-q with multiple args' ' + expect "" && + test_check_ignore "-q one two" 128 && + stderr_contains "fatal: --quiet is only valid with a single pathname" +' + +test_expect_success '--quiet with multiple args' ' + expect "" && + test_check_ignore "--quiet one two" 128 && + stderr_contains "fatal: --quiet is only valid with a single pathname" +' + +for verbose_opt in '-v' '--verbose' +do + for quiet_opt in '-q' '--quiet' + do + test_expect_success "$quiet_opt $verbose_opt" " + expect '' && + test_check_ignore '$quiet_opt $verbose_opt foo' 128 && + stderr_contains 'fatal: cannot have both --quiet and --verbose' + " + done +done + +test_expect_success '--quiet with multiple args' ' + expect "" && + test_check_ignore "--quiet one two" 128 && + stderr_contains "fatal: --quiet is only valid with a single pathname" +' + +test_expect_success_multi 'erroneous use of --' '' ' + test_check_ignore "--" 128 && + stderr_contains "fatal: no path specified" +' + +test_expect_success_multi '--stdin with superfluous arg' '' ' + test_check_ignore "--stdin foo" 128 && + stderr_contains "fatal: cannot specify pathnames with --stdin" +' + +test_expect_success_multi '--stdin -z with superfluous arg' '' ' + test_check_ignore "--stdin -z foo" 128 && + stderr_contains "fatal: cannot specify pathnames with --stdin" +' + +test_expect_success_multi '-z without --stdin' '' ' + test_check_ignore "-z" 128 && + stderr_contains "fatal: -z only makes sense with --stdin" +' + +test_expect_success_multi '-z without --stdin and superfluous arg' '' ' + test_check_ignore "-z foo" 128 && + stderr_contains "fatal: -z only makes sense with --stdin" +' + +test_expect_success_multi 'needs work tree' '' ' + ( + cd .git && + test_check_ignore "foo" 128 + ) && + stderr_contains "fatal: This operation must be run in a work tree" +' + +############################################################################ +# +# test standard ignores + +# First make sure that the presence of a file in the working tree +# does not impact results, but that the presence of a file in the +# index does unless the --no-index option is used. + +for subdir in '' 'a/' +do + if test -z "$subdir" + then + where="at top-level" + else + where="in subdir $subdir" + fi + + test_expect_success_multi "non-existent file $where not ignored" \ + ":: ${subdir}non-existent" \ + "test_check_ignore '${subdir}non-existent' 1" + + test_expect_success_no_index_multi "non-existent file $where not ignored" \ + ":: ${subdir}non-existent" \ + "test_check_ignore '${subdir}non-existent' 1" + + test_expect_success_multi "non-existent file $where ignored" \ + ".gitignore:1:one ${subdir}one" \ + "test_check_ignore '${subdir}one'" + + test_expect_success_no_index_multi "non-existent file $where ignored" \ + ".gitignore:1:one ${subdir}one" \ + "test_check_ignore '${subdir}one'" + + test_expect_success_multi "existing untracked file $where not ignored" \ + ":: ${subdir}not-ignored" \ + "test_check_ignore '${subdir}not-ignored' 1" + + test_expect_success_no_index_multi "existing untracked file $where not ignored" \ + ":: ${subdir}not-ignored" \ + "test_check_ignore '${subdir}not-ignored' 1" + + test_expect_success_multi "existing tracked file $where not ignored" \ + ":: ${subdir}ignored-but-in-index" \ + "test_check_ignore '${subdir}ignored-but-in-index' 1" + + test_expect_success_no_index_multi "existing tracked file $where shown as ignored" \ + ".gitignore:2:ignored-* ${subdir}ignored-but-in-index" \ + "test_check_ignore '${subdir}ignored-but-in-index'" + + test_expect_success_multi "existing untracked file $where ignored" \ + ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" \ + "test_check_ignore '${subdir}ignored-and-untracked'" + + test_expect_success_no_index_multi "existing untracked file $where ignored" \ + ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" \ + "test_check_ignore '${subdir}ignored-and-untracked'" + + test_expect_success_multi "mix of file types $where" \ +":: ${subdir}non-existent +.gitignore:1:one ${subdir}one +:: ${subdir}not-ignored +:: ${subdir}ignored-but-in-index +.gitignore:2:ignored-* ${subdir}ignored-and-untracked" \ + "test_check_ignore ' + ${subdir}non-existent + ${subdir}one + ${subdir}not-ignored + ${subdir}ignored-but-in-index + ${subdir}ignored-and-untracked' + " + + test_expect_success_no_index_multi "mix of file types $where" \ +":: ${subdir}non-existent +.gitignore:1:one ${subdir}one +:: ${subdir}not-ignored +.gitignore:2:ignored-* ${subdir}ignored-but-in-index +.gitignore:2:ignored-* ${subdir}ignored-and-untracked" \ + "test_check_ignore ' + ${subdir}non-existent + ${subdir}one + ${subdir}not-ignored + ${subdir}ignored-but-in-index + ${subdir}ignored-and-untracked' + " +done + +# Having established the above, from now on we mostly test against +# files which do not exist in the working tree or index. + +test_expect_success 'sub-directory local ignore' ' + expect "a/3-three" && + test_check_ignore "a/3-three a/three-not-this-one" +' + +test_expect_success 'sub-directory local ignore with --verbose' ' + expect "a/.gitignore:2:*three a/3-three" && + test_check_ignore "--verbose a/3-three a/three-not-this-one" +' + +test_expect_success 'local ignore inside a sub-directory' ' + expect "3-three" && + ( + cd a && + test_check_ignore "3-three three-not-this-one" + ) +' +test_expect_success 'local ignore inside a sub-directory with --verbose' ' + expect "a/.gitignore:2:*three 3-three" && + ( + cd a && + test_check_ignore "--verbose 3-three three-not-this-one" + ) +' + +test_expect_success_multi 'nested include' \ + 'a/b/.gitignore:8:!on* a/b/one' ' + test_check_ignore "a/b/one" +' + +############################################################################ +# +# test ignored sub-directories + +test_expect_success_multi 'ignored sub-directory' \ + 'a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir' ' + test_check_ignore "a/b/ignored-dir" +' + +test_expect_success 'multiple files inside ignored sub-directory' ' + expect_from_stdin <<-\EOF && + a/b/ignored-dir/foo + a/b/ignored-dir/twoooo + a/b/ignored-dir/seven + EOF + test_check_ignore "a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven" +' + +test_expect_success 'multiple files inside ignored sub-directory with -v' ' + expect_from_stdin <<-\EOF && + a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/foo + a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/twoooo + a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/seven + EOF + test_check_ignore "-v a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven" +' + +test_expect_success 'cd to ignored sub-directory' ' + expect_from_stdin <<-\EOF && + foo + twoooo + ../one + seven + ../../one + EOF + ( + cd a/b/ignored-dir && + test_check_ignore "foo twoooo ../one seven ../../one" + ) +' + +test_expect_success 'cd to ignored sub-directory with -v' ' + expect_from_stdin <<-\EOF && + a/b/.gitignore:5:ignored-dir/ foo + a/b/.gitignore:5:ignored-dir/ twoooo + a/b/.gitignore:8:!on* ../one + a/b/.gitignore:5:ignored-dir/ seven + .gitignore:1:one ../../one + EOF + ( + cd a/b/ignored-dir && + test_check_ignore "-v foo twoooo ../one seven ../../one" + ) +' + +############################################################################ +# +# test handling of symlinks + +test_expect_success_multi SYMLINKS 'symlink' ':: a/symlink' ' + test_check_ignore "a/symlink" 1 +' + +test_expect_success_multi SYMLINKS 'beyond a symlink' '' ' + test_check_ignore "a/symlink/foo" 128 && + test_stderr "fatal: pathspec '\''a/symlink/foo'\'' is beyond a symbolic link" +' + +test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' ' + ( + cd a && + test_check_ignore "symlink/foo" 128 + ) && + test_stderr "fatal: pathspec '\''symlink/foo'\'' is beyond a symbolic link" +' + +############################################################################ +# +# test handling of submodules + +test_expect_success_multi 'submodule' '' ' + test_check_ignore "a/submodule/one" 128 && + test_stderr "fatal: Pathspec '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''" +' + +test_expect_success_multi 'submodule from subdirectory' '' ' + ( + cd a && + test_check_ignore "submodule/one" 128 + ) && + test_stderr "fatal: Pathspec '\''submodule/one'\'' is in submodule '\''a/submodule'\''" +' + +############################################################################ +# +# test handling of global ignore files + +test_expect_success 'global ignore not yet enabled' ' + expect_from_stdin <<-\EOF && + .git/info/exclude:7:per-repo per-repo + a/.gitignore:2:*three a/globalthree + .git/info/exclude:7:per-repo a/per-repo + EOF + test_check_ignore "-v globalone per-repo a/globalthree a/per-repo not-ignored a/globaltwo" +' + +test_expect_success 'global ignore' ' + enable_global_excludes && + expect_from_stdin <<-\EOF && + globalone + per-repo + globalthree + a/globalthree + a/per-repo + globaltwo + EOF + test_check_ignore "globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo" +' + +test_expect_success 'global ignore with -v' ' + enable_global_excludes && + expect_from_stdin <<-EOF && + $global_excludes:1:globalone globalone + .git/info/exclude:7:per-repo per-repo + $global_excludes:3:globalthree globalthree + a/.gitignore:2:*three a/globalthree + .git/info/exclude:7:per-repo a/per-repo + $global_excludes:2:!globaltwo globaltwo + EOF + test_check_ignore "-v globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo" +' + +############################################################################ +# +# test --stdin + +cat <<-\EOF >stdin + one + not-ignored + a/one + a/not-ignored + a/b/on + a/b/one + a/b/one one + "a/b/one two" + "a/b/one\"three" + a/b/not-ignored + a/b/two + a/b/twooo + globaltwo + a/globaltwo + a/b/globaltwo + b/globaltwo +EOF +cat <<-\EOF >expected-default + one + a/one + a/b/on + a/b/one + a/b/one one + a/b/one two + "a/b/one\"three" + a/b/two + a/b/twooo + globaltwo + a/globaltwo + a/b/globaltwo + b/globaltwo +EOF +cat <<-EOF >expected-verbose + .gitignore:1:one one + .gitignore:1:one a/one + a/b/.gitignore:8:!on* a/b/on + a/b/.gitignore:8:!on* a/b/one + a/b/.gitignore:8:!on* a/b/one one + a/b/.gitignore:8:!on* a/b/one two + a/b/.gitignore:8:!on* "a/b/one\"three" + a/b/.gitignore:9:!two a/b/two + a/.gitignore:1:two* a/b/twooo + $global_excludes:2:!globaltwo globaltwo + $global_excludes:2:!globaltwo a/globaltwo + $global_excludes:2:!globaltwo a/b/globaltwo + $global_excludes:2:!globaltwo b/globaltwo +EOF + +broken_c_unquote stdin >stdin0 + +broken_c_unquote expected-default >expected-default0 + +broken_c_unquote_verbose expected-verbose >expected-verbose0 + +test_expect_success '--stdin' ' + expect_from_stdin <expected-default && + test_check_ignore "--stdin" <stdin +' + +test_expect_success '--stdin -q' ' + expect "" && + test_check_ignore "-q --stdin" <stdin +' + +test_expect_success '--stdin -v' ' + expect_from_stdin <expected-verbose && + test_check_ignore "-v --stdin" <stdin +' + +for opts in '--stdin -z' '-z --stdin' +do + test_expect_success "$opts" " + expect_from_stdin <expected-default0 && + test_check_ignore '$opts' <stdin0 + " + + test_expect_success "$opts -q" " + expect "" && + test_check_ignore '-q $opts' <stdin0 + " + + test_expect_success "$opts -v" " + expect_from_stdin <expected-verbose0 && + test_check_ignore '-v $opts' <stdin0 + " +done + +cat <<-\EOF >stdin + ../one + ../not-ignored + one + not-ignored + b/on + b/one + b/one one + "b/one two" + "b/one\"three" + b/two + b/not-ignored + b/twooo + ../globaltwo + globaltwo + b/globaltwo + ../b/globaltwo + c/not-ignored +EOF +# N.B. we deliberately end STDIN with a non-matching pattern in order +# to test that the exit code indicates that one or more of the +# provided paths is ignored - in other words, that it represents an +# aggregation of all the results, not just the final result. + +cat <<-EOF >expected-all + .gitignore:1:one ../one + :: ../not-ignored + .gitignore:1:one one + :: not-ignored + a/b/.gitignore:8:!on* b/on + a/b/.gitignore:8:!on* b/one + a/b/.gitignore:8:!on* b/one one + a/b/.gitignore:8:!on* b/one two + a/b/.gitignore:8:!on* "b/one\"three" + a/b/.gitignore:9:!two b/two + :: b/not-ignored + a/.gitignore:1:two* b/twooo + $global_excludes:2:!globaltwo ../globaltwo + $global_excludes:2:!globaltwo globaltwo + $global_excludes:2:!globaltwo b/globaltwo + $global_excludes:2:!globaltwo ../b/globaltwo + :: c/not-ignored +EOF +grep -v '^:: ' expected-all >expected-verbose +sed -e 's/.* //' expected-verbose >expected-default + +broken_c_unquote stdin >stdin0 + +broken_c_unquote expected-default >expected-default0 + +broken_c_unquote_verbose expected-verbose >expected-verbose0 + +test_expect_success '--stdin from subdirectory' ' + expect_from_stdin <expected-default && + ( + cd a && + test_check_ignore "--stdin" <../stdin + ) +' + +test_expect_success '--stdin from subdirectory with -v' ' + expect_from_stdin <expected-verbose && + ( + cd a && + test_check_ignore "--stdin -v" <../stdin + ) +' + +test_expect_success '--stdin from subdirectory with -v -n' ' + expect_from_stdin <expected-all && + ( + cd a && + test_check_ignore "--stdin -v -n" <../stdin + ) +' + +for opts in '--stdin -z' '-z --stdin' +do + test_expect_success "$opts from subdirectory" ' + expect_from_stdin <expected-default0 && + ( + cd a && + test_check_ignore "'"$opts"'" <../stdin0 + ) + ' + + test_expect_success "$opts from subdirectory with -v" ' + expect_from_stdin <expected-verbose0 && + ( + cd a && + test_check_ignore "'"$opts"' -v" <../stdin0 + ) + ' +done + +test_expect_success PIPE 'streaming support for --stdin' ' + mkfifo in out && + (git check-ignore -n -v --stdin <in >out &) && + + # We cannot just "echo >in" because check-ignore would get EOF + # after echo exited; instead we open the descriptor in our + # shell, and then echo to the fd. We make sure to close it at + # the end, so that the subprocess does get EOF and dies + # properly. + # + # Similarly, we must keep "out" open so that check-ignore does + # not ever get SIGPIPE trying to write to us. Not only would that + # produce incorrect results, but then there would be no writer on the + # other end of the pipe, and we would potentially block forever trying + # to open it. + exec 9>in && + exec 8<out && + test_when_finished "exec 9>&-" && + test_when_finished "exec 8<&-" && + echo >&9 one && + read response <&8 && + echo "$response" | grep "^\.gitignore:1:one one" && + echo >&9 two && + read response <&8 && + echo "$response" | grep "^:: two" +' + +test_done diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh new file mode 100755 index 0000000000..94045c3fad --- /dev/null +++ b/t/t0009-prio-queue.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +test_description='basic tests for priority queue implementation' +. ./test-lib.sh + +cat >expect <<'EOF' +1 +2 +3 +4 +5 +5 +6 +7 +8 +9 +10 +EOF +test_expect_success 'basic ordering' ' + test-prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +2 +3 +4 +1 +5 +6 +EOF +test_expect_success 'mixed put and get' ' + test-prio-queue 6 2 4 get 5 3 get get 1 dump >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +1 +2 +NULL +1 +2 +NULL +EOF +test_expect_success 'notice empty queue' ' + test-prio-queue 1 2 get get get 1 2 get get get >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh index 1a8f44c44c..e526184a0b 100755 --- a/t/t0020-crlf.sh +++ b/t/t0020-crlf.sh @@ -81,6 +81,14 @@ test_expect_success 'safecrlf: print warning only once' ' test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1 ' + +test_expect_success 'safecrlf: git diff demotes safecrlf=true to warn' ' + git config core.autocrlf input && + git config core.safecrlf true && + git diff HEAD +' + + test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' ' git config core.autocrlf false && git config core.safecrlf false && diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index f19e6510d0..b92e6cb046 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -153,4 +153,55 @@ test_expect_success 'filter shell-escaped filenames' ' : ' +test_expect_success 'required filter success' ' + git config filter.required.smudge cat && + git config filter.required.clean cat && + git config filter.required.required true && + + echo "*.r filter=required" >.gitattributes && + + echo test >test.r && + git add test.r && + rm -f test.r && + git checkout -- test.r +' + +test_expect_success 'required filter smudge failure' ' + git config filter.failsmudge.smudge false && + git config filter.failsmudge.clean cat && + git config filter.failsmudge.required true && + + echo "*.fs filter=failsmudge" >.gitattributes && + + echo test >test.fs && + git add test.fs && + rm -f test.fs && + test_must_fail git checkout -- test.fs +' + +test_expect_success 'required filter clean failure' ' + git config filter.failclean.smudge cat && + git config filter.failclean.clean false && + git config filter.failclean.required true && + + echo "*.fc filter=failclean" >.gitattributes && + + echo test >test.fc && + test_must_fail git add test.fc +' + +test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE + +test_expect_success EXPENSIVE 'filter large file' ' + git config filter.largefile.smudge cat && + git config filter.largefile.clean cat && + for i in $(test_seq 1 2048); do printf "%1048576d" 1; done >2GB && + echo "2GB filter=largefile" >.gitattributes && + git add 2GB 2>err && + ! test -s err && + rm -f 2GB && + git checkout -- 2GB 2>err && + ! test -s err +' + test_done diff --git a/t/t0023-crlf-am.sh b/t/t0023-crlf-am.sh index aaed725402..f9bbb91f64 100755 --- a/t/t0023-crlf-am.sh +++ b/t/t0023-crlf-am.sh @@ -11,7 +11,7 @@ Date: Thu, 23 Aug 2007 13:00:00 +0200 Subject: test1 --- - foo | 1 + + foo | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 foo diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh index ec6c1b3f8a..4e9fa3cd68 100755 --- a/t/t0024-crlf-archive.sh +++ b/t/t0024-crlf-archive.sh @@ -3,7 +3,6 @@ test_description='respect crlf in git archive' . ./test-lib.sh -UNZIP=${UNZIP:-unzip} test_expect_success setup ' @@ -26,18 +25,11 @@ test_expect_success 'tar archive' ' ' -"$UNZIP" -v >/dev/null 2>&1 -if [ $? -eq 127 ]; then - say "Skipping ZIP test, because unzip was not found" -else - test_set_prereq UNZIP -fi - test_expect_success UNZIP 'zip archive' ' git archive --format=zip HEAD >test.zip && - ( mkdir unzipped && cd unzipped && unzip ../test.zip ) && + ( mkdir unzipped && cd unzipped && "$GIT_UNZIP" ../test.zip ) && test_cmp sample unzipped/sample diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index ccb0a3cb61..a8e84d8546 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -397,4 +397,39 @@ test_expect_success 'strip comments, too' ' test -z "$(echo "# comment" | git stripspace -s)" ' +test_expect_success 'strip comments with changed comment char' ' + test ! -z "$(echo "; comment" | git -c core.commentchar=";" stripspace)" && + test -z "$(echo "; comment" | git -c core.commentchar=";" stripspace -s)" +' + +test_expect_success '-c with single line' ' + printf "# foo\n" >expect && + printf "foo" | git stripspace -c >actual && + test_cmp expect actual +' + +test_expect_success '-c with single line followed by empty line' ' + printf "# foo\n#\n" >expect && + printf "foo\n\n" | git stripspace -c >actual && + test_cmp expect actual +' + +test_expect_success '-c with newline only' ' + printf "#\n" >expect && + printf "\n" | git stripspace -c >actual && + test_cmp expect actual +' + +test_expect_success '--comment-lines with single line' ' + printf "# foo\n" >expect && + printf "foo" | git stripspace -c >actual && + test_cmp expect actual +' + +test_expect_success '-c with changed comment char' ' + printf "; foo\n" >expect && + printf "foo" | git -c core.commentchar=";" stripspace -c >actual && + test_cmp expect actual +' + test_done diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index a1e4616feb..65606df3ed 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -10,7 +10,10 @@ test_description='our own option parser' cat > expect << EOF usage: test-parse-options <options> - -b, --boolean get a boolean + --yes get a boolean + -D, --no-doubt begins with 'no-' + -B, --no-fear be brave + -b, --boolean increment by one -4, --or4 bitwise-or boolean with ...0100 --neg-or4 same as --no-or4 @@ -47,12 +50,89 @@ EOF test_expect_success 'test help' ' test_must_fail test-parse-options -h > output 2> output.err && - test ! -s output.err && - test_cmp expect output + test_must_be_empty output.err && + test_i18ncmp expect output ' mv expect expect.err +cat >expect.template <<EOF +boolean: 0 +integer: 0 +timestamp: 0 +string: (not set) +abbrev: 7 +verbose: 0 +quiet: no +dry run: no +file: (not set) +EOF + +check() { + what="$1" && + shift && + expect="$1" && + shift && + sed "s/^$what .*/$what $expect/" <expect.template >expect && + test-parse-options $* >output 2>output.err && + test_must_be_empty output.err && + test_cmp expect output +} + +check_i18n() { + what="$1" && + shift && + expect="$1" && + shift && + sed "s/^$what .*/$what $expect/" <expect.template >expect && + test-parse-options $* >output 2>output.err && + test_must_be_empty output.err && + test_i18ncmp expect output +} + +check_unknown() { + case "$1" in + --*) + echo error: unknown option \`${1#--}\' >expect ;; + -*) + echo error: unknown switch \`${1#-}\' >expect ;; + esac && + cat expect.err >>expect && + test_must_fail test-parse-options $* >output 2>output.err && + test_must_be_empty output && + test_cmp expect output.err +} + +check_unknown_i18n() { + case "$1" in + --*) + echo error: unknown option \`${1#--}\' >expect ;; + -*) + echo error: unknown switch \`${1#-}\' >expect ;; + esac && + cat expect.err >>expect && + test_must_fail test-parse-options $* >output 2>output.err && + test_must_be_empty output && + test_i18ncmp expect output.err +} + +test_expect_success 'OPT_BOOL() #1' 'check boolean: 1 --yes' +test_expect_success 'OPT_BOOL() #2' 'check boolean: 1 --no-doubt' +test_expect_success 'OPT_BOOL() #3' 'check boolean: 1 -D' +test_expect_success 'OPT_BOOL() #4' 'check boolean: 1 --no-fear' +test_expect_success 'OPT_BOOL() #5' 'check boolean: 1 -B' + +test_expect_success 'OPT_BOOL() is idempotent #1' 'check boolean: 1 --yes --yes' +test_expect_success 'OPT_BOOL() is idempotent #2' 'check boolean: 1 -DB' + +test_expect_success 'OPT_BOOL() negation #1' 'check boolean: 0 -D --no-yes' +test_expect_success 'OPT_BOOL() negation #2' 'check boolean: 0 -D --no-no-doubt' + +test_expect_success 'OPT_BOOL() no negation #1' 'check_unknown_i18n --fear' +test_expect_success 'OPT_BOOL() no negation #2' 'check_unknown_i18n --no-no-fear' + +test_expect_success 'OPT_BOOL() positivation' 'check boolean: 0 -D --doubt' + cat > expect << EOF boolean: 2 integer: 1729 @@ -69,7 +149,7 @@ test_expect_success 'short options' ' test-parse-options -s123 -b -i 1729 -b -vv -n -F my.file \ > output 2> output.err && test_cmp expect output && - test ! -s output.err + test_must_be_empty output.err ' cat > expect << EOF @@ -88,7 +168,7 @@ test_expect_success 'long options' ' test-parse-options --boolean --integer 1729 --boolean --string2=321 \ --verbose --verbose --no-dry-run --abbrev=10 --file fi.le\ --obsolete > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -119,7 +199,7 @@ EOF test_expect_success 'intermingled arguments' ' test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \ > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -137,13 +217,13 @@ EOF test_expect_success 'unambiguously abbreviated option' ' test-parse-options --int 2 --boolean --no-bo > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' test_expect_success 'unambiguously abbreviated option with "="' ' test-parse-options --int=2 > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -166,7 +246,7 @@ EOF test_expect_success 'non ambiguous option (after two options it abbreviates)' ' test-parse-options --st 123 > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -176,7 +256,17 @@ EOF test_expect_success 'detect possible typos' ' test_must_fail test-parse-options -boolean > output 2> output.err && - test ! -s output && + test_must_be_empty output && + test_cmp typo.err output.err +' + +cat > typo.err << EOF +error: did you mean \`--ambiguous\` (with two dashes ?) +EOF + +test_expect_success 'detect possible typos' ' + test_must_fail test-parse-options -ambiguous > output 2> output.err && + test_must_be_empty output && test_cmp typo.err output.err ' @@ -195,7 +285,7 @@ EOF test_expect_success 'keep some options as arguments' ' test-parse-options --quux > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -215,7 +305,7 @@ EOF test_expect_success 'OPT_DATE() and OPT_SET_PTR() work' ' test-parse-options -t "1970-01-01 00:00:01 +0000" --default-string \ foo -q > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -234,7 +324,7 @@ EOF test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' ' test-parse-options --length=four -b -4 > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -244,8 +334,8 @@ EOF test_expect_success 'OPT_CALLBACK() and callback errors work' ' test_must_fail test-parse-options --no-length > output 2> output.err && - test_cmp expect output && - test_cmp expect.err output.err + test_i18ncmp expect output && + test_i18ncmp expect.err output.err ' cat > expect <<EOF @@ -262,13 +352,13 @@ EOF test_expect_success 'OPT_BIT() and OPT_SET_INT() work' ' test-parse-options --set23 -bbbbb --no-or4 > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' ' test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -286,19 +376,19 @@ EOF test_expect_success 'OPT_BIT() works' ' test-parse-options -bb --or4 > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' test_expect_success 'OPT_NEGBIT() works' ' test-parse-options -bb --no-neg-or4 > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' -test_expect_success 'OPT_BOOLEAN() with PARSE_OPT_NODASH works' ' +test_expect_success 'OPT_COUNTUP() with PARSE_OPT_NODASH works' ' test-parse-options + + + + + + > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -316,7 +406,7 @@ EOF test_expect_success 'OPT_NUMBER_CALLBACK() works' ' test-parse-options -12345 > output 2> output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' @@ -334,7 +424,7 @@ EOF test_expect_success 'negation of OPT_NONEG flags is not ambiguous' ' test-parse-options --no-ambig >output 2>output.err && - test ! -s output.err && + test_must_be_empty output.err && test_cmp expect output ' diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh index 1542cf6a13..6b3cedcf24 100755 --- a/t/t0050-filesystem.sh +++ b/t/t0050-filesystem.sh @@ -7,78 +7,51 @@ test_description='Various filesystem issues' auml=$(printf '\303\244') aumlcdiar=$(printf '\141\314\210') -case_insensitive= -unibad= -no_symlinks= -test_expect_success 'see what we expect' ' - - test_case=test_expect_success && - test_unicode=test_expect_success && - mkdir junk && - echo good >junk/CamelCase && - echo bad >junk/camelcase && - if test "$(cat junk/CamelCase)" != good - then - test_case=test_expect_failure && - case_insensitive=t - fi && - rm -fr junk && - mkdir junk && - >junk/"$auml" && - case "$(cd junk && echo *)" in - "$aumlcdiar") - test_unicode=test_expect_failure && - unibad=t - ;; - *) ;; - esac && - rm -fr junk && - { - ln -s x y 2> /dev/null && - test -h y 2> /dev/null || - no_symlinks=1 && - rm -f y - } -' - -test "$case_insensitive" && +if test_have_prereq CASE_INSENSITIVE_FS +then say "will test on a case insensitive filesystem" -test "$unibad" && + test_case=test_expect_failure +else + test_case=test_expect_success +fi + +if test_have_prereq UTF8_NFD_TO_NFC +then say "will test on a unicode corrupting filesystem" -test "$no_symlinks" && + test_unicode=test_expect_failure +else + test_unicode=test_expect_success +fi + +test_have_prereq SYMLINKS || say "will test on a filesystem lacking symbolic links" -if test "$case_insensitive" +if test_have_prereq CASE_INSENSITIVE_FS then test_expect_success "detection of case insensitive filesystem during repo init" ' - test $(git config --bool core.ignorecase) = true ' else test_expect_success "detection of case insensitive filesystem during repo init" ' - test_must_fail git config --bool core.ignorecase >/dev/null || test $(git config --bool core.ignorecase) = false ' fi -if test "$no_symlinks" +if test_have_prereq SYMLINKS then test_expect_success "detection of filesystem w/o symlink support during repo init" ' - - v=$(git config --bool core.symlinks) && - test "$v" = false + test_must_fail git config --bool core.symlinks || + test "$(git config --bool core.symlinks)" = true ' else test_expect_success "detection of filesystem w/o symlink support during repo init" ' - - test_must_fail git config --bool core.symlinks || - test "$(git config --bool core.symlinks)" = true + v=$(git config --bool core.symlinks) && + test "$v" = false ' fi test_expect_success "setup case tests" ' - git config core.ignorecase true && touch camelcase && git add camelcase && @@ -89,29 +62,23 @@ test_expect_success "setup case tests" ' git mv tmp CamelCase && git commit -m "rename" && git checkout -f master - ' $test_case 'rename (case change)' ' - git mv camelcase CamelCase && git commit -m "rename" - ' -$test_case 'merge (case change)' ' - +test_expect_success 'merge (case change)' ' rm -f CamelCase && rm -f camelcase && git reset --hard initial && git merge topic - ' -test_expect_failure 'add (with different case)' ' - +test_expect_failure CASE_INSENSITIVE_FS 'add (with different case)' ' git reset --hard initial && rm camelcase && echo 1 >CamelCase && @@ -119,37 +86,31 @@ test_expect_failure 'add (with different case)' ' camel=$(git ls-files | grep -i camelcase) && test $(echo "$camel" | wc -l) = 1 && test "z$(git cat-file blob :$camel)" = z1 - ' test_expect_success "setup unicode normalization tests" ' - - test_create_repo unicode && - cd unicode && - touch "$aumlcdiar" && - git add "$aumlcdiar" && - git commit -m initial && - git tag initial && - git checkout -b topic && - git mv $aumlcdiar tmp && - git mv tmp "$auml" && - git commit -m rename && - git checkout -f master - + test_create_repo unicode && + cd unicode && + git config core.precomposeunicode false && + touch "$aumlcdiar" && + git add "$aumlcdiar" && + git commit -m initial && + git tag initial && + git checkout -b topic && + git mv $aumlcdiar tmp && + git mv tmp "$auml" && + git commit -m rename && + git checkout -f master ' $test_unicode 'rename (silent unicode normalization)' ' - - git mv "$aumlcdiar" "$auml" && - git commit -m rename - + git mv "$aumlcdiar" "$auml" && + git commit -m rename ' $test_unicode 'merge (silent unicode normalization)' ' - - git reset --hard initial && - git merge topic - + git reset --hard initial && + git merge topic ' test_done diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh new file mode 100755 index 0000000000..99c037703a --- /dev/null +++ b/t/t0056-git-C.sh @@ -0,0 +1,84 @@ +#!/bin/sh + +test_description='"-C <path>" option and its effects on other path-related options' + +. ./test-lib.sh + +test_expect_success '"git -C <path>" runs git from the directory <path>' ' + test_create_repo dir1 && + echo 1 >dir1/a.txt && + msg="initial in dir1" && + (cd dir1 && git add a.txt && git commit -m "$msg") && + echo "$msg" >expected && + git -C dir1 log --format=%s >actual && + test_cmp expected actual +' + +test_expect_success 'Multiple -C options: "-C dir1 -C dir2" is equivalent to "-C dir1/dir2"' ' + test_create_repo dir1/dir2 && + echo 1 >dir1/dir2/b.txt && + git -C dir1/dir2 add b.txt && + msg="initial in dir1/dir2" && + echo "$msg" >expected && + git -C dir1/dir2 commit -m "$msg" && + git -C dir1 -C dir2 log --format=%s >actual && + test_cmp expected actual +' + +test_expect_success 'Effect on --git-dir option: "-C c --git-dir=a.git" is equivalent to "--git-dir c/a.git"' ' + mkdir c && + mkdir c/a && + mkdir c/a.git && + (cd c/a.git && git init --bare) && + echo 1 >c/a/a.txt && + git --git-dir c/a.git --work-tree=c/a add a.txt && + git --git-dir c/a.git --work-tree=c/a commit -m "initial" && + git --git-dir=c/a.git log -1 --format=%s >expected && + git -C c --git-dir=a.git log -1 --format=%s >actual && + test_cmp expected actual +' + +test_expect_success 'Order should not matter: "--git-dir=a.git -C c" is equivalent to "-C c --git-dir=a.git"' ' + git -C c --git-dir=a.git log -1 --format=%s >expected && + git --git-dir=a.git -C c log -1 --format=%s >actual && + test_cmp expected actual +' + +test_expect_success 'Effect on --work-tree option: "-C c/a.git --work-tree=../a" is equivalent to "--work-tree=c/a --git-dir=c/a.git"' ' + rm c/a/a.txt && + git --git-dir=c/a.git --work-tree=c/a status >expected && + git -C c/a.git --work-tree=../a status >actual && + test_cmp expected actual +' + +test_expect_success 'Order should not matter: "--work-tree=../a -C c/a.git" is equivalent to "-C c/a.git --work-tree=../a"' ' + git -C c/a.git --work-tree=../a status >expected && + git --work-tree=../a -C c/a.git status >actual && + test_cmp expected actual +' + +test_expect_success 'Effect on --git-dir and --work-tree options - "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=c/a.git --work-tree=c/a"' ' + git --git-dir=c/a.git --work-tree=c/a status >expected && + git -C c --git-dir=a.git --work-tree=a status >actual && + test_cmp expected actual +' + +test_expect_success 'Order should not matter: "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=a.git -C c --work-tree=a"' ' + git -C c --git-dir=a.git --work-tree=a status >expected && + git --git-dir=a.git -C c --work-tree=a status >actual && + test_cmp expected actual +' + +test_expect_success 'Order should not matter: "-C c --git-dir=a.git --work-tree=a" is equivalent to "--git-dir=a.git --work-tree=a -C c"' ' + git -C c --git-dir=a.git --work-tree=a status >expected && + git --git-dir=a.git --work-tree=a -C c status >actual && + test_cmp expected actual +' + +test_expect_success 'Relative followed by fullpath: "-C ./here -C /there" is equivalent to "-C /there"' ' + echo "initial in dir1/dir2" >expected && + git -C dir1 -C "$(pwd)/dir1/dir2" log --format=%s >actual && + test_cmp expected actual +' + +test_done diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 53cf1f8dc4..07c10c8dca 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -8,8 +8,15 @@ test_description='Test various path utilities' . ./test-lib.sh norm_path() { + expected=$(test-path-utils print_path "$2") test_expect_success $3 "normalize path: $1 => $2" \ - "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'" + "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$expected'" +} + +relative_path() { + expected=$(test-path-utils print_path "$3") + test_expect_success $4 "relative path: $1 $2 => $3" \ + "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'" } # On Windows, we are using MSYS's bash, which mangles the paths. @@ -34,8 +41,8 @@ ancestor() { test \"\$actual\" = '$expected'" } -# Absolute path tests must be skipped on Windows because due to path mangling -# the test program never sees a POSIX-style absolute path +# Some absolute path tests should be skipped on Windows due to path mangling +# on POSIX-style absolute paths case $(uname -s) in *MINGW*) ;; @@ -68,75 +75,152 @@ norm_path d1/s1//../s2/../../d2 d2 norm_path d1/.../d2 d1/.../d2 norm_path d1/..././../d2 d1/d2 -norm_path / / POSIX +norm_path / / norm_path // / POSIX norm_path /// / POSIX -norm_path /. / POSIX +norm_path /. / norm_path /./ / POSIX norm_path /./.. ++failed++ POSIX -norm_path /../. ++failed++ POSIX +norm_path /../. ++failed++ norm_path /./../.// ++failed++ POSIX norm_path /dir/.. / POSIX norm_path /dir/sub/../.. / POSIX norm_path /dir/sub/../../.. ++failed++ POSIX -norm_path /dir /dir POSIX -norm_path /dir// /dir/ POSIX -norm_path /./dir /dir POSIX -norm_path /dir/. /dir/ POSIX -norm_path /dir///./ /dir/ POSIX -norm_path /dir//sub/.. /dir/ POSIX -norm_path /dir/sub/../ /dir/ POSIX +norm_path /dir /dir +norm_path /dir// /dir/ +norm_path /./dir /dir +norm_path /dir/. /dir/ +norm_path /dir///./ /dir/ +norm_path /dir//sub/.. /dir/ +norm_path /dir/sub/../ /dir/ norm_path //dir/sub/../. /dir/ POSIX -norm_path /dir/s1/../s2/ /dir/s2/ POSIX -norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX -norm_path /d1/s1//../s2/../../d2 /d2 POSIX -norm_path /d1/.../d2 /d1/.../d2 POSIX -norm_path /d1/..././../d2 /d1/d2 POSIX +norm_path /dir/s1/../s2/ /dir/s2/ +norm_path /d1/s1///s2/..//../s3/ /d1/s3/ +norm_path /d1/s1//../s2/../../d2 /d2 +norm_path /d1/.../d2 /d1/.../d2 +norm_path /d1/..././../d2 /d1/d2 -ancestor / "" -1 ancestor / / -1 -ancestor /foo "" -1 -ancestor /foo : -1 -ancestor /foo ::. -1 -ancestor /foo ::..:: -1 ancestor /foo / 0 ancestor /foo /fo -1 ancestor /foo /foo -1 -ancestor /foo /foo/ -1 ancestor /foo /bar -1 -ancestor /foo /bar/ -1 ancestor /foo /foo/bar -1 -ancestor /foo /foo:/bar/ -1 -ancestor /foo /foo/:/bar/ -1 -ancestor /foo /foo::/bar/ -1 -ancestor /foo /:/foo:/bar/ 0 -ancestor /foo /foo:/:/bar/ 0 -ancestor /foo /:/bar/:/foo 0 -ancestor /foo/bar "" -1 +ancestor /foo /foo:/bar -1 +ancestor /foo /:/foo:/bar 0 +ancestor /foo /foo:/:/bar 0 +ancestor /foo /:/bar:/foo 0 ancestor /foo/bar / 0 ancestor /foo/bar /fo -1 -ancestor /foo/bar foo -1 ancestor /foo/bar /foo 4 -ancestor /foo/bar /foo/ 4 ancestor /foo/bar /foo/ba -1 ancestor /foo/bar /:/fo 0 ancestor /foo/bar /foo:/foo/ba 4 ancestor /foo/bar /bar -1 -ancestor /foo/bar /bar/ -1 -ancestor /foo/bar /fo: -1 -ancestor /foo/bar :/fo -1 -ancestor /foo/bar /foo:/bar/ 4 -ancestor /foo/bar /:/foo:/bar/ 4 -ancestor /foo/bar /foo:/:/bar/ 4 -ancestor /foo/bar /:/bar/:/fo 0 -ancestor /foo/bar /:/bar/ 0 -ancestor /foo/bar .:/foo/. 4 -ancestor /foo/bar .:/foo/.:.: 4 -ancestor /foo/bar /foo/./:.:/bar 4 -ancestor /foo/bar .:/bar -1 +ancestor /foo/bar /fo -1 +ancestor /foo/bar /foo:/bar 4 +ancestor /foo/bar /:/foo:/bar 4 +ancestor /foo/bar /foo:/:/bar 4 +ancestor /foo/bar /:/bar:/fo 0 +ancestor /foo/bar /:/bar 0 +ancestor /foo/bar /foo 4 +ancestor /foo/bar /foo:/bar 4 +ancestor /foo/bar /bar -1 test_expect_success 'strip_path_suffix' ' test c:/msysgit = $(test-path-utils strip_path_suffix \ c:/msysgit/libexec//git-core libexec/git-core) ' + +test_expect_success 'absolute path rejects the empty string' ' + test_must_fail test-path-utils absolute_path "" +' + +test_expect_success 'real path rejects the empty string' ' + test_must_fail test-path-utils real_path "" +' + +test_expect_success POSIX 'real path works on absolute paths 1' ' + nopath="hopefully-absent-path" && + test "/" = "$(test-path-utils real_path "/")" && + test "/$nopath" = "$(test-path-utils real_path "/$nopath")" +' + +test_expect_success 'real path works on absolute paths 2' ' + nopath="hopefully-absent-path" && + # Find an existing top-level directory for the remaining tests: + d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") && + test "$d" = "$(test-path-utils real_path "$d")" && + test "$d/$nopath" = "$(test-path-utils real_path "$d/$nopath")" +' + +test_expect_success POSIX 'real path removes extra leading slashes' ' + nopath="hopefully-absent-path" && + test "/" = "$(test-path-utils real_path "///")" && + test "/$nopath" = "$(test-path-utils real_path "///$nopath")" && + # Find an existing top-level directory for the remaining tests: + d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") && + test "$d" = "$(test-path-utils real_path "//$d")" && + test "$d/$nopath" = "$(test-path-utils real_path "//$d/$nopath")" +' + +test_expect_success 'real path removes other extra slashes' ' + nopath="hopefully-absent-path" && + # Find an existing top-level directory for the remaining tests: + d=$(pwd -P | sed -e "s|^\([^/]*/[^/]*\)/.*|\1|") && + test "$d" = "$(test-path-utils real_path "$d///")" && + test "$d/$nopath" = "$(test-path-utils real_path "$d///$nopath")" +' + +test_expect_success SYMLINKS 'real path works on symlinks' ' + mkdir first && + ln -s ../.git first/.git && + mkdir second && + ln -s ../first second/other && + mkdir third && + dir="$(cd .git; pwd -P)" && + dir2=third/../second/other/.git && + test "$dir" = "$(test-path-utils real_path $dir2)" && + file="$dir"/index && + test "$file" = "$(test-path-utils real_path $dir2/index)" && + basename=blub && + test "$dir/$basename" = "$(cd .git && test-path-utils real_path "$basename")" && + ln -s ../first/file .git/syml && + sym="$(cd first; pwd -P)"/file && + test "$sym" = "$(test-path-utils real_path "$dir2/syml")" +' + +relative_path /foo/a/b/c/ /foo/a/b/ c/ +relative_path /foo/a/b/c/ /foo/a/b c/ +relative_path /foo/a//b//c/ ///foo/a/b// c/ POSIX +relative_path /foo/a/b /foo/a/b ./ +relative_path /foo/a/b/ /foo/a/b ./ +relative_path /foo/a /foo/a/b ../ +relative_path / /foo/a/b/ ../../../ +relative_path /foo/a/c /foo/a/b/ ../c +relative_path /foo/a/c /foo/a/b ../c +relative_path /foo/x/y /foo/a/b/ ../../x/y +relative_path /foo/a/b "<empty>" /foo/a/b +relative_path /foo/a/b "<null>" /foo/a/b +relative_path foo/a/b/c/ foo/a/b/ c/ +relative_path foo/a/b/c/ foo/a/b c/ +relative_path foo/a/b//c foo/a//b c +relative_path foo/a/b/ foo/a/b/ ./ +relative_path foo/a/b/ foo/a/b ./ +relative_path foo/a foo/a/b ../ +relative_path foo/x/y foo/a/b ../../x/y +relative_path foo/a/c foo/a/b ../c +relative_path foo/a/b /foo/x/y foo/a/b +relative_path /foo/a/b foo/x/y /foo/a/b +relative_path d:/a/b D:/a/c ../b MINGW +relative_path C:/a/b D:/a/c C:/a/b MINGW +relative_path foo/a/b "<empty>" foo/a/b +relative_path foo/a/b "<null>" foo/a/b +relative_path "<empty>" /foo/a/b ./ +relative_path "<empty>" "<empty>" ./ +relative_path "<empty>" "<null>" ./ +relative_path "<null>" "<empty>" ./ +relative_path "<null>" "<null>" ./ +relative_path "<null>" /foo/a/b ./ + test_done diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh index 8d4938f019..17e969df60 100755 --- a/t/t0061-run-command.sh +++ b/t/t0061-run-command.sh @@ -34,4 +34,17 @@ test_expect_success POSIXPERM 'run_command reports EACCES' ' grep "fatal: cannot exec.*hello.sh" err ' +test_expect_success POSIXPERM 'unreadable directory in PATH' ' + mkdir local-command && + test_when_finished "chmod u+rwx local-command && rm -fr local-command" && + git config alias.nitfol "!echo frotz" && + chmod a-rx local-command && + ( + PATH=./local-command:$PATH && + git nitfol >actual + ) && + echo frotz >expect && + test_cmp expect actual +' + test_done diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh new file mode 100755 index 0000000000..3d98eb847f --- /dev/null +++ b/t/t0062-revision-walking.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# +# Copyright (c) 2012 Heiko Voigt +# + +test_description='Test revision walking api' + +. ./test-lib.sh + +cat >run_twice_expected <<-EOF +1st + > add b + > add a +2nd + > add b + > add a +EOF + +test_expect_success 'setup' ' + echo a > a && + git add a && + git commit -m "add a" && + echo b > b && + git add b && + git commit -m "add b" +' + +test_expect_success 'revision walking can be done twice' ' + test-revision-walking run-twice > run_twice_actual + test_cmp run_twice_expected run_twice_actual +' + +test_done diff --git a/t/t0063-string-list.sh b/t/t0063-string-list.sh new file mode 100755 index 0000000000..dbfc05ebdc --- /dev/null +++ b/t/t0063-string-list.sh @@ -0,0 +1,91 @@ +#!/bin/sh +# +# Copyright (c) 2012 Michael Haggerty +# + +test_description='Test string list functionality' + +. ./test-lib.sh + +test_split () { + cat >expected && + test_expect_success "split $1 at $2, max $3" " + test-string-list split '$1' '$2' '$3' >actual && + test_cmp expected actual && + test-string-list split_in_place '$1' '$2' '$3' >actual && + test_cmp expected actual + " +} + +test_split "foo:bar:baz" ":" "-1" <<EOF +3 +[0]: "foo" +[1]: "bar" +[2]: "baz" +EOF + +test_split "foo:bar:baz" ":" "0" <<EOF +1 +[0]: "foo:bar:baz" +EOF + +test_split "foo:bar:baz" ":" "1" <<EOF +2 +[0]: "foo" +[1]: "bar:baz" +EOF + +test_split "foo:bar:baz" ":" "2" <<EOF +3 +[0]: "foo" +[1]: "bar" +[2]: "baz" +EOF + +test_split "foo:bar:" ":" "-1" <<EOF +3 +[0]: "foo" +[1]: "bar" +[2]: "" +EOF + +test_split "" ":" "-1" <<EOF +1 +[0]: "" +EOF + +test_split ":" ":" "-1" <<EOF +2 +[0]: "" +[1]: "" +EOF + +test_expect_success "test filter_string_list" ' + test "x-" = "x$(test-string-list filter - y)" && + test "x-" = "x$(test-string-list filter no y)" && + test yes = "$(test-string-list filter yes y)" && + test yes = "$(test-string-list filter no:yes y)" && + test yes = "$(test-string-list filter yes:no y)" && + test y1:y2 = "$(test-string-list filter y1:y2 y)" && + test y2:y1 = "$(test-string-list filter y2:y1 y)" && + test "x-" = "x$(test-string-list filter x1:x2 y)" +' + +test_expect_success "test remove_duplicates" ' + test "x-" = "x$(test-string-list remove_duplicates -)" && + test "x" = "x$(test-string-list remove_duplicates "")" && + test a = "$(test-string-list remove_duplicates a)" && + test a = "$(test-string-list remove_duplicates a:a)" && + test a = "$(test-string-list remove_duplicates a:a:a:a:a)" && + test a:b = "$(test-string-list remove_duplicates a:b)" && + test a:b = "$(test-string-list remove_duplicates a:a:b)" && + test a:b = "$(test-string-list remove_duplicates a:b:b)" && + test a:b:c = "$(test-string-list remove_duplicates a:b:c)" && + test a:b:c = "$(test-string-list remove_duplicates a:a:b:c)" && + test a:b:c = "$(test-string-list remove_duplicates a:b:b:c)" && + test a:b:c = "$(test-string-list remove_duplicates a:b:c:c)" && + test a:b:c = "$(test-string-list remove_duplicates a:a:b:b:c:c)" && + test a:b:c = "$(test-string-list remove_duplicates a:a:a:b:b:b:c:c:c)" +' + +test_done diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh index 9bee8bfd2e..5ed69a6f56 100755 --- a/t/t0070-fundamental.sh +++ b/t/t0070-fundamental.sh @@ -17,7 +17,7 @@ test_expect_success 'mktemp to nonexistent directory prints filename' ' grep "doesnotexist/test" err ' -test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' ' +test_expect_success POSIXPERM,SANITY 'mktemp to unwritable directory prints filename' ' mkdir cannotwrite && chmod -w cannotwrite && test_when_finished "chmod +w cannotwrite" && @@ -25,4 +25,13 @@ test_expect_success POSIXPERM 'mktemp to unwritable directory prints filename' ' grep "cannotwrite/test" err ' +test_expect_success 'git_mkstemps_mode does not fail if fd 0 is not open' ' + git commit --allow-empty -m message <&- +' + +test_expect_success 'check for a bug in the regex routines' ' + # if this test fails, re-build git with NO_REGEX=1 + test-regex +' + test_done diff --git a/t/t0080-vcs-svn.sh b/t/t0080-vcs-svn.sh deleted file mode 100755 index 99a314b080..0000000000 --- a/t/t0080-vcs-svn.sh +++ /dev/null @@ -1,117 +0,0 @@ -#!/bin/sh - -test_description='check infrastructure for svn importer' - -. ./test-lib.sh -uint32_max=4294967295 - -test_expect_success 'obj pool: store data' ' - cat <<-\EOF >expected && - 0 - 1 - EOF - - test-obj-pool <<-\EOF >actual && - alloc one 16 - set one 13 - test one 13 - reset one - EOF - test_cmp expected actual -' - -test_expect_success 'obj pool: NULL is offset ~0' ' - echo "$uint32_max" >expected && - echo null one | test-obj-pool >actual && - test_cmp expected actual -' - -test_expect_success 'obj pool: out-of-bounds access' ' - cat <<-EOF >expected && - 0 - 0 - $uint32_max - $uint32_max - 16 - 20 - $uint32_max - EOF - - test-obj-pool <<-\EOF >actual && - alloc one 16 - alloc two 16 - offset one 20 - offset two 20 - alloc one 5 - offset one 20 - free one 1 - offset one 20 - reset one - reset two - EOF - test_cmp expected actual -' - -test_expect_success 'obj pool: high-water mark' ' - cat <<-\EOF >expected && - 0 - 0 - 10 - 20 - 20 - 20 - EOF - - test-obj-pool <<-\EOF >actual && - alloc one 10 - committed one - alloc one 10 - commit one - committed one - alloc one 10 - free one 20 - committed one - reset one - EOF - test_cmp expected actual -' - -test_expect_success 'string pool' ' - echo a does not equal b >expected.differ && - echo a equals a >expected.match && - echo equals equals equals >expected.matchmore && - - test-string-pool "a,--b" >actual.differ && - test-string-pool "a,a" >actual.match && - test-string-pool "equals-equals" >actual.matchmore && - test_must_fail test-string-pool a,a,a && - test_must_fail test-string-pool a && - - test_cmp expected.differ actual.differ && - test_cmp expected.match actual.match && - test_cmp expected.matchmore actual.matchmore -' - -test_expect_success 'treap sort' ' - cat <<-\EOF >unsorted && - 68 - 12 - 13 - 13 - 68 - 13 - 13 - 21 - 10 - 11 - 12 - 13 - 13 - EOF - sort unsorted >expected && - - test-treap <unsorted >actual && - test_cmp expected actual -' - -test_done diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh index 315b9b3f10..e0a6940232 100755 --- a/t/t0100-previous.sh +++ b/t/t0100-previous.sh @@ -27,6 +27,7 @@ test_expect_success 'merge @{-1}' ' test_commit B && git checkout A && test_commit C && + test_commit D && git branch -f master B && git branch -f other && git checkout other && @@ -35,14 +36,24 @@ test_expect_success 'merge @{-1}' ' git cat-file commit HEAD | grep "Merge branch '\''other'\''" ' -test_expect_success 'merge @{-1} when there is not enough switches yet' ' +test_expect_success 'merge @{-1}~1' ' + git checkout master && + git reset --hard B && + git checkout other && + git checkout master && + git merge @{-1}~1 && + git cat-file commit HEAD >actual && + grep "Merge branch '\''other'\''" actual +' + +test_expect_success 'merge @{-100} before checking out that many branches yet' ' git reflog expire --expire=now && git checkout -f master && git reset --hard B && git branch -f other C && git checkout other && git checkout master && - test_must_fail git merge @{-12} + test_must_fail git merge @{-100} ' test_done diff --git a/t/t0110-urlmatch-normalization.sh b/t/t0110-urlmatch-normalization.sh new file mode 100755 index 0000000000..8d6096d4d1 --- /dev/null +++ b/t/t0110-urlmatch-normalization.sh @@ -0,0 +1,177 @@ +#!/bin/sh + +test_description='urlmatch URL normalization' +. ./test-lib.sh + +# The base name of the test url files +tu="$TEST_DIRECTORY/t0110/url" + +# Note that only file: URLs should be allowed without a host + +test_expect_success 'url scheme' ' + ! test-urlmatch-normalization "" && + ! test-urlmatch-normalization "_" && + ! test-urlmatch-normalization "scheme" && + ! test-urlmatch-normalization "scheme:" && + ! test-urlmatch-normalization "scheme:/" && + ! test-urlmatch-normalization "scheme://" && + ! test-urlmatch-normalization "file" && + ! test-urlmatch-normalization "file:" && + ! test-urlmatch-normalization "file:/" && + test-urlmatch-normalization "file://" && + ! test-urlmatch-normalization "://acme.co" && + ! test-urlmatch-normalization "x_test://acme.co" && + ! test-urlmatch-normalization "-test://acme.co" && + ! test-urlmatch-normalization "0test://acme.co" && + ! test-urlmatch-normalization "+test://acme.co" && + ! test-urlmatch-normalization ".test://acme.co" && + ! test-urlmatch-normalization "schem%6e://" && + test-urlmatch-normalization "x-Test+v1.0://acme.co" && + test "$(test-urlmatch-normalization -p "AbCdeF://x.Y")" = "abcdef://x.y/" +' + +test_expect_success 'url authority' ' + ! test-urlmatch-normalization "scheme://user:pass@" && + ! test-urlmatch-normalization "scheme://?" && + ! test-urlmatch-normalization "scheme://#" && + ! test-urlmatch-normalization "scheme:///" && + ! test-urlmatch-normalization "scheme://:" && + ! test-urlmatch-normalization "scheme://:555" && + test-urlmatch-normalization "file://user:pass@" && + test-urlmatch-normalization "file://?" && + test-urlmatch-normalization "file://#" && + test-urlmatch-normalization "file:///" && + test-urlmatch-normalization "file://:" && + ! test-urlmatch-normalization "file://:555" && + test-urlmatch-normalization "scheme://user:pass@host" && + test-urlmatch-normalization "scheme://@host" && + test-urlmatch-normalization "scheme://%00@host" && + ! test-urlmatch-normalization "scheme://%%@host" && + ! test-urlmatch-normalization "scheme://host_" && + test-urlmatch-normalization "scheme://user:pass@host/" && + test-urlmatch-normalization "scheme://@host/" && + test-urlmatch-normalization "scheme://host/" && + test-urlmatch-normalization "scheme://host?x" && + test-urlmatch-normalization "scheme://host#x" && + test-urlmatch-normalization "scheme://host/@" && + test-urlmatch-normalization "scheme://host?@x" && + test-urlmatch-normalization "scheme://host#@x" && + test-urlmatch-normalization "scheme://[::1]" && + test-urlmatch-normalization "scheme://[::1]/" && + ! test-urlmatch-normalization "scheme://hos%41/" && + test-urlmatch-normalization "scheme://[invalid....:/" && + test-urlmatch-normalization "scheme://invalid....:]/" && + ! test-urlmatch-normalization "scheme://invalid....:[/" && + ! test-urlmatch-normalization "scheme://invalid....:[" +' + +test_expect_success 'url port checks' ' + test-urlmatch-normalization "xyz://q@some.host:" && + test-urlmatch-normalization "xyz://q@some.host:456/" && + ! test-urlmatch-normalization "xyz://q@some.host:0" && + ! test-urlmatch-normalization "xyz://q@some.host:0000000" && + test-urlmatch-normalization "xyz://q@some.host:0000001?" && + test-urlmatch-normalization "xyz://q@some.host:065535#" && + test-urlmatch-normalization "xyz://q@some.host:65535" && + ! test-urlmatch-normalization "xyz://q@some.host:65536" && + ! test-urlmatch-normalization "xyz://q@some.host:99999" && + ! test-urlmatch-normalization "xyz://q@some.host:100000" && + ! test-urlmatch-normalization "xyz://q@some.host:100001" && + test-urlmatch-normalization "http://q@some.host:80" && + test-urlmatch-normalization "https://q@some.host:443" && + test-urlmatch-normalization "http://q@some.host:80/" && + test-urlmatch-normalization "https://q@some.host:443?" && + ! test-urlmatch-normalization "http://q@:8008" && + ! test-urlmatch-normalization "http://:8080" && + ! test-urlmatch-normalization "http://:" && + test-urlmatch-normalization "xyz://q@some.host:456/" && + test-urlmatch-normalization "xyz://[::1]:456/" && + test-urlmatch-normalization "xyz://[::1]:/" && + ! test-urlmatch-normalization "xyz://[::1]:000/" && + ! test-urlmatch-normalization "xyz://[::1]:0%300/" && + ! test-urlmatch-normalization "xyz://[::1]:0x80/" && + ! test-urlmatch-normalization "xyz://[::1]:4294967297/" && + ! test-urlmatch-normalization "xyz://[::1]:030f/" +' + +test_expect_success 'url port normalization' ' + test "$(test-urlmatch-normalization -p "http://x:800")" = "http://x:800/" && + test "$(test-urlmatch-normalization -p "http://x:0800")" = "http://x:800/" && + test "$(test-urlmatch-normalization -p "http://x:00000800")" = "http://x:800/" && + test "$(test-urlmatch-normalization -p "http://x:065535")" = "http://x:65535/" && + test "$(test-urlmatch-normalization -p "http://x:1")" = "http://x:1/" && + test "$(test-urlmatch-normalization -p "http://x:80")" = "http://x/" && + test "$(test-urlmatch-normalization -p "http://x:080")" = "http://x/" && + test "$(test-urlmatch-normalization -p "http://x:000000080")" = "http://x/" && + test "$(test-urlmatch-normalization -p "https://x:443")" = "https://x/" && + test "$(test-urlmatch-normalization -p "https://x:0443")" = "https://x/" && + test "$(test-urlmatch-normalization -p "https://x:000000443")" = "https://x/" +' + +test_expect_success 'url general escapes' ' + ! test-urlmatch-normalization "http://x.y?%fg" && + test "$(test-urlmatch-normalization -p "X://W/%7e%41^%3a")" = "x://w/~A%5E%3A" && + test "$(test-urlmatch-normalization -p "X://W/:/?#[]@")" = "x://w/:/?#[]@" && + test "$(test-urlmatch-normalization -p "X://W/$&()*+,;=")" = "x://w/$&()*+,;=" && + test "$(test-urlmatch-normalization -p "X://W/'\''")" = "x://w/'\''" && + test "$(test-urlmatch-normalization -p "X://W?'\!'")" = "x://w/?'\!'" +' + +test_expect_success 'url high-bit escapes' ' + test "$(test-urlmatch-normalization -p "$(cat "$tu-1")")" = "x://q/%01%02%03%04%05%06%07%08%0E%0F%10%11%12" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-2")")" = "x://q/%13%14%15%16%17%18%19%1B%1C%1D%1E%1F%7F" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-3")")" = "x://q/%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-4")")" = "x://q/%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-5")")" = "x://q/%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-6")")" = "x://q/%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-7")")" = "x://q/%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-8")")" = "x://q/%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-9")")" = "x://q/%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-10")")" = "x://q/%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF" && + test "$(test-urlmatch-normalization -p "$(cat "$tu-11")")" = "x://q/%C2%80%DF%BF%E0%A0%80%EF%BF%BD%F0%90%80%80%F0%AF%BF%BD" +' + +test_expect_success 'url username/password escapes' ' + test "$(test-urlmatch-normalization -p "x://%41%62(^):%70+d@foo")" = "x://Ab(%5E):p+d@foo/" +' + +test_expect_success 'url normalized lengths' ' + test "$(test-urlmatch-normalization -l "Http://%4d%65:%4d^%70@The.Host")" = 25 && + test "$(test-urlmatch-normalization -l "http://%41:%42@x.y/%61/")" = 17 && + test "$(test-urlmatch-normalization -l "http://@x.y/^")" = 15 +' + +test_expect_success 'url . and .. segments' ' + test "$(test-urlmatch-normalization -p "x://y/.")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/./")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/a/.")" = "x://y/a" && + test "$(test-urlmatch-normalization -p "x://y/a/./")" = "x://y/a/" && + test "$(test-urlmatch-normalization -p "x://y/.?")" = "x://y/?" && + test "$(test-urlmatch-normalization -p "x://y/./?")" = "x://y/?" && + test "$(test-urlmatch-normalization -p "x://y/a/.?")" = "x://y/a?" && + test "$(test-urlmatch-normalization -p "x://y/a/./?")" = "x://y/a/?" && + test "$(test-urlmatch-normalization -p "x://y/a/./b/.././../c")" = "x://y/c" && + test "$(test-urlmatch-normalization -p "x://y/a/./b/../.././c/")" = "x://y/c/" && + test "$(test-urlmatch-normalization -p "x://y/a/./b/.././../c/././.././.")" = "x://y/" && + ! test-urlmatch-normalization "x://y/a/./b/.././../c/././.././.." && + test "$(test-urlmatch-normalization -p "x://y/a/./?/././..")" = "x://y/a/?/././.." && + test "$(test-urlmatch-normalization -p "x://y/%2e/")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/%2E/")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/a/%2e./")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/b/.%2E/")" = "x://y/" && + test "$(test-urlmatch-normalization -p "x://y/c/%2e%2E/")" = "x://y/" +' + +# http://@foo specifies an empty user name but does not specify a password +# http://foo specifies neither a user name nor a password +# So they should not be equivalent +test_expect_success 'url equivalents' ' + test-urlmatch-normalization "httP://x" "Http://X/" && + test-urlmatch-normalization "Http://%4d%65:%4d^%70@The.Host" "hTTP://Me:%4D^p@the.HOST:80/" && + ! test-urlmatch-normalization "https://@x.y/^" "httpS://x.y:443/^" && + test-urlmatch-normalization "https://@x.y/^" "httpS://@x.y:0443/^" && + test-urlmatch-normalization "https://@x.y/^/../abc" "httpS://@x.y:0443/abc" && + test-urlmatch-normalization "https://@x.y/^/.." "httpS://@x.y:0443/" +' + +test_done diff --git a/t/t0110/README b/t/t0110/README new file mode 100644 index 0000000000..ad4a50ecd8 --- /dev/null +++ b/t/t0110/README @@ -0,0 +1,9 @@ +The url data files in this directory contain URLs with characters +in the range 0x01-0x1f and 0x7f-0xff to test the proper normalization +of unprintable characters. + +A select few characters in the 0x01-0x1f range are skipped to help +avoid problems running the test itself. + +The urls are in test files in this directory rather than being +embedded in the test script for portability. diff --git a/t/t0110/url-1 b/t/t0110/url-1 new file mode 100644 index 0000000000..519019c5ce --- /dev/null +++ b/t/t0110/url-1 @@ -0,0 +1 @@ +x://q/ diff --git a/t/t0110/url-10 b/t/t0110/url-10 new file mode 100644 index 0000000000..b9965de6a5 --- /dev/null +++ b/t/t0110/url-10 @@ -0,0 +1 @@ +x://q/ðñòóôõö÷øùúûüýþÿ diff --git a/t/t0110/url-11 b/t/t0110/url-11 new file mode 100644 index 0000000000..f0a50f1009 --- /dev/null +++ b/t/t0110/url-11 @@ -0,0 +1 @@ +x://q/Â€ß¿à €ï¿½ð€€ð¯¿½ diff --git a/t/t0110/url-2 b/t/t0110/url-2 new file mode 100644 index 0000000000..43334b05b2 --- /dev/null +++ b/t/t0110/url-2 @@ -0,0 +1 @@ +x://q/ diff --git a/t/t0110/url-3 b/t/t0110/url-3 new file mode 100644 index 0000000000..7378c7bec2 --- /dev/null +++ b/t/t0110/url-3 @@ -0,0 +1 @@ +x://q/€‚ƒ„…†‡ˆ‰Š‹ŒŽ diff --git a/t/t0110/url-4 b/t/t0110/url-4 new file mode 100644 index 0000000000..220b198c97 --- /dev/null +++ b/t/t0110/url-4 @@ -0,0 +1 @@ +x://q/‘’“”•–—˜™š›œžŸ diff --git a/t/t0110/url-5 b/t/t0110/url-5 new file mode 100644 index 0000000000..1ccd927779 --- /dev/null +++ b/t/t0110/url-5 @@ -0,0 +1 @@ +x://q/ ¡¢£¤¥¦§¨©ª«¬®¯ diff --git a/t/t0110/url-6 b/t/t0110/url-6 new file mode 100644 index 0000000000..e8283aac6d --- /dev/null +++ b/t/t0110/url-6 @@ -0,0 +1 @@ +x://q/°±²³´µ¶·¸¹º»¼½¾¿ diff --git a/t/t0110/url-7 b/t/t0110/url-7 new file mode 100644 index 0000000000..fa7c10b615 --- /dev/null +++ b/t/t0110/url-7 @@ -0,0 +1 @@ +x://q/ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏ diff --git a/t/t0110/url-8 b/t/t0110/url-8 new file mode 100644 index 0000000000..79a0ba836f --- /dev/null +++ b/t/t0110/url-8 @@ -0,0 +1 @@ +x://q/ÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß diff --git a/t/t0110/url-9 b/t/t0110/url-9 new file mode 100644 index 0000000000..8b44bec48b --- /dev/null +++ b/t/t0110/url-9 @@ -0,0 +1 @@ +x://q/àáâãäåæçèéêëìíîï diff --git a/t/t0201-gettext-fallbacks.sh b/t/t0201-gettext-fallbacks.sh index 52b1c27c2c..5d80a985fb 100755 --- a/t/t0201-gettext-fallbacks.sh +++ b/t/t0201-gettext-fallbacks.sh @@ -51,16 +51,16 @@ test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate v test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces' ' cmdline="git am" && export cmdline; - printf "When you have resolved this problem run git am --resolved." >expect && - eval_gettext "When you have resolved this problem run \$cmdline --resolved." >actual + printf "When you have resolved this problem, run git am --resolved." >expect && + eval_gettext "When you have resolved this problem, run \$cmdline --resolved." >actual test_i18ncmp expect actual ' test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes' ' cmdline="git am" && export cmdline; - printf "When you have resolved this problem run \"git am --resolved\"." >expect && - eval_gettext "When you have resolved this problem run \"\$cmdline --resolved\"." >actual + printf "When you have resolved this problem, run \"git am --resolved\"." >expect && + eval_gettext "When you have resolved this problem, run \"\$cmdline --resolved\"." >actual test_i18ncmp expect actual ' diff --git a/t/t0202-gettext-perl.sh b/t/t0202-gettext-perl.sh index 428ebb0080..a29d166e00 100755 --- a/t/t0202-gettext-perl.sh +++ b/t/t0202-gettext-perl.sh @@ -12,7 +12,7 @@ if ! test_have_prereq PERL; then test_done fi -"$PERL_PATH" -MTest::More -e 0 2>/dev/null || { +perl -MTest::More -e 0 2>/dev/null || { skip_all="Perl Test::More unavailable, skipping test" test_done } @@ -22,6 +22,6 @@ test_external_has_tap=1 test_external_without_stderr \ 'Perl Git::I18N API' \ - "$PERL_PATH" "$TEST_DIRECTORY"/t0202/test.pl + perl "$TEST_DIRECTORY"/t0202/test.pl test_done diff --git a/t/t0204-gettext-reencode-sanity.sh b/t/t0204-gettext-reencode-sanity.sh index 189af90c02..8437e51eb5 100755 --- a/t/t0204-gettext-reencode-sanity.sh +++ b/t/t0204-gettext-reencode-sanity.sh @@ -7,6 +7,10 @@ test_description="Gettext reencoding of our *.po/*.mo files works" . ./lib-gettext.sh +# The constants used in a tricky observation for undefined behaviour +RUNES="TILRAUN: ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛠᚻᛖ ᛒᚢᛞᛖ áš©áš¾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ áš¹á›áš¦ ᚦᚪ ᚹᛖᛥᚫ" +PUNTS="TILRAUN: ?? ???? ??? ?? ???? ?? ??? ????? ??????????? ??? ?? ????" +MSGKEY="TEST: Old English Runes" test_expect_success GETTEXT_LOCALE 'gettext: Emitting UTF-8 from our UTF-8 *.mo files / Icelandic' ' printf "TILRAUN: Halló Heimur!" >expect && @@ -15,8 +19,8 @@ test_expect_success GETTEXT_LOCALE 'gettext: Emitting UTF-8 from our UTF-8 *.mo ' test_expect_success GETTEXT_LOCALE 'gettext: Emitting UTF-8 from our UTF-8 *.mo files / Runes' ' - printf "TILRAUN: ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛠᚻᛖ ᛒᚢᛞᛖ áš©áš¾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ áš¹á›áš¦ ᚦᚪ ᚹᛖᛥᚫ" >expect && - LANGUAGE=is LC_ALL="$is_IS_locale" gettext "TEST: Old English Runes" >actual && + printf "%s" "$RUNES" >expect && + LANGUAGE=is LC_ALL="$is_IS_locale" gettext "$MSGKEY" >actual && test_cmp expect actual ' @@ -26,18 +30,23 @@ test_expect_success GETTEXT_ISO_LOCALE 'gettext: Emitting ISO-8859-1 from our UT test_cmp expect actual ' -test_expect_success GETTEXT_ISO_LOCALE 'gettext: Emitting ISO-8859-1 from our UTF-8 *.mo files / Runes' ' - LANGUAGE=is LC_ALL="$is_IS_iso_locale" gettext "TEST: Old English Runes" >runes && - - if grep "^TEST: Old English Runes$" runes - then - say "Your system can not handle this complexity and returns the string as-is" - else - # Both Solaris and GNU libintl will return this stream of - # question marks, so it is s probably portable enough - printf "TILRAUN: ?? ???? ??? ?? ???? ?? ??? ????? ??????????? ??? ?? ????" >runes-expect && - test_cmp runes-expect runes - fi +test_expect_success GETTEXT_ISO_LOCALE 'gettext: impossible ISO-8859-1 output' ' + LANGUAGE=is LC_ALL="$is_IS_iso_locale" gettext "$MSGKEY" >runes && + case "$(cat runes)" in + "$MSGKEY") + say "Your system gives back the key to message catalog" + ;; + "$PUNTS") + say "Your system replaces an impossible character with ?" + ;; + "$RUNES") + say "Your system gives back the raw message for an impossible request" + ;; + *) + say "We never saw the error behaviour your system exhibits" + false + ;; + esac ' test_expect_success GETTEXT_LOCALE 'gettext: Fetching a UTF-8 msgid -> UTF-8' ' diff --git a/t/t0300-credentials.sh b/t/t0300-credentials.sh index c1c8108148..538ea5fb1c 100755 --- a/t/t0300-credentials.sh +++ b/t/t0300-credentials.sh @@ -17,22 +17,18 @@ test_expect_success 'setup helper scripts' ' IFS=$OIFS EOF - cat >git-credential-useless <<-\EOF && - #!/bin/sh + write_script git-credential-useless <<-\EOF && . ./dump exit 0 EOF - chmod +x git-credential-useless && - cat >git-credential-verbatim <<-\EOF && - #!/bin/sh + write_script git-credential-verbatim <<-\EOF && user=$1; shift pass=$1; shift . ./dump test -z "$user" || echo username=$user test -z "$pass" || echo password=$pass EOF - chmod +x git-credential-verbatim && PATH="$PWD:$PATH" ' @@ -86,6 +82,9 @@ test_expect_success 'credential_fill passes along metadata' ' host=example.com path=foo.git -- + protocol=ftp + host=example.com + path=foo.git username=one password=two -- @@ -217,6 +216,8 @@ test_expect_success 'match configured credential' ' host=example.com path=repo.git -- + protocol=https + host=example.com username=foo password=bar -- @@ -229,6 +230,8 @@ test_expect_success 'do not match configured credential' ' protocol=https host=bar -- + protocol=https + host=bar username=askpass-username password=askpass-password -- @@ -243,6 +246,8 @@ test_expect_success 'pull username from config' ' protocol=https host=example.com -- + protocol=https + host=example.com username=foo password=askpass-password -- @@ -256,6 +261,8 @@ test_expect_success 'http paths can be part of context' ' host=example.com path=foo.git -- + protocol=https + host=example.com username=foo password=bar -- @@ -269,6 +276,9 @@ test_expect_success 'http paths can be part of context' ' host=example.com path=foo.git -- + protocol=https + host=example.com + path=foo.git username=foo password=bar -- diff --git a/t/t0303-credential-external.sh b/t/t0303-credential-external.sh index 267f4c8ba3..f028fd1418 100755 --- a/t/t0303-credential-external.sh +++ b/t/t0303-credential-external.sh @@ -1,39 +1,60 @@ #!/bin/sh -test_description='external credential helper tests' -. ./test-lib.sh -. "$TEST_DIRECTORY"/lib-credential.sh +test_description='external credential helper tests -pre_test() { - test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" || - eval "$GIT_TEST_CREDENTIAL_HELPER_SETUP" +This is a tool for authors of external helper tools to sanity-check +their helpers. If you have written the "git-credential-foo" helper, +you check it with: + + make GIT_TEST_CREDENTIAL_HELPER=foo t0303-credential-external.sh + +This assumes that your helper is capable of both storing and +retrieving credentials (some helpers may be read-only, and they will +fail these tests). + +Please note that the individual tests do not verify all of the +preconditions themselves, but rather build on each other. A failing +test means that tests later in the sequence can return false "OK" +results. + +If your helper supports time-based expiration with a configurable +timeout, you can test that feature with: + + make GIT_TEST_CREDENTIAL_HELPER=foo \ + GIT_TEST_CREDENTIAL_HELPER_TIMEOUT="foo --timeout=1" \ + t0303-credential-external.sh - # clean before the test in case there is cruft left - # over from a previous run that would impact results - helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER" -} +If your helper requires additional setup before the tests are started, +you can set GIT_TEST_CREDENTIAL_HELPER_SETUP to a sequence of shell +commands. +' -post_test() { - # clean afterwards so that we are good citizens - # and don't leave cruft in the helper's storage, which - # might be long-term system storage - helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER" -} +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-credential.sh if test -z "$GIT_TEST_CREDENTIAL_HELPER"; then - say "# skipping external helper tests (set GIT_TEST_CREDENTIAL_HELPER)" -else - pre_test - helper_test "$GIT_TEST_CREDENTIAL_HELPER" - post_test + skip_all="used to test external credential helpers" + test_done fi +test -z "$GIT_TEST_CREDENTIAL_HELPER_SETUP" || + eval "$GIT_TEST_CREDENTIAL_HELPER_SETUP" + +# clean before the test in case there is cruft left +# over from a previous run that would impact results +helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER" + +helper_test "$GIT_TEST_CREDENTIAL_HELPER" + if test -z "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT"; then - say "# skipping external helper timeout tests" + say "# skipping timeout tests (GIT_TEST_CREDENTIAL_HELPER_TIMEOUT not set)" else - pre_test helper_test_timeout "$GIT_TEST_CREDENTIAL_HELPER_TIMEOUT" - post_test fi +# clean afterwards so that we are good citizens +# and don't leave cruft in the helper's storage, which +# might be long-term system storage +helper_test_clean "$GIT_TEST_CREDENTIAL_HELPER" + test_done diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh index b3ae7d52c6..3e72aff470 100755 --- a/t/t1004-read-tree-m-u-wf.sh +++ b/t/t1004-read-tree-m-u-wf.sh @@ -158,7 +158,7 @@ test_expect_success '3-way not overwriting local changes (their side)' ' ' -test_expect_success SYMLINKS 'funny symlink in work tree' ' +test_expect_success 'funny symlink in work tree' ' git reset --hard && git checkout -b sym-b side-b && @@ -170,15 +170,14 @@ test_expect_success SYMLINKS 'funny symlink in work tree' ' rm -fr a && git checkout -b sym-a side-a && mkdir -p a && - ln -s ../b a/b && - git add a/b && + test_ln_s_add ../b a/b && git commit -m "we add a/b" && read_tree_u_must_succeed -m -u sym-a sym-a sym-b ' -test_expect_success SYMLINKS,SANITY 'funny symlink in work tree, un-unlink-able' ' +test_expect_success SANITY 'funny symlink in work tree, un-unlink-able' ' rm -fr a b && git reset --hard && diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index d8b7f2ffbc..8a1bc5c532 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -36,66 +36,54 @@ $content" ' test_expect_success "Type of $type is correct" ' - test $type = "$(git cat-file -t $sha1)" + echo $type >expect && + git cat-file -t $sha1 >actual && + test_cmp expect actual ' test_expect_success "Size of $type is correct" ' - test $size = "$(git cat-file -s $sha1)" + echo $size >expect && + git cat-file -s $sha1 >actual && + test_cmp expect actual ' test -z "$content" || test_expect_success "Content of $type is correct" ' - expect="$(maybe_remove_timestamp "$content" $no_ts)" - actual="$(maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts)" - - if test "z$expect" = "z$actual" - then - : happy - else - echo "Oops: expected $expect" - echo "but got $actual" - false - fi + maybe_remove_timestamp "$content" $no_ts >expect && + maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts >actual && + test_cmp expect actual ' test_expect_success "Pretty content of $type is correct" ' - expect="$(maybe_remove_timestamp "$pretty_content" $no_ts)" - actual="$(maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts)" - if test "z$expect" = "z$actual" - then - : happy - else - echo "Oops: expected $expect" - echo "but got $actual" - false - fi + maybe_remove_timestamp "$pretty_content" $no_ts >expect && + maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts >actual && + test_cmp expect actual ' test -z "$content" || test_expect_success "--batch output of $type is correct" ' - expect="$(maybe_remove_timestamp "$batch_output" $no_ts)" - actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts)" - if test "z$expect" = "z$actual" - then - : happy - else - echo "Oops: expected $expect" - echo "but got $actual" - false - fi + maybe_remove_timestamp "$batch_output" $no_ts >expect && + maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts >actual && + test_cmp expect actual ' test_expect_success "--batch-check output of $type is correct" ' - expect="$sha1 $type $size" - actual="$(echo_without_newline $sha1 | git cat-file --batch-check)" - if test "z$expect" = "z$actual" - then - : happy - else - echo "Oops: expected $expect" - echo "but got $actual" - false - fi + echo "$sha1 $type $size" >expect && + echo_without_newline $sha1 | git cat-file --batch-check >actual && + test_cmp expect actual + ' + + test_expect_success "custom --batch-check format" ' + echo "$type $sha1" >expect && + echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual && + test_cmp expect actual + ' + + test_expect_success '--batch-check with %(rest)' ' + echo "$type this is some extra content" >expect && + echo "$sha1 this is some extra content" | + git cat-file --batch-check="%(objecttype) %(rest)" >actual && + test_cmp expect actual ' } @@ -110,15 +98,23 @@ test_expect_success "setup" ' run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content" +test_expect_success '--batch-check without %(rest) considers whole line' ' + echo "$hello_sha1 blob $hello_size" >expect && + git update-index --add --cacheinfo 100644 $hello_sha1 "white space" && + test_when_finished "git update-index --remove \"white space\"" && + echo ":white space" | git cat-file --batch-check >actual && + test_cmp expect actual +' + tree_sha1=$(git write-tree) tree_size=33 tree_pretty_content="100644 blob $hello_sha1 hello" run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content" -commit_message="Intial commit" +commit_message="Initial commit" commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1) -commit_size=176 +commit_size=177 commit_content="tree $tree_sha1 author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000 @@ -135,14 +131,11 @@ tag_description="This is a tag" tag_content="$tag_header_without_timestamp 0000000000 +0000 $tag_description" -tag_pretty_content="$tag_header_without_timestamp Thu Jan 1 00:00:00 1970 +0000 - -$tag_description" tag_sha1=$(echo_without_newline "$tag_content" | git mktag) tag_size=$(strlen "$tag_content") -run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_pretty_content" 1 +run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_content" 1 test_expect_success \ "Reach a blob from a tag pointing to it" \ @@ -201,6 +194,12 @@ test_expect_success "--batch-check for an emtpy line" ' test " missing" = "$(echo | git cat-file --batch-check)" ' +test_expect_success 'empty --batch-check notices missing object' ' + echo "$_z40 missing" >expect && + echo "$_z40" | git cat-file --batch-check="" >actual && + test_cmp expect actual +' + batch_input="$hello_sha1 $commit_sha1 $tag_sha1 diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh index 5c0053a20b..0c74beedd2 100755 --- a/t/t1011-read-tree-sparse-checkout.sh +++ b/t/t1011-read-tree-sparse-checkout.sh @@ -250,4 +250,28 @@ EOF test_cmp expected actual ' +test_expect_success 'checkout without --ignore-skip-worktree-bits' ' + echo "*" >.git/info/sparse-checkout && + git checkout -f top && + test_path_is_file init.t && + echo sub >.git/info/sparse-checkout && + git checkout && + echo modified >> sub/added && + git checkout . && + test_path_is_missing init.t && + git diff --exit-code HEAD +' + +test_expect_success 'checkout with --ignore-skip-worktree-bits' ' + echo "*" >.git/info/sparse-checkout && + git checkout -f top && + test_path_is_file init.t && + echo sub >.git/info/sparse-checkout && + git checkout && + echo modified >> sub/added && + git checkout --ignore-skip-worktree-bits . && + test_path_is_file init.t && + git diff --exit-code HEAD +' + test_done diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh index e23ac0e69d..1e2945ec7e 100755 --- a/t/t1020-subdirectory.sh +++ b/t/t1020-subdirectory.sh @@ -111,19 +111,19 @@ test_expect_success 'read-tree' ' test_expect_success 'alias expansion' ' ( - git config alias.ss status && + git config alias.test-status-alias status && cd dir && git status && - git ss + git test-status-alias ) ' test_expect_success NOT_MINGW '!alias expansion' ' pwd >expect && ( - git config alias.test !pwd && + git config alias.test-alias-directory !pwd && cd dir && - git test >../actual + git test-alias-directory >../actual ) && test_cmp expect actual ' @@ -131,9 +131,9 @@ test_expect_success NOT_MINGW '!alias expansion' ' test_expect_success 'GIT_PREFIX for !alias' ' printf "dir/" >expect && ( - git config alias.test "!sh -c \"printf \$GIT_PREFIX\"" && + git config alias.test-alias-directory "!sh -c \"printf \$GIT_PREFIX\"" && cd dir && - git test >../actual + git test-alias-directory >../actual ) && test_cmp expect actual ' diff --git a/t/t1050-large.sh b/t/t1050-large.sh index 29d6024b7f..fd10528009 100755 --- a/t/t1050-large.sh +++ b/t/t1050-large.sh @@ -6,11 +6,15 @@ test_description='adding and checking out large blobs' . ./test-lib.sh test_expect_success setup ' - git config core.bigfilethreshold 200k && + # clone does not allow us to pass core.bigfilethreshold to + # new repos, so set core.bigfilethreshold globally + git config --global core.bigfilethreshold 200k && echo X | dd of=large1 bs=1k seek=2000 && echo X | dd of=large2 bs=1k seek=2000 && echo X | dd of=large3 bs=1k seek=2000 && - echo Y | dd of=huge bs=1k seek=2500 + echo Y | dd of=huge bs=1k seek=2500 && + GIT_ALLOC_LIMIT=1500 && + export GIT_ALLOC_LIMIT ' test_expect_success 'add a large file or two' ' @@ -100,4 +104,63 @@ test_expect_success 'packsize limit' ' ) ' +test_expect_success 'diff --raw' ' + git commit -q -m initial && + echo modified >>large1 && + git add large1 && + git commit -q -m modified && + git diff --raw HEAD^ +' + +test_expect_success 'hash-object' ' + git hash-object large1 +' + +test_expect_success 'cat-file a large file' ' + git cat-file blob :large1 >/dev/null +' + +test_expect_success 'cat-file a large file from a tag' ' + git tag -m largefile largefiletag :large1 && + git cat-file blob largefiletag >/dev/null +' + +test_expect_success 'git-show a large file' ' + git show :large1 >/dev/null + +' + +test_expect_success 'index-pack' ' + git clone file://"`pwd`"/.git foo && + GIT_DIR=non-existent git index-pack --strict --verify foo/.git/objects/pack/*.pack +' + +test_expect_success 'repack' ' + git repack -ad +' + +test_expect_success 'pack-objects with large loose object' ' + SHA1=`git hash-object huge` && + test_create_repo loose && + echo $SHA1 | git pack-objects --stdout | + GIT_ALLOC_LIMIT=0 GIT_DIR=loose/.git git unpack-objects && + echo $SHA1 | GIT_DIR=loose/.git git pack-objects pack && + test_create_repo packed && + mv pack-* packed/.git/objects/pack && + GIT_DIR=packed/.git git cat-file blob $SHA1 >actual && + cmp huge actual +' + +test_expect_success 'tar achiving' ' + git archive --format=tar HEAD >/dev/null +' + +test_expect_success 'zip achiving, store only' ' + git archive --format=zip -0 HEAD >/dev/null +' + +test_expect_success 'zip achiving, deflate' ' + git archive --format=zip HEAD >/dev/null +' + test_done diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh new file mode 100755 index 0000000000..3f8705139d --- /dev/null +++ b/t/t1060-object-corruption.sh @@ -0,0 +1,104 @@ +#!/bin/sh + +test_description='see how we handle various forms of corruption' +. ./test-lib.sh + +# convert "1234abcd" to ".git/objects/12/34abcd" +obj_to_file() { + echo "$(git rev-parse --git-dir)/objects/$(git rev-parse "$1" | sed 's,..,&/,')" +} + +# Convert byte at offset "$2" of object "$1" into '\0' +corrupt_byte() { + obj_file=$(obj_to_file "$1") && + chmod +w "$obj_file" && + printf '\0' | dd of="$obj_file" bs=1 seek="$2" conv=notrunc +} + +test_expect_success 'setup corrupt repo' ' + git init bit-error && + ( + cd bit-error && + test_commit content && + corrupt_byte HEAD:content.t 10 + ) +' + +test_expect_success 'setup repo with missing object' ' + git init missing && + ( + cd missing && + test_commit content && + rm -f "$(obj_to_file HEAD:content.t)" + ) +' + +test_expect_success 'setup repo with misnamed object' ' + git init misnamed && + ( + cd misnamed && + test_commit content && + good=$(obj_to_file HEAD:content.t) && + blob=$(echo corrupt | git hash-object -w --stdin) && + bad=$(obj_to_file $blob) && + rm -f "$good" && + mv "$bad" "$good" + ) +' + +test_expect_success 'streaming a corrupt blob fails' ' + ( + cd bit-error && + test_must_fail git cat-file blob HEAD:content.t + ) +' + +test_expect_success 'read-tree -u detects bit-errors in blobs' ' + ( + cd bit-error && + rm -f content.t && + test_must_fail git read-tree --reset -u HEAD + ) +' + +test_expect_success 'read-tree -u detects missing objects' ' + ( + cd missing && + rm -f content.t && + test_must_fail git read-tree --reset -u HEAD + ) +' + +# We use --bare to make sure that the transport detects it, not the checkout +# phase. +test_expect_success 'clone --no-local --bare detects corruption' ' + test_must_fail git clone --no-local --bare bit-error corrupt-transport +' + +test_expect_success 'clone --no-local --bare detects missing object' ' + test_must_fail git clone --no-local --bare missing missing-transport +' + +test_expect_success 'clone --no-local --bare detects misnamed object' ' + test_must_fail git clone --no-local --bare misnamed misnamed-transport +' + +# We do not expect --local to detect corruption at the transport layer, +# so we are really checking the checkout() code path. +test_expect_success 'clone --local detects corruption' ' + test_must_fail git clone --local bit-error corrupt-checkout +' + +test_expect_success 'error detected during checkout leaves repo intact' ' + test_path_is_dir corrupt-checkout/.git +' + +test_expect_success 'clone --local detects missing objects' ' + test_must_fail git clone --local missing missing-checkout +' + +test_expect_failure 'clone --local detects misnamed objects' ' + test_must_fail git clone --local misnamed misnamed-checkout +' + +test_done diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh index c4414ff576..f8457f9d14 100755 --- a/t/t1100-commit-tree-options.sh +++ b/t/t1100-commit-tree-options.sh @@ -7,6 +7,9 @@ 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. + +Also make sure that command line parser understands the normal +"flags first and then non flag arguments" command line. ' . ./test-lib.sh @@ -42,4 +45,19 @@ test_expect_success \ 'compare commit' \ 'test_cmp expected commit' + +test_expect_success 'flags and then non flags' ' + test_tick && + echo comment text | + git commit-tree $(cat treeid) >commitid && + echo comment text | + git commit-tree $(cat treeid) -p $(cat commitid) >childid-1 && + echo comment text | + git commit-tree -p $(cat commitid) $(cat treeid) >childid-2 && + test_cmp childid-1 childid-2 && + git commit-tree $(cat treeid) -m foo >childid-3 && + git commit-tree -m foo $(cat treeid) >childid-4 && + test_cmp childid-3 childid-4 +' + test_done diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh index 9356beaf4b..397ccb6909 100755 --- a/t/t1200-tutorial.sh +++ b/t/t1200-tutorial.sh @@ -154,8 +154,8 @@ test_expect_success 'git show-branch' ' cat > resolve.expect << EOF Updating VARIABLE..VARIABLE FASTFORWARD (no commit created; -m option ignored) - example | 1 + - hello | 1 + + example | 1 + + hello | 1 + 2 files changed, 2 insertions(+) EOF diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 728a965669..967359344d 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -55,11 +55,13 @@ test_expect_success 'uppercase section' ' test_cmp expect .git/config ' -test_expect_success 'replace with non-match' \ - 'git config core.penguin kingpin !blue' +test_expect_success 'replace with non-match' ' + git config core.penguin kingpin !blue +' -test_expect_success 'replace with non-match (actually matching)' \ - 'git config core.penguin "very blue" !kingpin' +test_expect_success 'replace with non-match (actually matching)' ' + git config core.penguin "very blue" !kingpin +' cat > expect << EOF [core] @@ -108,8 +110,9 @@ baz = multiple \ lines EOF -test_expect_success 'unset with cont. lines' \ - 'git config --unset beta.baz' +test_expect_success 'unset with cont. lines' ' + git config --unset beta.baz +' cat > expect <<\EOF [alpha] @@ -133,8 +136,9 @@ EOF cp .git/config .git/config2 -test_expect_success 'multiple unset' \ - 'git config --unset-all beta.haha' +test_expect_success 'multiple unset' ' + git config --unset-all beta.haha +' cat > expect << EOF [beta] ; silly comment # another comment @@ -145,7 +149,9 @@ noIndent= sillyValue ; 'nother silly comment [nextSection] noNewline = ouch EOF -test_expect_success 'multiple unset is correct' 'test_cmp expect .git/config' +test_expect_success 'multiple unset is correct' ' + test_cmp expect .git/config +' cp .git/config2 .git/config @@ -156,8 +162,9 @@ test_expect_success '--replace-all missing value' ' rm .git/config2 -test_expect_success '--replace-all' \ - 'git config --replace-all beta.haha gamma' +test_expect_success '--replace-all' ' + git config --replace-all beta.haha gamma +' cat > expect << EOF [beta] ; silly comment # another comment @@ -169,7 +176,9 @@ noIndent= sillyValue ; 'nother silly comment [nextSection] noNewline = ouch EOF -test_expect_success 'all replaced' 'test_cmp expect .git/config' +test_expect_success 'all replaced' ' + test_cmp expect .git/config +' cat > expect << EOF [beta] ; silly comment # another comment @@ -200,7 +209,11 @@ test_expect_success 'really really mean test' ' test_cmp expect .git/config ' -test_expect_success 'get value' 'test alpha = $(git config beta.haha)' +test_expect_success 'get value' ' + echo alpha >expect && + git config beta.haha >actual && + test_cmp expect actual +' cat > expect << EOF [beta] ; silly comment # another comment @@ -231,18 +244,30 @@ test_expect_success 'multivar' ' test_cmp expect .git/config ' -test_expect_success 'non-match' \ - 'git config --get nextsection.nonewline !for' +test_expect_success 'non-match' ' + git config --get nextsection.nonewline !for +' -test_expect_success 'non-match value' \ - 'test wow = $(git config --get nextsection.nonewline !for)' +test_expect_success 'non-match value' ' + echo wow >expect && + git config --get nextsection.nonewline !for >actual && + test_cmp expect actual +' -test_expect_success 'ambiguous get' ' - test_must_fail git config --get nextsection.nonewline +test_expect_success 'multi-valued get returns final one' ' + echo "wow2 for me" >expect && + git config --get nextsection.nonewline >actual && + test_cmp expect actual ' -test_expect_success 'get multivar' \ - 'git config --get-all nextsection.nonewline' +test_expect_success 'multi-valued get-all returns all' ' + cat >expect <<-\EOF && + wow + wow2 for me + EOF + git config --get-all nextsection.nonewline >actual && + test_cmp expect actual +' cat > expect << EOF [beta] ; silly comment # another comment @@ -259,10 +284,6 @@ test_expect_success 'multivar replace' ' test_cmp expect .git/config ' -test_expect_success 'ambiguous value' ' - test_must_fail git config nextsection.nonewline -' - test_expect_success 'ambiguous unset' ' test_must_fail git config --unset nextsection.nonewline ' @@ -290,8 +311,9 @@ test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla' test_expect_success 'correct key' 'git config 123456.a123 987' -test_expect_success 'hierarchical section' \ - 'git config Version.1.2.3eX.Alpha beta' +test_expect_success 'hierarchical section' ' + git config Version.1.2.3eX.Alpha beta +' cat > expect << EOF [beta] ; silly comment # another comment @@ -307,7 +329,9 @@ noIndent= sillyValue ; 'nother silly comment Alpha = beta EOF -test_expect_success 'hierarchical section value' 'test_cmp expect .git/config' +test_expect_success 'hierarchical section value' ' + test_cmp expect .git/config +' cat > expect << EOF beta.noindent=sillyValue @@ -316,9 +340,10 @@ nextsection.nonewline=wow2 for me version.1.2.3eX.alpha=beta EOF -test_expect_success 'working --list' \ - 'git config --list > output && cmp output expect' - +test_expect_success 'working --list' ' + git config --list > output && + test_cmp expect output +' cat > expect << EOF EOF @@ -332,8 +357,10 @@ beta.noindent sillyValue nextsection.nonewline wow2 for me EOF -test_expect_success '--get-regexp' \ - 'git config --get-regexp in > output && cmp output expect' +test_expect_success '--get-regexp' ' + git config --get-regexp in >output && + test_cmp expect output +' cat > expect << EOF wow2 for me @@ -353,45 +380,52 @@ cat > .git/config << EOF variable = EOF -test_expect_success 'get variable with no value' \ - 'git config --get novalue.variable ^$' +test_expect_success 'get variable with no value' ' + git config --get novalue.variable ^$ +' -test_expect_success 'get variable with empty value' \ - 'git config --get emptyvalue.variable ^$' +test_expect_success 'get variable with empty value' ' + git config --get emptyvalue.variable ^$ +' echo novalue.variable > expect -test_expect_success 'get-regexp variable with no value' \ - 'git config --get-regexp novalue > output && - cmp output expect' +test_expect_success 'get-regexp variable with no value' ' + git config --get-regexp novalue > output && + test_cmp expect output +' echo 'novalue.variable true' > expect -test_expect_success 'get-regexp --bool variable with no value' \ - 'git config --bool --get-regexp novalue > output && - cmp output expect' +test_expect_success 'get-regexp --bool variable with no value' ' + git config --bool --get-regexp novalue > output && + test_cmp expect output +' echo 'emptyvalue.variable ' > expect -test_expect_success 'get-regexp variable with empty value' \ - 'git config --get-regexp emptyvalue > output && - cmp output expect' +test_expect_success 'get-regexp variable with empty value' ' + git config --get-regexp emptyvalue > output && + test_cmp expect output +' echo true > expect -test_expect_success 'get bool variable with no value' \ - 'git config --bool novalue.variable > output && - cmp output expect' +test_expect_success 'get bool variable with no value' ' + git config --bool novalue.variable > output && + test_cmp expect output +' echo false > expect -test_expect_success 'get bool variable with empty value' \ - 'git config --bool emptyvalue.variable > output && - cmp output expect' +test_expect_success 'get bool variable with empty value' ' + git config --bool emptyvalue.variable > output && + test_cmp expect output +' test_expect_success 'no arguments, but no crash' ' test_must_fail git config >output 2>&1 && - grep usage output + test_i18ngrep usage output ' cat > .git/config << EOF @@ -427,8 +461,9 @@ test_expect_success 'new variable inserts into proper section' ' test_cmp expect .git/config ' -test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \ - 'test_must_fail git config --file non-existing-config -l' +test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' ' + test_must_fail git config --file non-existing-config -l +' cat > other-config << EOF [ein] @@ -444,20 +479,30 @@ test_expect_success 'alternative GIT_CONFIG' ' test_cmp expect output ' -test_expect_success 'alternative GIT_CONFIG (--file)' \ - 'git config --file other-config -l > output && cmp output expect' +test_expect_success 'alternative GIT_CONFIG (--file)' ' + git config --file other-config -l > output && + test_cmp expect output +' test_expect_success 'refer config from subdirectory' ' mkdir x && ( cd x && - echo strasse >expect + echo strasse >expect && git config --get --file ../other-config ein.bahn >actual && test_cmp expect actual ) ' +test_expect_success 'refer config from subdirectory via GIT_CONFIG' ' + ( + cd x && + GIT_CONFIG=../other-config git config --get ein.bahn >actual && + test_cmp expect actual + ) +' + cat > expect << EOF [ein] bahn = strasse @@ -481,8 +526,9 @@ cat > .git/config << EOF weird EOF -test_expect_success "rename section" \ - "git config --rename-section branch.eins branch.zwei" +test_expect_success 'rename section' ' + git config --rename-section branch.eins branch.zwei +' cat > expect << EOF # Hallo @@ -495,17 +541,22 @@ cat > expect << EOF weird EOF -test_expect_success "rename succeeded" "test_cmp expect .git/config" +test_expect_success 'rename succeeded' ' + test_cmp expect .git/config +' -test_expect_success "rename non-existing section" ' +test_expect_success 'rename non-existing section' ' test_must_fail git config --rename-section \ branch."world domination" branch.drei ' -test_expect_success "rename succeeded" "test_cmp expect .git/config" +test_expect_success 'rename succeeded' ' + test_cmp expect .git/config +' -test_expect_success "rename another section" \ - 'git config --rename-section branch."1 234 blabl/a" branch.drei' +test_expect_success 'rename another section' ' + git config --rename-section branch."1 234 blabl/a" branch.drei +' cat > expect << EOF # Hallo @@ -518,14 +569,17 @@ cat > expect << EOF weird EOF -test_expect_success "rename succeeded" "test_cmp expect .git/config" +test_expect_success 'rename succeeded' ' + test_cmp expect .git/config +' cat >> .git/config << EOF [branch "vier"] z = 1 EOF -test_expect_success "rename a section with a var on the same line" \ - 'git config --rename-section branch.vier branch.zwei' +test_expect_success 'rename a section with a var on the same line' ' + git config --rename-section branch.vier branch.zwei +' cat > expect << EOF # Hallo @@ -540,13 +594,25 @@ weird z = 1 EOF -test_expect_success "rename succeeded" "test_cmp expect .git/config" +test_expect_success 'rename succeeded' ' + test_cmp expect .git/config +' + +test_expect_success 'renaming empty section name is rejected' ' + test_must_fail git config --rename-section branch.zwei "" +' + +test_expect_success 'renaming to bogus section is rejected' ' + test_must_fail git config --rename-section branch.zwei "bogus name" +' cat >> .git/config << EOF [branch "zwei"] a = 1 [branch "vier"] EOF -test_expect_success "remove section" "git config --remove-section branch.zwei" +test_expect_success 'remove section' ' + git config --remove-section branch.zwei +' cat > expect << EOF # Hallo @@ -555,8 +621,9 @@ cat > expect << EOF weird EOF -test_expect_success "section was removed properly" \ - "test_cmp expect .git/config" +test_expect_success 'section was removed properly' ' + test_cmp expect .git/config +' cat > expect << EOF [gitcvs] @@ -567,7 +634,6 @@ cat > expect << EOF EOF test_expect_success 'section ending' ' - rm -f .git/config && git config gitcvs.enabled true && git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite && @@ -577,30 +643,32 @@ test_expect_success 'section ending' ' ' test_expect_success numbers ' - git config kilo.gram 1k && git config mega.ton 1m && - k=$(git config --int --get kilo.gram) && - test z1024 = "z$k" && - m=$(git config --int --get mega.ton) && - test z1048576 = "z$m" + echo 1024 >expect && + echo 1048576 >>expect && + git config --int --get kilo.gram >actual && + git config --int --get mega.ton >>actual && + test_cmp expect actual ' -cat > expect <<EOF -fatal: bad config value for 'aninvalid.unit' in .git/config -EOF +test_expect_success '--int is at least 64 bits' ' + git config giga.watts 121g && + echo 129922760704 >expect && + git config --int --get giga.watts >actual && + test_cmp expect actual +' test_expect_success 'invalid unit' ' - git config aninvalid.unit "1auto" && - s=$(git config aninvalid.unit) && - test "z1auto" = "z$s" && - if git config --int --get aninvalid.unit 2>actual - then - echo config should have failed - false - fi && - cmp actual expect + echo 1auto >expect && + git config aninvalid.unit >actual && + test_cmp expect actual && + cat >expect <<-\EOF + fatal: bad numeric config value '\''1auto'\'' for '\''aninvalid.unit'\'' in .git/config: invalid unit + EOF + test_must_fail git config --int --get aninvalid.unit 2>actual && + test_i18ncmp expect actual ' cat > expect << EOF @@ -630,7 +698,7 @@ test_expect_success bool ' git config --bool --get bool.true$i >>result git config --bool --get bool.false$i >>result done && - cmp expect result' + test_cmp expect result' test_expect_success 'invalid bool (--get)' ' @@ -664,7 +732,7 @@ test_expect_success 'set --bool' ' git config --bool bool.false2 "" && git config --bool bool.false3 nO && git config --bool bool.false4 FALSE && - cmp expect .git/config' + test_cmp expect .git/config' cat > expect <<\EOF [int] @@ -679,39 +747,37 @@ test_expect_success 'set --int' ' git config --int int.val1 01 && git config --int int.val2 -1 && git config --int int.val3 5m && - cmp expect .git/config' + test_cmp expect .git/config +' -cat >expect <<\EOF -[bool] - true1 = true +test_expect_success 'get --bool-or-int' ' + cat >.git/config <<-\EOF && + [bool] + true1 true2 = true - false1 = false - false2 = false -[int] + false = false + [int] int1 = 0 int2 = 1 int3 = -1 -EOF - -test_expect_success 'get --bool-or-int' ' - rm -f .git/config && - ( - echo "[bool]" - echo true1 - echo true2 = true - echo false = false - echo "[int]" - echo int1 = 0 - echo int2 = 1 - echo int3 = -1 - ) >>.git/config && - test $(git config --bool-or-int bool.true1) = true && - test $(git config --bool-or-int bool.true2) = true && - test $(git config --bool-or-int bool.false) = false && - test $(git config --bool-or-int int.int1) = 0 && - test $(git config --bool-or-int int.int2) = 1 && - test $(git config --bool-or-int int.int3) = -1 - + EOF + cat >expect <<-\EOF && + true + true + false + 0 + 1 + -1 + EOF + { + git config --bool-or-int bool.true1 && + git config --bool-or-int bool.true2 && + git config --bool-or-int bool.false && + git config --bool-or-int int.int1 && + git config --bool-or-int int.int2 && + git config --bool-or-int int.int3 + } >actual && + test_cmp expect actual ' cat >expect <<\EOF @@ -787,6 +853,11 @@ test_expect_success NOT_MINGW 'get --path copes with unset $HOME' ' test_cmp expect result ' +test_expect_success 'get --path barfs on boolean variable' ' + echo "[path]bool" >.git/config && + test_must_fail git config --get --path path.bool +' + cat > expect << EOF [quote] leading = " test" @@ -828,7 +899,7 @@ EOF test_expect_success 'value continued on next line' ' git config --list > result && - cmp result expect + test_cmp result expect ' cat > .git/config <<\EOF @@ -864,11 +935,12 @@ test_expect_success '--null --get-regexp' ' test_expect_success 'inner whitespace kept verbatim' ' git config section.val "foo bar" && - test "z$(git config section.val)" = "zfoo bar" + echo "foo bar" >expect && + git config section.val >actual && + test_cmp expect actual ' test_expect_success SYMLINKS 'symlinked configuration' ' - ln -s notyet myconfig && GIT_CONFIG=myconfig git config test.frotz nitfol && test -h myconfig && @@ -877,9 +949,15 @@ test_expect_success SYMLINKS 'symlinked configuration' ' GIT_CONFIG=myconfig git config test.xyzzy rezrov && test -h myconfig && test -f notyet && - test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol && - test "z$(GIT_CONFIG=notyet git config test.xyzzy)" = zrezrov - + cat >expect <<-\EOF && + nitfol + rezrov + EOF + { + GIT_CONFIG=notyet git config test.frotz && + GIT_CONFIG=notyet git config test.xyzzy + } >actual && + test_cmp expect actual ' test_expect_success 'nonexistent configuration' ' @@ -911,12 +989,20 @@ test_expect_success 'check split_cmdline return' " git commit -m 'initial commit' && git config branch.master.mergeoptions 'echo \"' && test_must_fail git merge master - " +" test_expect_success 'git -c "key=value" support' ' - test "z$(git -c core.name=value config core.name)" = zvalue && - test "z$(git -c foo.CamelCase=value config foo.camelcase)" = zvalue && - test "z$(git -c foo.flag config --bool foo.flag)" = ztrue && + cat >expect <<-\EOF && + value + value + true + EOF + { + git -c core.name=value config core.name && + git -c foo.CamelCase=value config foo.camelcase && + git -c foo.flag config --bool foo.flag + } >actual && + test_cmp expect actual && test_must_fail git -c name=value config core.name ' @@ -960,6 +1046,23 @@ test_expect_success 'git -c complains about empty key and value' ' test_must_fail git -c "" rev-parse ' +test_expect_success 'git config --edit works' ' + git config -f tmp test.value no && + echo test.value=yes >expect && + GIT_EDITOR="echo [test]value=yes >" git config -f tmp --edit && + git config -f tmp --list >actual && + test_cmp expect actual +' + +test_expect_success 'git config --edit respects core.editor' ' + git config -f tmp test.value no && + echo test.value=yes >expect && + test_config core.editor "echo [test]value=yes >" && + git config -f tmp --edit && + git config -f tmp --list >actual && + test_cmp expect actual +' + # malformed configuration files test_expect_success 'barf on syntax error' ' cat >.git/config <<-\EOF && @@ -991,4 +1094,64 @@ test_expect_success 'barf on incomplete string' ' grep " line 3 " error ' +test_expect_success 'urlmatch' ' + cat >.git/config <<-\EOF && + [http] + sslVerify + [http "https://weak.example.com"] + sslVerify = false + cookieFile = /tmp/cookie.txt + EOF + + echo true >expect && + git config --bool --get-urlmatch http.SSLverify https://good.example.com >actual && + test_cmp expect actual && + + echo false >expect && + git config --bool --get-urlmatch http.sslverify https://weak.example.com >actual && + test_cmp expect actual && + + { + echo http.cookiefile /tmp/cookie.txt && + echo http.sslverify false + } >expect && + git config --get-urlmatch HTTP https://weak.example.com >actual && + test_cmp expect actual +' + +# good section hygiene +test_expect_failure 'unsetting the last key in a section removes header' ' + cat >.git/config <<-\EOF && + # some generic comment on the configuration file itself + # a comment specific to this "section" section. + [section] + # some intervening lines + # that should also be dropped + + key = value + # please be careful when you update the above variable + EOF + + cat >expect <<-\EOF && + # some generic comment on the configuration file itself + EOF + + git config --unset section.key && + test_cmp expect .git/config +' + +test_expect_failure 'adding a key into an empty section reuses header' ' + cat >.git/config <<-\EOF && + [section] + EOF + + q_to_tab >expect <<-\EOF && + [section] + Qkey = value + EOF + + git config section.key value + test_cmp expect .git/config +' + test_done diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh index 2b962cfda7..79045abb51 100755 --- a/t/t1304-default-acl.sh +++ b/t/t1304-default-acl.sh @@ -14,16 +14,15 @@ umask 077 # We need an arbitrary other user give permission to using ACLs. root # is a good candidate: exists on all unices, and it has permission # anyway, so we don't create a security hole running the testsuite. - -setfacl_out="$(setfacl -m u:root:rwx . 2>&1)" -setfacl_ret=$? - -if test $setfacl_ret != 0 -then - say "Unable to use setfacl (output: '$setfacl_out'; return code: '$setfacl_ret')" -else - test_set_prereq SETFACL -fi +test_expect_success 'checking for a working acl setup' ' + if setfacl -m d:m:rwx -m u:root:rwx . && + getfacl . | grep user:root:rwx && + touch should-have-readable-acl && + getfacl should-have-readable-acl | egrep "mask::?rw-" + then + test_set_prereq SETFACL + fi +' if test -z "$LOGNAME" then diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh new file mode 100755 index 0000000000..a70707620f --- /dev/null +++ b/t/t1305-config-include.sh @@ -0,0 +1,142 @@ +#!/bin/sh + +test_description='test config file include directives' +. ./test-lib.sh + +test_expect_success 'include file by absolute path' ' + echo "[test]one = 1" >one && + echo "[include]path = \"$(pwd)/one\"" >.gitconfig && + echo 1 >expect && + git config test.one >actual && + test_cmp expect actual +' + +test_expect_success 'include file by relative path' ' + echo "[test]one = 1" >one && + echo "[include]path = one" >.gitconfig && + echo 1 >expect && + git config test.one >actual && + test_cmp expect actual +' + +test_expect_success 'chained relative paths' ' + mkdir subdir && + echo "[test]three = 3" >subdir/three && + echo "[include]path = three" >subdir/two && + echo "[include]path = subdir/two" >.gitconfig && + echo 3 >expect && + git config test.three >actual && + test_cmp expect actual +' + +test_expect_success 'include paths get tilde-expansion' ' + echo "[test]one = 1" >one && + echo "[include]path = ~/one" >.gitconfig && + echo 1 >expect && + git config test.one >actual && + test_cmp expect actual +' + +test_expect_success 'include options can still be examined' ' + echo "[test]one = 1" >one && + echo "[include]path = one" >.gitconfig && + echo one >expect && + git config include.path >actual && + test_cmp expect actual +' + +test_expect_success 'listing includes option and expansion' ' + echo "[test]one = 1" >one && + echo "[include]path = one" >.gitconfig && + cat >expect <<-\EOF && + include.path=one + test.one=1 + EOF + git config --list >actual.full && + grep -v ^core actual.full >actual && + test_cmp expect actual +' + +test_expect_success 'single file lookup does not expand includes by default' ' + echo "[test]one = 1" >one && + echo "[include]path = one" >.gitconfig && + test_must_fail git config -f .gitconfig test.one && + test_must_fail git config --global test.one && + echo 1 >expect && + git config --includes -f .gitconfig test.one >actual && + test_cmp expect actual +' + +test_expect_success 'single file list does not expand includes by default' ' + echo "[test]one = 1" >one && + echo "[include]path = one" >.gitconfig && + echo "include.path=one" >expect && + git config -f .gitconfig --list >actual && + test_cmp expect actual +' + +test_expect_success 'writing config file does not expand includes' ' + echo "[test]one = 1" >one && + echo "[include]path = one" >.gitconfig && + git config test.two 2 && + echo 2 >expect && + git config --no-includes test.two >actual && + test_cmp expect actual && + test_must_fail git config --no-includes test.one +' + +test_expect_success 'config modification does not affect includes' ' + echo "[test]one = 1" >one && + echo "[include]path = one" >.gitconfig && + git config test.one 2 && + echo 1 >expect && + git config -f one test.one >actual && + test_cmp expect actual && + cat >expect <<-\EOF && + 1 + 2 + EOF + git config --get-all test.one >actual && + test_cmp expect actual +' + +test_expect_success 'missing include files are ignored' ' + cat >.gitconfig <<-\EOF && + [include]path = foo + [test]value = yes + EOF + echo yes >expect && + git config test.value >actual && + test_cmp expect actual +' + +test_expect_success 'absolute includes from command line work' ' + echo "[test]one = 1" >one && + echo 1 >expect && + git -c include.path="$PWD/one" config test.one >actual && + test_cmp expect actual +' + +test_expect_success 'relative includes from command line fail' ' + echo "[test]one = 1" >one && + test_must_fail git -c include.path=one config test.one +' + +test_expect_success 'include cycles are detected' ' + cat >.gitconfig <<-\EOF && + [test]value = gitconfig + [include]path = cycle + EOF + cat >cycle <<-\EOF && + [test]value = cycle + [include]path = .gitconfig + EOF + cat >expect <<-\EOF && + gitconfig + cycle + EOF + test_must_fail git config --get-all test.value 2>stderr && + grep "exceeded maximum include depth" stderr +' + +test_done diff --git a/t/t1306-xdg-files.sh b/t/t1306-xdg-files.sh new file mode 100755 index 0000000000..8b14ab187c --- /dev/null +++ b/t/t1306-xdg-files.sh @@ -0,0 +1,197 @@ +#!/bin/sh +# +# Copyright (c) 2012 Valentin Duperray, Lucien Kong, Franck Jonas, +# Thomas Nguy, Khoi Nguyen +# Grenoble INP Ensimag +# + +test_description='Compatibility with $XDG_CONFIG_HOME/git/ files' + +. ./test-lib.sh + +test_expect_success 'read config: xdg file exists and ~/.gitconfig doesn'\''t' ' + mkdir -p .config/git && + echo "[alias]" >.config/git/config && + echo " myalias = !echo in_config" >>.config/git/config && + echo in_config >expected && + git myalias >actual && + test_cmp expected actual +' + + +test_expect_success 'read config: xdg file exists and ~/.gitconfig exists' ' + >.gitconfig && + echo "[alias]" >.gitconfig && + echo " myalias = !echo in_gitconfig" >>.gitconfig && + echo in_gitconfig >expected && + git myalias >actual && + test_cmp expected actual +' + + +test_expect_success 'read with --get: xdg file exists and ~/.gitconfig doesn'\''t' ' + rm .gitconfig && + echo "[user]" >.config/git/config && + echo " name = read_config" >>.config/git/config && + echo read_config >expected && + git config --get user.name >actual && + test_cmp expected actual +' + +test_expect_success '"$XDG_CONFIG_HOME overrides $HOME/.config/git' ' + mkdir -p "$HOME"/xdg/git && + echo "[user]name = in_xdg" >"$HOME"/xdg/git/config && + echo in_xdg >expected && + XDG_CONFIG_HOME="$HOME"/xdg git config --get-all user.name >actual && + test_cmp expected actual +' + +test_expect_success 'read with --get: xdg file exists and ~/.gitconfig exists' ' + >.gitconfig && + echo "[user]" >.gitconfig && + echo " name = read_gitconfig" >>.gitconfig && + echo read_gitconfig >expected && + git config --get user.name >actual && + test_cmp expected actual +' + + +test_expect_success 'read with --list: xdg file exists and ~/.gitconfig doesn'\''t' ' + rm .gitconfig && + echo user.name=read_config >expected && + git config --global --list >actual && + test_cmp expected actual +' + + +test_expect_success 'read with --list: xdg file exists and ~/.gitconfig exists' ' + >.gitconfig && + echo "[user]" >.gitconfig && + echo " name = read_gitconfig" >>.gitconfig && + echo user.name=read_gitconfig >expected && + git config --global --list >actual && + test_cmp expected actual +' + + +test_expect_success 'Setup' ' + git init git && + cd git && + echo foo >to_be_excluded +' + + +test_expect_success 'Exclusion of a file in the XDG ignore file' ' + mkdir -p "$HOME"/.config/git/ && + echo to_be_excluded >"$HOME"/.config/git/ignore && + test_must_fail git add to_be_excluded +' + +test_expect_success '$XDG_CONFIG_HOME overrides $HOME/.config/git/ignore' ' + mkdir -p "$HOME"/xdg/git && + echo content >excluded_by_xdg_only && + echo excluded_by_xdg_only >"$HOME"/xdg/git/ignore && + test_when_finished "git read-tree --empty" && + (XDG_CONFIG_HOME="$HOME/xdg" && + export XDG_CONFIG_HOME && + git add to_be_excluded && + test_must_fail git add excluded_by_xdg_only + ) +' + +test_expect_success 'Exclusion in both XDG and local ignore files' ' + echo to_be_excluded >.gitignore && + test_must_fail git add to_be_excluded +' + + +test_expect_success 'Exclusion in a non-XDG global ignore file' ' + rm .gitignore && + echo >"$HOME"/.config/git/ignore && + echo to_be_excluded >"$HOME"/my_gitignore && + git config core.excludesfile "$HOME"/my_gitignore && + test_must_fail git add to_be_excluded +' + +test_expect_success 'Checking XDG ignore file when HOME is unset' ' + >expected && + (sane_unset HOME && + git config --unset core.excludesfile && + git ls-files --exclude-standard --ignored >actual) && + test_cmp expected actual +' + +test_expect_success 'Checking attributes in the XDG attributes file' ' + echo foo >f && + git check-attr -a f >actual && + test_line_count -eq 0 actual && + echo "f attr_f" >"$HOME"/.config/git/attributes && + echo "f: attr_f: set" >expected && + git check-attr -a f >actual && + test_cmp expected actual +' + +test_expect_success 'Checking XDG attributes when HOME is unset' ' + >expected && + (sane_unset HOME && + git check-attr -a f >actual) && + test_cmp expected actual +' + +test_expect_success '$XDG_CONFIG_HOME overrides $HOME/.config/git/attributes' ' + mkdir -p "$HOME"/xdg/git && + echo "f attr_f=xdg" >"$HOME"/xdg/git/attributes && + echo "f: attr_f: xdg" >expected && + XDG_CONFIG_HOME="$HOME/xdg" git check-attr -a f >actual && + test_cmp expected actual +' + +test_expect_success 'Checking attributes in both XDG and local attributes files' ' + echo "f -attr_f" >.gitattributes && + echo "f: attr_f: unset" >expected && + git check-attr -a f >actual && + test_cmp expected actual +' + + +test_expect_success 'Checking attributes in a non-XDG global attributes file' ' + test_might_fail rm .gitattributes && + echo "f attr_f=test" >"$HOME"/my_gitattributes && + git config core.attributesfile "$HOME"/my_gitattributes && + echo "f: attr_f: test" >expected && + git check-attr -a f >actual && + test_cmp expected actual +' + + +test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\''t' ' + mkdir -p "$HOME"/.config/git && + >"$HOME"/.config/git/config && + test_might_fail rm "$HOME"/.gitconfig && + git config --global user.name "write_config" && + echo "[user]" >expected && + echo " name = write_config" >>expected && + test_cmp expected "$HOME"/.config/git/config +' + + +test_expect_success 'write: xdg file exists and ~/.gitconfig exists' ' + >"$HOME"/.gitconfig && + git config --global user.name "write_gitconfig" && + echo "[user]" >expected && + echo " name = write_gitconfig" >>expected && + test_cmp expected "$HOME"/.gitconfig +' + + +test_expect_success 'write: ~/.config/git/ exists and config file doesn'\''t' ' + test_might_fail rm "$HOME"/.gitconfig && + test_might_fail rm "$HOME"/.config/git/config && + git config --global user.name "write_gitconfig" && + echo "[user]" >expected && + echo " name = write_gitconfig" >>expected && + test_cmp expected "$HOME"/.gitconfig +' + + +test_done diff --git a/t/t1307-config-blob.sh b/t/t1307-config-blob.sh new file mode 100755 index 0000000000..fdc257e66f --- /dev/null +++ b/t/t1307-config-blob.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +test_description='support for reading config from a blob' +. ./test-lib.sh + +test_expect_success 'create config blob' ' + cat >config <<-\EOF && + [some] + value = 1 + EOF + git add config && + git commit -m foo +' + +test_expect_success 'list config blob contents' ' + echo some.value=1 >expect && + git config --blob=HEAD:config --list >actual && + test_cmp expect actual +' + +test_expect_success 'fetch value from blob' ' + echo true >expect && + git config --blob=HEAD:config --bool some.value >actual && + test_cmp expect actual +' + +test_expect_success 'reading non-existing value from blob is an error' ' + test_must_fail git config --blob=HEAD:config non.existing +' + +test_expect_success 'reading from blob and file is an error' ' + test_must_fail git config --blob=HEAD:config --system --list +' + +test_expect_success 'reading from missing ref is an error' ' + test_must_fail git config --blob=HEAD:doesnotexist --list +' + +test_expect_success 'reading from non-blob is an error' ' + test_must_fail git config --blob=HEAD --list +' + +test_expect_success 'setting a value in a blob is an error' ' + test_must_fail git config --blob=HEAD:config some.value foo +' + +test_expect_success 'deleting a value in a blob is an error' ' + test_must_fail git config --blob=HEAD:config --unset some.value +' + +test_expect_success 'editing a blob is an error' ' + test_must_fail git config --blob=HEAD:config --edit +' + +test_expect_success 'parse errors in blobs are properly attributed' ' + cat >config <<-\EOF && + [some] + value = " + EOF + git add config && + git commit -m broken && + + test_must_fail git config --blob=HEAD:config some.value 2>err && + + # just grep for our token as the exact error message is likely to + # change or be internationalized + grep "HEAD:config" err +' + +test_done diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index 4fd83a667a..6ffd82fe32 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -74,6 +74,24 @@ test_expect_success "delete $m (by HEAD)" ' ' rm -f .git/$m +test_expect_success \ + "create $m (by HEAD)" \ + "git update-ref HEAD $A && + test $A"' = $(cat .git/'"$m"')' +test_expect_success \ + "pack refs" \ + "git pack-refs --all" +test_expect_success \ + "move $m (by HEAD)" \ + "git update-ref HEAD $B $A && + test $B"' = $(cat .git/'"$m"')' +test_expect_success "delete $m (by HEAD) should remove both packed and loose $m" ' + git update-ref -d HEAD $B && + ! grep "$m" .git/packed-refs && + ! test -f .git/$m +' +rm -f .git/$m + cp -f .git/HEAD .git/HEAD.orig test_expect_success "delete symref without dereference" ' git update-ref --no-deref -d HEAD && @@ -284,4 +302,636 @@ 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")' +a=refs/heads/a +b=refs/heads/b +c=refs/heads/c +E='""' +F='%s\0' +pws='path with space' + +test_expect_success 'stdin test setup' ' + echo "$pws" >"$pws" && + git add -- "$pws" && + git commit -m "$pws" +' + +test_expect_success '-z fails without --stdin' ' + test_must_fail git update-ref -z $m $m $m 2>err && + grep "usage: git update-ref" err +' + +test_expect_success 'stdin works with no input' ' + >stdin && + git update-ref --stdin <stdin && + git rev-parse --verify -q $m +' + +test_expect_success 'stdin fails on empty line' ' + echo "" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: empty command in input" err +' + +test_expect_success 'stdin fails on only whitespace' ' + echo " " >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: whitespace before command: " err +' + +test_expect_success 'stdin fails on leading whitespace' ' + echo " create $a $m" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: whitespace before command: create $a $m" err +' + +test_expect_success 'stdin fails on unknown command' ' + echo "unknown $a" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: unknown command: unknown $a" err +' + +test_expect_success 'stdin fails on badly quoted input' ' + echo "create $a \"master" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: badly quoted argument: \\\"master" err +' + +test_expect_success 'stdin fails on arguments not separated by space' ' + echo "create \"$a\"master" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: expected SP but got: master" err +' + +test_expect_success 'stdin fails create with no ref' ' + echo "create " >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: create line missing <ref>" err +' + +test_expect_success 'stdin fails create with bad ref name' ' + echo "create ~a $m" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: invalid ref format: ~a" err +' + +test_expect_success 'stdin fails create with no new value' ' + echo "create $a" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: create $a missing <newvalue>" err +' + +test_expect_success 'stdin fails create with too many arguments' ' + echo "create $a $m $m" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: create $a has extra input: $m" err +' + +test_expect_success 'stdin fails update with no ref' ' + echo "update " >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: update line missing <ref>" err +' + +test_expect_success 'stdin fails update with bad ref name' ' + echo "update ~a $m" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: invalid ref format: ~a" err +' + +test_expect_success 'stdin fails update with no new value' ' + echo "update $a" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: update $a missing <newvalue>" err +' + +test_expect_success 'stdin fails update with too many arguments' ' + echo "update $a $m $m $m" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: update $a has extra input: $m" err +' + +test_expect_success 'stdin fails delete with no ref' ' + echo "delete " >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: delete line missing <ref>" err +' + +test_expect_success 'stdin fails delete with bad ref name' ' + echo "delete ~a $m" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: invalid ref format: ~a" err +' + +test_expect_success 'stdin fails delete with too many arguments' ' + echo "delete $a $m $m" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: delete $a has extra input: $m" err +' + +test_expect_success 'stdin fails verify with too many arguments' ' + echo "verify $a $m $m" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: verify $a has extra input: $m" err +' + +test_expect_success 'stdin fails option with unknown name' ' + echo "option unknown" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: option unknown: unknown" err +' + +test_expect_success 'stdin fails with duplicate refs' ' + cat >stdin <<-EOF && + create $a $m + create $b $m + create $a $m + EOF + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err +' + +test_expect_success 'stdin create ref works' ' + echo "create $a $m" >stdin && + git update-ref --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual +' + +test_expect_success 'stdin update ref creates with zero old value' ' + echo "update $b $m $Z" >stdin && + git update-ref --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $b >actual && + test_cmp expect actual && + git update-ref -d $b +' + +test_expect_success 'stdin update ref creates with empty old value' ' + echo "update $b $m $E" >stdin && + git update-ref --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $b >actual && + test_cmp expect actual +' + +test_expect_success 'stdin create ref works with path with space to blob' ' + echo "create refs/blobs/pws \"$m:$pws\"" >stdin && + git update-ref --stdin <stdin && + git rev-parse "$m:$pws" >expect && + git rev-parse refs/blobs/pws >actual && + test_cmp expect actual && + git update-ref -d refs/blobs/pws +' + +test_expect_success 'stdin update ref fails with wrong old value' ' + echo "update $c $m $m~1" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin update ref fails with bad old value' ' + echo "update $c $m does-not-exist" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: invalid old value for ref $c: does-not-exist" err && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin create ref fails with bad new value' ' + echo "create $c does-not-exist" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: invalid new value for ref $c: does-not-exist" err && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin create ref fails with zero new value' ' + echo "create $c " >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: create $c given zero new value" err && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin update ref works with right old value' ' + echo "update $b $m~1 $m" >stdin && + git update-ref --stdin <stdin && + git rev-parse $m~1 >expect && + git rev-parse $b >actual && + test_cmp expect actual +' + +test_expect_success 'stdin delete ref fails with wrong old value' ' + echo "delete $a $m~1" >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: Cannot lock the ref '"'"'$a'"'"'" err && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual +' + +test_expect_success 'stdin delete ref fails with zero old value' ' + echo "delete $a " >stdin && + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: delete $a given zero old value" err && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual +' + +test_expect_success 'stdin update symref works option no-deref' ' + git symbolic-ref TESTSYMREF $b && + cat >stdin <<-EOF && + option no-deref + update TESTSYMREF $a $b + EOF + git update-ref --stdin <stdin && + git rev-parse TESTSYMREF >expect && + git rev-parse $a >actual && + test_cmp expect actual && + git rev-parse $m~1 >expect && + git rev-parse $b >actual && + test_cmp expect actual +' + +test_expect_success 'stdin delete symref works option no-deref' ' + git symbolic-ref TESTSYMREF $b && + cat >stdin <<-EOF && + option no-deref + delete TESTSYMREF $b + EOF + git update-ref --stdin <stdin && + test_must_fail git rev-parse --verify -q TESTSYMREF && + git rev-parse $m~1 >expect && + git rev-parse $b >actual && + test_cmp expect actual +' + +test_expect_success 'stdin delete ref works with right old value' ' + echo "delete $b $m~1" >stdin && + git update-ref --stdin <stdin && + test_must_fail git rev-parse --verify -q $b +' + +test_expect_success 'stdin update/create/verify combination works' ' + cat >stdin <<-EOF && + update $a $m + create $b $m + verify $c + EOF + git update-ref --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual && + git rev-parse $b >actual && + test_cmp expect actual && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin update refs works with identity updates' ' + cat >stdin <<-EOF && + update $a $m $m + update $b $m $m + update $c $Z $E + EOF + git update-ref --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual && + git rev-parse $b >actual && + test_cmp expect actual && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin update refs fails with wrong old value' ' + git update-ref $c $m && + cat >stdin <<-EOF && + update $a $m $m + update $b $m $m + update $c '' + EOF + test_must_fail git update-ref --stdin <stdin 2>err && + grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual && + git rev-parse $b >actual && + test_cmp expect actual && + git rev-parse $c >actual && + test_cmp expect actual +' + +test_expect_success 'stdin delete refs works with packed and loose refs' ' + git pack-refs --all && + git update-ref $c $m~1 && + cat >stdin <<-EOF && + delete $a $m + update $b $Z $m + update $c $E $m~1 + EOF + git update-ref --stdin <stdin && + test_must_fail git rev-parse --verify -q $a && + test_must_fail git rev-parse --verify -q $b && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin -z works on empty input' ' + >stdin && + git update-ref -z --stdin <stdin && + git rev-parse --verify -q $m +' + +test_expect_success 'stdin -z fails on empty line' ' + echo "" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: whitespace before command: " err +' + +test_expect_success 'stdin -z fails on empty command' ' + printf $F "" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: empty command in input" err +' + +test_expect_success 'stdin -z fails on only whitespace' ' + printf $F " " >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: whitespace before command: " err +' + +test_expect_success 'stdin -z fails on leading whitespace' ' + printf $F " create $a" "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: whitespace before command: create $a" err +' + +test_expect_success 'stdin -z fails on unknown command' ' + printf $F "unknown $a" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: unknown command: unknown $a" err +' + +test_expect_success 'stdin -z fails create with no ref' ' + printf $F "create " >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: create line missing <ref>" err +' + +test_expect_success 'stdin -z fails create with bad ref name' ' + printf $F "create ~a " "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: invalid ref format: ~a " err +' + +test_expect_success 'stdin -z fails create with no new value' ' + printf $F "create $a" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: create $a missing <newvalue>" err +' + +test_expect_success 'stdin -z fails create with too many arguments' ' + printf $F "create $a" "$m" "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: unknown command: $m" err +' + +test_expect_success 'stdin -z fails update with no ref' ' + printf $F "update " >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: update line missing <ref>" err +' + +test_expect_success 'stdin -z fails update with bad ref name' ' + printf $F "update ~a" "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: invalid ref format: ~a" err +' + +test_expect_success 'stdin -z fails update with no new value' ' + printf $F "update $a" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: update $a missing <newvalue>" err +' + +test_expect_success 'stdin -z fails update with no old value' ' + printf $F "update $a" "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: update $a missing \\[<oldvalue>\\] NUL" err +' + +test_expect_success 'stdin -z fails update with too many arguments' ' + printf $F "update $a" "$m" "$m" "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: unknown command: $m" err +' + +test_expect_success 'stdin -z fails delete with no ref' ' + printf $F "delete " >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: delete line missing <ref>" err +' + +test_expect_success 'stdin -z fails delete with bad ref name' ' + printf $F "delete ~a" "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: invalid ref format: ~a" err +' + +test_expect_success 'stdin -z fails delete with no old value' ' + printf $F "delete $a" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: delete $a missing \\[<oldvalue>\\] NUL" err +' + +test_expect_success 'stdin -z fails delete with too many arguments' ' + printf $F "delete $a" "$m" "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: unknown command: $m" err +' + +test_expect_success 'stdin -z fails verify with too many arguments' ' + printf $F "verify $a" "$m" "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: unknown command: $m" err +' + +test_expect_success 'stdin -z fails verify with no old value' ' + printf $F "verify $a" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: verify $a missing \\[<oldvalue>\\] NUL" err +' + +test_expect_success 'stdin -z fails option with unknown name' ' + printf $F "option unknown" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: option unknown: unknown" err +' + +test_expect_success 'stdin -z fails with duplicate refs' ' + printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err +' + +test_expect_success 'stdin -z create ref works' ' + printf $F "create $a" "$m" >stdin && + git update-ref -z --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual +' + +test_expect_success 'stdin -z update ref creates with zero old value' ' + printf $F "update $b" "$m" "$Z" >stdin && + git update-ref -z --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $b >actual && + test_cmp expect actual && + git update-ref -d $b +' + +test_expect_success 'stdin -z update ref creates with empty old value' ' + printf $F "update $b" "$m" "" >stdin && + git update-ref -z --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $b >actual && + test_cmp expect actual +' + +test_expect_success 'stdin -z create ref works with path with space to blob' ' + printf $F "create refs/blobs/pws" "$m:$pws" >stdin && + git update-ref -z --stdin <stdin && + git rev-parse "$m:$pws" >expect && + git rev-parse refs/blobs/pws >actual && + test_cmp expect actual && + git update-ref -d refs/blobs/pws +' + +test_expect_success 'stdin -z update ref fails with wrong old value' ' + printf $F "update $c" "$m" "$m~1" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin -z update ref fails with bad old value' ' + printf $F "update $c" "$m" "does-not-exist" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: invalid old value for ref $c: does-not-exist" err && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin -z create ref fails with bad new value' ' + printf $F "create $c" "does-not-exist" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: invalid new value for ref $c: does-not-exist" err && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin -z create ref fails with zero new value' ' + printf $F "create $c" "" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: create $c given zero new value" err && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin -z update ref works with right old value' ' + printf $F "update $b" "$m~1" "$m" >stdin && + git update-ref -z --stdin <stdin && + git rev-parse $m~1 >expect && + git rev-parse $b >actual && + test_cmp expect actual +' + +test_expect_success 'stdin -z delete ref fails with wrong old value' ' + printf $F "delete $a" "$m~1" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: Cannot lock the ref '"'"'$a'"'"'" err && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual +' + +test_expect_success 'stdin -z delete ref fails with zero old value' ' + printf $F "delete $a" "$Z" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: delete $a given zero old value" err && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual +' + +test_expect_success 'stdin -z update symref works option no-deref' ' + git symbolic-ref TESTSYMREF $b && + printf $F "option no-deref" "update TESTSYMREF" "$a" "$b" >stdin && + git update-ref -z --stdin <stdin && + git rev-parse TESTSYMREF >expect && + git rev-parse $a >actual && + test_cmp expect actual && + git rev-parse $m~1 >expect && + git rev-parse $b >actual && + test_cmp expect actual +' + +test_expect_success 'stdin -z delete symref works option no-deref' ' + git symbolic-ref TESTSYMREF $b && + printf $F "option no-deref" "delete TESTSYMREF" "$b" >stdin && + git update-ref -z --stdin <stdin && + test_must_fail git rev-parse --verify -q TESTSYMREF && + git rev-parse $m~1 >expect && + git rev-parse $b >actual && + test_cmp expect actual +' + +test_expect_success 'stdin -z delete ref works with right old value' ' + printf $F "delete $b" "$m~1" >stdin && + git update-ref -z --stdin <stdin && + test_must_fail git rev-parse --verify -q $b +' + +test_expect_success 'stdin -z update/create/verify combination works' ' + printf $F "update $a" "$m" "" "create $b" "$m" "verify $c" "" >stdin && + git update-ref -z --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual && + git rev-parse $b >actual && + test_cmp expect actual && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin -z update refs works with identity updates' ' + printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$Z" "" >stdin && + git update-ref -z --stdin <stdin && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual && + git rev-parse $b >actual && + test_cmp expect actual && + test_must_fail git rev-parse --verify -q $c +' + +test_expect_success 'stdin -z update refs fails with wrong old value' ' + git update-ref $c $m && + printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "" "$Z" >stdin && + test_must_fail git update-ref -z --stdin <stdin 2>err && + grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err && + git rev-parse $m >expect && + git rev-parse $a >actual && + test_cmp expect actual && + git rev-parse $b >actual && + test_cmp expect actual && + git rev-parse $c >actual && + test_cmp expect actual +' + +test_expect_success 'stdin -z delete refs works with packed and loose refs' ' + git pack-refs --all && + git update-ref $c $m~1 && + printf $F "delete $a" "$m" "update $b" "$Z" "$m" "update $c" "" "$m~1" >stdin && + git update-ref -z --stdin <stdin && + test_must_fail git rev-parse --verify -q $a && + test_must_fail git rev-parse --verify -q $b && + test_must_fail git rev-parse --verify -q $c +' + test_done diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh index 2c96551ed0..36378b0e3f 100755 --- a/t/t1401-symbolic-ref.sh +++ b/t/t1401-symbolic-ref.sh @@ -33,4 +33,34 @@ test_expect_success 'symbolic-ref refuses bare sha1' ' ' reset_to_sane +test_expect_success 'symbolic-ref deletes HEAD' ' + git symbolic-ref -d HEAD && + test_path_is_file .git/refs/heads/foo && + test_path_is_missing .git/HEAD +' +reset_to_sane + +test_expect_success 'symbolic-ref deletes dangling HEAD' ' + git symbolic-ref HEAD refs/heads/missing && + git symbolic-ref -d HEAD && + test_path_is_missing .git/refs/heads/missing && + test_path_is_missing .git/HEAD +' +reset_to_sane + +test_expect_success 'symbolic-ref fails to delete missing FOO' ' + echo "fatal: Cannot delete FOO, not a symbolic ref" >expect && + test_must_fail git symbolic-ref -d FOO >actual 2>&1 && + test_cmp expect actual +' +reset_to_sane + +test_expect_success 'symbolic-ref fails to delete real ref' ' + echo "fatal: Cannot delete refs/heads/foo, not a symbolic ref" >expect && + test_must_fail git symbolic-ref -d refs/heads/foo >actual 2>&1 && + test_path_is_file .git/refs/heads/foo && + test_cmp expect actual +' +reset_to_sane + test_done diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh index 1ae4d87c92..1a5a5f39fd 100755 --- a/t/t1402-check-ref-format.sh +++ b/t/t1402-check-ref-format.sh @@ -11,7 +11,8 @@ valid_ref() { prereq=$1 shift esac - test_expect_success $prereq "ref name '$1' is valid${2:+ with options $2}" " + desc="ref name '$1' is valid${2:+ with options $2}" + test_expect_success $prereq "$desc" " git check-ref-format $2 '$1' " } @@ -22,7 +23,8 @@ invalid_ref() { prereq=$1 shift esac - test_expect_success $prereq "ref name '$1' is invalid${2:+ with options $2}" " + desc="ref name '$1' is invalid${2:+ with options $2}" + test_expect_success $prereq "$desc" " test_must_fail git check-ref-format $2 '$1' " } diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh new file mode 100755 index 0000000000..3e500ed7da --- /dev/null +++ b/t/t1403-show-ref.sh @@ -0,0 +1,167 @@ +#!/bin/sh + +test_description='show-ref' +. ./test-lib.sh + +test_expect_success setup ' + test_commit A && + git tag -f -a -m "annotated A" A && + git checkout -b side && + test_commit B && + git tag -f -a -m "annotated B" B && + git checkout master && + test_commit C && + git branch B A^0 +' + +test_expect_success 'show-ref' ' + echo $(git rev-parse refs/tags/A) refs/tags/A >expect && + + git show-ref A >actual && + test_cmp expect actual && + + git show-ref tags/A >actual && + test_cmp expect actual && + + git show-ref refs/tags/A >actual && + test_cmp expect actual && + + >expect && + + test_must_fail git show-ref D >actual + test_cmp expect actual +' + +test_expect_success 'show-ref -q' ' + >expect && + + git show-ref -q A >actual && + test_cmp expect actual && + + git show-ref -q tags/A >actual && + test_cmp expect actual && + + git show-ref -q refs/tags/A >actual && + test_cmp expect actual && + + test_must_fail git show-ref -q D >actual && + test_cmp expect actual +' + +test_expect_success 'show-ref --verify' ' + echo $(git rev-parse refs/tags/A) refs/tags/A >expect && + + git show-ref --verify refs/tags/A >actual && + test_cmp expect actual && + + >expect && + + test_must_fail git show-ref --verify A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify tags/A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify D >actual + test_cmp expect actual +' + +test_expect_success 'show-ref --verify -q' ' + >expect && + + git show-ref --verify -q refs/tags/A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify -q A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify -q tags/A >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify -q D >actual + test_cmp expect actual +' + +test_expect_success 'show-ref -d' ' + { + echo $(git rev-parse refs/tags/A) refs/tags/A && + echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}" + echo $(git rev-parse refs/tags/C) refs/tags/C + } >expect && + git show-ref -d A C >actual && + test_cmp expect actual && + + git show-ref -d tags/A tags/C >actual && + test_cmp expect actual && + + git show-ref -d refs/tags/A refs/tags/C >actual && + test_cmp expect actual && + + echo $(git rev-parse refs/heads/master) refs/heads/master >expect && + git show-ref -d master >actual && + test_cmp expect actual && + + git show-ref -d heads/master >actual && + test_cmp expect actual && + + git show-ref -d refs/heads/master >actual && + test_cmp expect actual + + git show-ref -d --verify refs/heads/master >actual && + test_cmp expect actual + + >expect && + + test_must_fail git show-ref -d --verify master >actual && + test_cmp expect actual && + + test_must_fail git show-ref -d --verify heads/master >actual && + test_cmp expect actual + +' + +test_expect_success 'show-ref --heads, --tags, --head, pattern' ' + for branch in B master side + do + echo $(git rev-parse refs/heads/$branch) refs/heads/$branch + done >expect.heads && + git show-ref --heads >actual && + test_cmp expect.heads actual && + + for tag in A B C + do + echo $(git rev-parse refs/tags/$tag) refs/tags/$tag + done >expect.tags && + git show-ref --tags >actual && + test_cmp expect.tags actual && + + cat expect.heads expect.tags >expect && + git show-ref --heads --tags >actual && + test_cmp expect actual && + + { + echo $(git rev-parse HEAD) HEAD && + cat expect.heads expect.tags + } >expect && + git show-ref --heads --tags --head >actual && + test_cmp expect actual && + + { + echo $(git rev-parse HEAD) HEAD && + echo $(git rev-parse refs/heads/B) refs/heads/B + echo $(git rev-parse refs/tags/B) refs/tags/B + } >expect && + git show-ref --head B >actual && + test_cmp expect actual && + + { + echo $(git rev-parse HEAD) HEAD && + echo $(git rev-parse refs/heads/B) refs/heads/B + echo $(git rev-parse refs/tags/B) refs/tags/B + echo $(git rev-parse refs/tags/B^0) "refs/tags/B^{}" + } >expect && + git show-ref --head -d B >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh index 252fc82837..236b13a3ab 100755 --- a/t/t1410-reflog.sh +++ b/t/t1410-reflog.sh @@ -100,8 +100,7 @@ test_expect_success setup ' check_fsck && - loglen=$(wc -l <.git/logs/refs/heads/master) && - test $loglen = 4 + test_line_count = 4 .git/logs/refs/heads/master ' test_expect_success rewind ' @@ -117,8 +116,7 @@ test_expect_success rewind ' check_have A B C D E F G H I J K L && - loglen=$(wc -l <.git/logs/refs/heads/master) && - test $loglen = 5 + test_line_count = 5 .git/logs/refs/heads/master ' test_expect_success 'corrupt and check' ' @@ -136,8 +134,7 @@ test_expect_success 'reflog expire --dry-run should not touch reflog' ' --stale-fix \ --all && - loglen=$(wc -l <.git/logs/refs/heads/master) && - test $loglen = 5 && + test_line_count = 5 .git/logs/refs/heads/master && check_fsck "missing blob $F" ' @@ -150,8 +147,7 @@ test_expect_success 'reflog expire' ' --stale-fix \ --all && - loglen=$(wc -l <.git/logs/refs/heads/master) && - test $loglen = 2 && + test_line_count = 2 .git/logs/refs/heads/master && check_fsck "dangling commit $K" ' @@ -217,9 +213,7 @@ test_expect_success 'delete' ' test_expect_success 'rewind2' ' test_tick && git reset --hard HEAD~2 && - loglen=$(wc -l <.git/logs/refs/heads/master) && - test $loglen = 4 - + test_line_count = 4 .git/logs/refs/heads/master ' test_expect_success '--expire=never' ' @@ -228,9 +222,7 @@ test_expect_success '--expire=never' ' --expire=never \ --expire-unreachable=never \ --all && - loglen=$(wc -l <.git/logs/refs/heads/master) && - test $loglen = 4 - + test_line_count = 4 .git/logs/refs/heads/master ' test_expect_success 'gc.reflogexpire=never' ' @@ -238,8 +230,7 @@ test_expect_success 'gc.reflogexpire=never' ' git config gc.reflogexpire never && git config gc.reflogexpireunreachable never && git reflog expire --verbose --all && - loglen=$(wc -l <.git/logs/refs/heads/master) && - test $loglen = 4 + test_line_count = 4 .git/logs/refs/heads/master ' test_expect_success 'gc.reflogexpire=false' ' @@ -247,8 +238,7 @@ test_expect_success 'gc.reflogexpire=false' ' git config gc.reflogexpire false && git config gc.reflogexpireunreachable false && git reflog expire --verbose --all && - loglen=$(wc -l <.git/logs/refs/heads/master) && - test $loglen = 4 && + test_line_count = 4 .git/logs/refs/heads/master && git config --unset gc.reflogexpire && git config --unset gc.reflogexpireunreachable diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh index caa687b5b4..6f47c0dd0e 100755 --- a/t/t1411-reflog-show.sh +++ b/t/t1411-reflog-show.sh @@ -65,20 +65,73 @@ test_expect_success 'using @{now} syntax shows reflog date (oneline)' ' ' cat >expect <<'EOF' -Reflog: HEAD@{1112911993 -0700} (C O Mitter <committer@example.com>) +HEAD@{Thu Apr 7 15:13:13 2005 -0700} +EOF +test_expect_success 'using @{now} syntax shows reflog date (format=%gd)' ' + git log -g -1 --format=%gd HEAD@{now} >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>) Reflog message: commit (initial): one EOF test_expect_success 'using --date= shows reflog date (multiline)' ' - git log -g -1 --date=raw >tmp && + git log -g -1 --date=default >tmp && grep ^Reflog <tmp >actual && test_cmp expect actual ' cat >expect <<'EOF' -e46513e HEAD@{1112911993 -0700}: commit (initial): one +e46513e HEAD@{Thu Apr 7 15:13:13 2005 -0700}: commit (initial): one EOF test_expect_success 'using --date= shows reflog date (oneline)' ' - git log -g -1 --oneline --date=raw >actual && + git log -g -1 --oneline --date=default >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +HEAD@{1112911993 -0700} +EOF +test_expect_success 'using --date= shows reflog date (format=%gd)' ' + git log -g -1 --format=%gd --date=raw >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +Reflog: HEAD@{0} (C O Mitter <committer@example.com>) +Reflog message: commit (initial): one +EOF +test_expect_success 'log.date does not invoke "--date" magic (multiline)' ' + test_config log.date raw && + git log -g -1 >tmp && + grep ^Reflog <tmp >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +e46513e HEAD@{0}: commit (initial): one +EOF +test_expect_success 'log.date does not invoke "--date" magic (oneline)' ' + test_config log.date raw && + git log -g -1 --oneline >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +HEAD@{0} +EOF +test_expect_success 'log.date does not invoke "--date" magic (format=%gd)' ' + test_config log.date raw && + git log -g -1 --format=%gd >actual && + test_cmp expect actual +' + +cat >expect <<'EOF' +HEAD@{0} +EOF +test_expect_success '--date magic does not override explicit @{0} syntax' ' + git log -g -1 --format=%gd --date=raw HEAD@{0} >actual && test_cmp expect actual ' @@ -91,4 +144,26 @@ test_expect_success 'empty reflog file' ' test_cmp expect actual ' +# This guards against the alternative of showing the diffs vs. the +# reflog ancestor. The reflog used is designed to list the commits +# more than once, so as to exercise the corresponding logic. +test_expect_success 'git log -g -p shows diffs vs. parents' ' + test_commit two && + git branch flipflop && + git update-ref refs/heads/flipflop -m flip1 HEAD^ && + git update-ref refs/heads/flipflop -m flop1 HEAD && + git update-ref refs/heads/flipflop -m flip2 HEAD^ && + git log -g -p flipflop >reflog && + grep -v ^Reflog reflog >actual && + git log -1 -p HEAD^ >log.one && + git log -1 -p HEAD >log.two && + ( + cat log.one; echo + cat log.two; echo + cat log.one; echo + cat log.two + ) >expect && + test_cmp expect actual +' + test_done diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 5e36cc71b4..d730734fde 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -27,12 +27,8 @@ test_expect_success 'loose objects borrowed from alternate are not missing' ' git init && echo ../../../.git/objects >.git/objects/info/alternates && test_commit C fileC one && - git fsck >../out 2>&1 + git fsck --no-dangling >../actual 2>&1 ) && - { - grep -v dangling out >actual || - : - } && test_cmp empty actual ' @@ -181,9 +177,7 @@ test_expect_success 'tag pointing to something else than its type' ' test_when_finished "remove_object $tag" && echo $tag >.git/refs/tags/wrong && test_when_finished "git update-ref -d refs/tags/wrong" && - test_must_fail git fsck --tags 2>out && - cat out && - grep "error in tag.*broken links" out + test_must_fail git fsck --tags ' test_expect_success 'cleaned up' ' @@ -243,4 +237,35 @@ test_expect_success 'fsck notices submodule entry pointing to null sha1' ' ) ' +test_expect_success 'fsck notices "." and ".." in trees' ' + ( + git init dots && + cd dots && + blob=$(echo foo | git hash-object -w --stdin) && + tab=$(printf "\\t") && + git mktree <<-EOF && + 100644 blob $blob$tab. + 100644 blob $blob$tab.. + EOF + git fsck 2>out && + cat out && + grep "warning.*\\." out + ) +' + +test_expect_success 'fsck notices ".git" in trees' ' + ( + git init dotgit && + cd dotgit && + blob=$(echo foo | git hash-object -w --stdin) && + tab=$(printf "\\t") && + git mktree <<-EOF && + 100644 blob $blob$tab.git + EOF + git fsck 2>out && + cat out && + grep "warning.*\\.git" out + ) +' + test_done diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh index e661147c57..8f36aa9fc4 100755 --- a/t/t1501-worktree.sh +++ b/t/t1501-worktree.sh @@ -68,7 +68,7 @@ test_expect_success 'inside work tree' ' ) ' -test_expect_failure 'empty prefix is actually written out' ' +test_expect_success 'empty prefix is actually written out' ' echo >expected && ( cd work && diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh index 1efd7f76dd..13c88c9aae 100755 --- a/t/t1502-rev-parse-parseopt.sh +++ b/t/t1502-rev-parse-parseopt.sh @@ -41,7 +41,7 @@ EOF test_expect_success 'test --parseopt help output' ' test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec && - test_cmp expect output + test_i18ncmp expect output ' cat > expect <<EOF diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh index cce87a5ab5..3d51615e42 100755 --- a/t/t1504-ceiling-dirs.sh +++ b/t/t1504-ceiling-dirs.sh @@ -44,6 +44,10 @@ test_prefix ceil_at_sub "" GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/" test_prefix ceil_at_sub_slash "" +if test_have_prereq SYMLINKS +then + ln -s sub top +fi mkdir -p sub/dir || exit 1 cd sub/dir || exit 1 @@ -68,6 +72,19 @@ test_fail subdir_ceil_at_sub GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/" test_fail subdir_ceil_at_sub_slash +if test_have_prereq SYMLINKS +then + GIT_CEILING_DIRECTORIES="$TRASH_ROOT/top" + test_fail subdir_ceil_at_top + GIT_CEILING_DIRECTORIES="$TRASH_ROOT/top/" + test_fail subdir_ceil_at_top_slash + + GIT_CEILING_DIRECTORIES=":$TRASH_ROOT/top" + test_prefix subdir_ceil_at_top_no_resolve "sub/dir/" + GIT_CEILING_DIRECTORIES=":$TRASH_ROOT/top/" + test_prefix subdir_ceil_at_top_slash_no_resolve "sub/dir/" +fi + GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/dir" test_prefix subdir_ceil_at_subdir "sub/dir/" diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh index d709ecf8df..4969edb314 100755 --- a/t/t1505-rev-parse-last.sh +++ b/t/t1505-rev-parse-last.sh @@ -32,32 +32,24 @@ test_expect_success 'setup' ' # # and 'side' should be the last branch -test_rev_equivalent () { - - git rev-parse "$1" > expect && - git rev-parse "$2" > output && - test_cmp expect output - -} - test_expect_success '@{-1} works' ' - test_rev_equivalent side @{-1} + test_cmp_rev side @{-1} ' test_expect_success '@{-1}~2 works' ' - test_rev_equivalent side~2 @{-1}~2 + test_cmp_rev side~2 @{-1}~2 ' test_expect_success '@{-1}^2 works' ' - test_rev_equivalent side^2 @{-1}^2 + test_cmp_rev side^2 @{-1}^2 ' test_expect_success '@{-1}@{1} works' ' - test_rev_equivalent side@{1} @{-1}@{1} + test_cmp_rev side@{1} @{-1}@{1} ' test_expect_success '@{-2} works' ' - test_rev_equivalent master @{-2} + test_cmp_rev master @{-2} ' test_expect_success '@{-3} fails' ' diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh index 0843a1c13b..f950c10128 100755 --- a/t/t1506-rev-parse-diagnosis.sh +++ b/t/t1506-rev-parse-diagnosis.sh @@ -171,4 +171,29 @@ test_expect_success 'relative path when startup_info is NULL' ' grep "BUG: startup_info struct is not initialized." error ' +test_expect_success '<commit>:file correctly diagnosed after a pathname' ' + test_must_fail git rev-parse file.txt HEAD:file.txt 1>actual 2>error && + test_i18ngrep ! "exists on disk" error && + test_i18ngrep "no such path in the working tree" error && + cat >expect <<-\EOF && + file.txt + HEAD:file.txt + EOF + test_cmp expect actual +' + +test_expect_success 'dotdot is not an empty set' ' + ( H=$(git rev-parse HEAD) && echo $H && echo ^$H ) >expect && + + git rev-parse HEAD.. >actual && + test_cmp expect actual && + + git rev-parse ..HEAD >actual && + test_cmp expect actual && + + echo .. >expect && + git rev-parse .. >actual && + test_cmp expect actual +' + test_done diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh index a4555510c3..2a19e797eb 100755 --- a/t/t1507-rev-parse-upstream.sh +++ b/t/t1507-rev-parse-upstream.sh @@ -15,10 +15,18 @@ test_expect_success 'setup' ' test_commit 3 && (cd clone && test_commit 4 && - git branch --track my-side origin/side) - + git branch --track my-side origin/side && + git branch --track local-master master && + git remote add -t master master-only .. && + git fetch master-only && + git branch bad-upstream && + git config branch.bad-upstream.remote master-only && + git config branch.bad-upstream.merge refs/heads/side + ) ' +sq="'" + full_name () { (cd clone && git rev-parse --symbolic-full-name "$@") @@ -29,6 +37,11 @@ commit_subject () { git show -s --pretty=format:%s "$@") } +error_message () { + (cd clone && + test_must_fail git rev-parse --verify "$@") +} + test_expect_success '@{upstream} resolves to correct full name' ' test refs/remotes/origin/master = "$(full_name @{upstream})" ' @@ -41,6 +54,10 @@ test_expect_success 'my-side@{upstream} resolves to correct full name' ' test refs/remotes/origin/side = "$(full_name my-side@{u})" ' +test_expect_success 'refs/heads/my-side@{upstream} does not resolve to my-side{upstream}' ' + test_must_fail full_name refs/heads/my-side@{upstream} +' + test_expect_success 'my-side@{u} resolves to correct commit' ' git checkout side && test_commit 5 && @@ -78,7 +95,6 @@ test_expect_success 'checkout -b new my-side@{u} forks from the same' ' test_expect_success 'merge my-side@{u} records the correct name' ' ( - sq="'\''" && cd clone || exit git checkout master || exit git branch -D new ;# can fail but is ok @@ -107,6 +123,64 @@ test_expect_success 'checkout other@{u}' ' test_cmp expect actual ' +test_expect_success 'branch@{u} works when tracking a local branch' ' + test refs/heads/master = "$(full_name local-master@{u})" +' + +test_expect_success 'branch@{u} error message when no upstream' ' + cat >expect <<-EOF && + fatal: No upstream configured for branch ${sq}non-tracking${sq} + EOF + error_message non-tracking@{u} 2>actual && + test_i18ncmp expect actual +' + +test_expect_success '@{u} error message when no upstream' ' + cat >expect <<-EOF && + fatal: No upstream configured for branch ${sq}master${sq} + EOF + test_must_fail git rev-parse --verify @{u} 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'branch@{u} error message with misspelt branch' ' + cat >expect <<-EOF && + fatal: No such branch: ${sq}no-such-branch${sq} + EOF + error_message no-such-branch@{u} 2>actual && + test_i18ncmp expect actual +' + +test_expect_success '@{u} error message when not on a branch' ' + cat >expect <<-EOF && + fatal: HEAD does not point to a branch + EOF + git checkout HEAD^0 && + test_must_fail git rev-parse --verify @{u} 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'branch@{u} error message if upstream branch not fetched' ' + cat >expect <<-EOF && + fatal: Upstream branch ${sq}refs/heads/side${sq} not stored as a remote-tracking branch + EOF + error_message bad-upstream@{u} 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'pull works when tracking a local branch' ' +( + cd clone && + git checkout local-master && + git pull +) +' + +# makes sense if the previous one succeeded +test_expect_success '@{u} works when tracking a local branch' ' + test refs/heads/master = "$(full_name @{u})" +' + cat >expect <<EOF commit 8f489d01d0cc65c3b0f09504ec50b5ed02a70bd5 Reflog: master@{0} (C O Mitter <committer@example.com>) diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh index d5d6244178..ceb844985f 100755 --- a/t/t1508-at-combinations.sh +++ b/t/t1508-at-combinations.sh @@ -4,17 +4,24 @@ test_description='test various @{X} syntax combinations together' . ./test-lib.sh check() { -test_expect_${3:-success} "$1 = $2" " - echo '$2' >expect && - git log -1 --format=%s '$1' >actual && - test_cmp expect actual -" + test_expect_${4:-success} "$1 = $3" " + echo '$3' >expect && + if test '$2' = 'commit' + then + git log -1 --format=%s '$1' >actual + else + git rev-parse --symbolic-full-name '$1' >actual + fi && + test_cmp expect actual + " } + nonsense() { -test_expect_${2:-success} "$1 is nonsensical" " - test_must_fail git log -1 '$1' -" + test_expect_${2:-success} "$1 is nonsensical" " + test_must_fail git rev-parse --verify '$1' + " } + fail() { "$@" failure } @@ -25,27 +32,54 @@ test_expect_success 'setup' ' git checkout -b upstream-branch && test_commit upstream-one && test_commit upstream-two && + git checkout -b @/at-test && + git checkout -b @@/at-test && + git checkout -b @at-test && git checkout -b old-branch && test_commit old-one && test_commit old-two && git checkout -b new-branch && test_commit new-one && test_commit new-two && - git config branch.old-branch.remote . && - git config branch.old-branch.merge refs/heads/master && - git config branch.new-branch.remote . && - git config branch.new-branch.merge refs/heads/upstream-branch + git branch -u master old-branch && + git branch -u upstream-branch new-branch ' -check HEAD new-two -check "@{1}" new-one -check "@{-1}" old-two -check "@{-1}@{1}" old-one -check "@{u}" upstream-two -check "@{u}@{1}" upstream-one -check "@{-1}@{u}" master-two -check "@{-1}@{u}@{1}" master-one +check HEAD ref refs/heads/new-branch +check "@{1}" commit new-one +check "HEAD@{1}" commit new-one +check "@{now}" commit new-two +check "HEAD@{now}" commit new-two +check "@{-1}" ref refs/heads/old-branch +check "@{-1}@{0}" commit old-two +check "@{-1}@{1}" commit old-one +check "@{u}" ref refs/heads/upstream-branch +check "HEAD@{u}" ref refs/heads/upstream-branch +check "@{u}@{1}" commit upstream-one +check "@{-1}@{u}" ref refs/heads/master +check "@{-1}@{u}@{1}" commit master-one +check "@" commit new-two +check "@@{u}" ref refs/heads/upstream-branch +check "@@/at-test" ref refs/heads/@@/at-test +check "@/at-test" ref refs/heads/@/at-test +check "@at-test" ref refs/heads/@at-test nonsense "@{u}@{-1}" +nonsense "@{0}@{0}" nonsense "@{1}@{u}" +nonsense "HEAD@{-1}" +nonsense "@{-1}@{-1}" + +# @{N} versus HEAD@{N} + +check "HEAD@{3}" commit old-two +nonsense "@{3}" + +test_expect_success 'switch to old-branch' ' + git checkout old-branch +' + +check HEAD ref refs/heads/old-branch +check "HEAD@{1}" commit new-two +check "@{1}" commit old-one test_done diff --git a/t/t1509/prepare-chroot.sh b/t/t1509/prepare-chroot.sh index c5334a8fa4..62691172e3 100755 --- a/t/t1509/prepare-chroot.sh +++ b/t/t1509/prepare-chroot.sh @@ -14,7 +14,7 @@ xmkdir() { R="$1" -[ -n "$R" ] || die "Usage: prepare-chroot.sh <root>" +[ -n "$R" ] || die "usage: prepare-chroot.sh <root>" [ -x git ] || die "This script needs to be executed at git source code's top directory" [ -x /bin/busybox ] || die "You need busybox" diff --git a/t/t1510-repo-setup.sh b/t/t1510-repo-setup.sh index 80aedfca8c..cf2ee7885a 100755 --- a/t/t1510-repo-setup.sh +++ b/t/t1510-repo-setup.sh @@ -517,6 +517,25 @@ test_expect_success '#16c: bare .git has no worktree' ' "$here/16c/.git" "(null)" "$here/16c/sub" "(null)" ' +test_expect_success '#16d: bareness preserved across alias' ' + setup_repo 16d unset "" unset && + ( + cd 16d/.git && + test_must_fail git status && + git config alias.st status && + test_must_fail git st + ) +' + +test_expect_success '#16e: bareness preserved by --bare' ' + setup_repo 16e unset "" unset && + ( + cd 16e/.git && + test_must_fail git status && + test_must_fail git --bare status + ) +' + test_expect_success '#17: GIT_WORK_TREE without explicit GIT_DIR is accepted (bare case)' ' # Just like #16. setup_repo 17a unset "" true && diff --git a/t/t1511-rev-parse-caret.sh b/t/t1511-rev-parse-caret.sh index eaefc777bd..15973f2094 100755 --- a/t/t1511-rev-parse-caret.sh +++ b/t/t1511-rev-parse-caret.sh @@ -54,6 +54,13 @@ test_expect_success 'ref^{tree}' ' test_must_fail git rev-parse blob-tag^{tree} ' +test_expect_success 'ref^{tag}' ' + test_must_fail git rev-parse HEAD^{tag} && + git rev-parse commit-tag >expected && + git rev-parse commit-tag^{tag} >actual && + test_cmp expected actual +' + test_expect_success 'ref^{/.}' ' git rev-parse master >expected && git rev-parse master^{/.} >actual && diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh new file mode 100755 index 0000000000..4a155c8d09 --- /dev/null +++ b/t/t1512-rev-parse-disambiguation.sh @@ -0,0 +1,294 @@ +#!/bin/sh + +test_description='object name disambiguation + +Create blobs, trees, commits and a tag that all share the same +prefix, and make sure "git rev-parse" can take advantage of +type information to disambiguate short object names that are +not necessarily unique. + +The final history used in the test has five commits, with the bottom +one tagged as v1.0.0. They all have one regular file each. + + +-------------------------------------------+ + | | + | .-------b3wettvi---- ad2uee | + | / / | + | a2onsxbvj---czy8f73t--ioiley5o | + | | + +-------------------------------------------+ + +' + +. ./test-lib.sh + +test_expect_success 'blob and tree' ' + test_tick && + ( + for i in 0 1 2 3 4 5 6 7 8 9 + do + echo $i + done + echo + echo b1rwzyc3 + ) >a0blgqsjc && + + # create one blob 0000000000b36 + git add a0blgqsjc && + + # create one tree 0000000000cdc + git write-tree +' + +test_expect_success 'warn ambiguity when no candidate matches type hint' ' + test_must_fail git rev-parse --verify 000000000^{commit} 2>actual && + grep "short SHA1 000000000 is ambiguous" actual +' + +test_expect_success 'disambiguate tree-ish' ' + # feed tree-ish in an unambiguous way + git rev-parse --verify 0000000000cdc:a0blgqsjc && + + # ambiguous at the object name level, but there is only one + # such tree-ish (the other is a blob) + git rev-parse --verify 000000000:a0blgqsjc +' + +test_expect_success 'disambiguate blob' ' + sed -e "s/|$//" >patch <<-EOF && + diff --git a/frotz b/frotz + index 000000000..ffffff 100644 + --- a/frotz + +++ b/frotz + @@ -10,3 +10,4 @@ + 9 + | + b1rwzyc3 + +irwry + EOF + ( + GIT_INDEX_FILE=frotz && + export GIT_INDEX_FILE && + git apply --build-fake-ancestor frotz patch && + git cat-file blob :frotz >actual + ) && + test_cmp a0blgqsjc actual +' + +test_expect_success 'disambiguate tree' ' + commit=$(echo "d7xm" | git commit-tree 000000000) && + # this commit is fffff2e and not ambiguous with the 00000* objects + test $(git rev-parse $commit^{tree}) = $(git rev-parse 0000000000cdc) +' + +test_expect_success 'first commit' ' + # create one commit 0000000000e4f + git commit -m a2onsxbvj +' + +test_expect_success 'disambiguate commit-ish' ' + # feed commit-ish in an unambiguous way + git rev-parse --verify 0000000000e4f^{commit} && + + # ambiguous at the object name level, but there is only one + # such commit (the others are tree and blob) + git rev-parse --verify 000000000^{commit} && + + # likewise + git rev-parse --verify 000000000^0 +' + +test_expect_success 'disambiguate commit' ' + commit=$(echo "hoaxj" | git commit-tree 0000000000cdc -p 000000000) && + # this commit is ffffffd8 and not ambiguous with the 00000* objects + test $(git rev-parse $commit^) = $(git rev-parse 0000000000e4f) +' + +test_expect_success 'log name1..name2 takes only commit-ishes on both ends' ' + # These are underspecified from the prefix-length point of view + # to disambiguate the commit with other objects, but there is only + # one commit that has 00000* prefix at this point. + git log 000000000..000000000 && + git log ..000000000 && + git log 000000000.. && + git log 000000000...000000000 && + git log ...000000000 && + git log 000000000... +' + +test_expect_success 'rev-parse name1..name2 takes only commit-ishes on both ends' ' + # Likewise. + git rev-parse 000000000..000000000 && + git rev-parse ..000000000 && + git rev-parse 000000000.. +' + +test_expect_success 'git log takes only commit-ish' ' + # Likewise. + git log 000000000 +' + +test_expect_success 'git reset takes only commit-ish' ' + # Likewise. + git reset 000000000 +' + +test_expect_success 'first tag' ' + # create one tag 0000000000f8f + git tag -a -m j7cp83um v1.0.0 +' + +test_expect_failure 'two semi-ambiguous commit-ish' ' + # At this point, we have a tag 0000000000f8f that points + # at a commit 0000000000e4f, and a tree and a blob that + # share 0000000000 prefix with these tag and commit. + # + # Once the parser becomes ultra-smart, it could notice that + # 0000000000 before ^{commit} name many different objects, but + # that only two (HEAD and v1.0.0 tag) can be peeled to commit, + # and that peeling them down to commit yield the same commit + # without ambiguity. + git rev-parse --verify 0000000000^{commit} && + + # likewise + git log 0000000000..0000000000 && + git log ..0000000000 && + git log 0000000000.. && + git log 0000000000...0000000000 && + git log ...0000000000 && + git log 0000000000... +' + +test_expect_failure 'three semi-ambiguous tree-ish' ' + # Likewise for tree-ish. HEAD, v1.0.0 and HEAD^{tree} share + # the prefix but peeling them to tree yields the same thing + git rev-parse --verify 0000000000^{tree} +' + +test_expect_success 'parse describe name' ' + # feed an unambiguous describe name + git rev-parse --verify v1.0.0-0-g0000000000e4f && + + # ambiguous at the object name level, but there is only one + # such commit (others are blob, tree and tag) + git rev-parse --verify v1.0.0-0-g000000000 +' + +test_expect_success 'more history' ' + # commit 0000000000043 + git mv a0blgqsjc d12cr3h8t && + echo h62xsjeu >>d12cr3h8t && + git add d12cr3h8t && + + test_tick && + git commit -m czy8f73t && + + # commit 00000000008ec + git mv d12cr3h8t j000jmpzn && + echo j08bekfvt >>j000jmpzn && + git add j000jmpzn && + + test_tick && + git commit -m ioiley5o && + + # commit 0000000005b0 + git checkout v1.0.0^0 && + git mv a0blgqsjc f5518nwu && + + for i in h62xsjeu j08bekfvt kg7xflhm + do + echo $i + done >>f5518nwu && + git add f5518nwu && + + test_tick && + git commit -m b3wettvi && + side=$(git rev-parse HEAD) && + + # commit 000000000066 + git checkout master && + + # If you use recursive, merge will fail and you will need to + # clean up a0blgqsjc as well. If you use resolve, merge will + # succeed. + test_might_fail git merge --no-commit -s recursive $side && + git rm -f f5518nwu j000jmpzn && + + test_might_fail git rm -f a0blgqsjc && + ( + git cat-file blob $side:f5518nwu + echo j3l0i9s6 + ) >ab2gs879 && + git add ab2gs879 && + + test_tick && + git commit -m ad2uee + +' + +test_expect_failure 'parse describe name taking advantage of generation' ' + # ambiguous at the object name level, but there is only one + # such commit at generation 0 + git rev-parse --verify v1.0.0-0-g000000000 && + + # likewise for generation 2 and 4 + git rev-parse --verify v1.0.0-2-g000000000 && + git rev-parse --verify v1.0.0-4-g000000000 +' + +# Note: because rev-parse does not even try to disambiguate based on +# the generation number, this test currently succeeds for a wrong +# reason. When it learns to use the generation number, the previous +# test should succeed, and also this test should fail because the +# describe name used in the test with generation number can name two +# commits. Make sure that such a future enhancement does not randomly +# pick one. +test_expect_success 'parse describe name not ignoring ambiguity' ' + # ambiguous at the object name level, and there are two such + # commits at generation 1 + test_must_fail git rev-parse --verify v1.0.0-1-g000000000 +' + +test_expect_success 'ambiguous commit-ish' ' + # Now there are many commits that begin with the + # common prefix, none of these should pick one at + # random. They all should result in ambiguity errors. + test_must_fail git rev-parse --verify 00000000^{commit} && + + # likewise + test_must_fail git log 000000000..000000000 && + test_must_fail git log ..000000000 && + test_must_fail git log 000000000.. && + test_must_fail git log 000000000...000000000 && + test_must_fail git log ...000000000 && + test_must_fail git log 000000000... +' + +test_expect_success 'rev-parse --disambiguate' ' + # The test creates 16 objects that share the prefix and two + # commits created by commit-tree in earlier tests share a + # different prefix. + git rev-parse --disambiguate=000000000 >actual && + test $(wc -l <actual) = 16 && + test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000 +' + +test_expect_success 'ambiguous 40-hex ref' ' + TREE=$(git mktree </dev/null) && + REF=`git rev-parse HEAD` && + VAL=$(git commit-tree $TREE </dev/null) && + git update-ref refs/heads/$REF $VAL && + test `git rev-parse $REF 2>err` = $REF && + grep "refname.*${REF}.*ambiguous" err +' + +test_expect_success 'ambiguous short sha1 ref' ' + TREE=$(git mktree </dev/null) && + REF=`git rev-parse --short HEAD` && + VAL=$(git commit-tree $TREE </dev/null) && + git update-ref refs/heads/$REF $VAL && + test `git rev-parse $REF 2>err` = $VAL && + grep "refname.*${REF}.*ambiguous" err +' + +test_done diff --git a/t/t1513-rev-parse-prefix.sh b/t/t1513-rev-parse-prefix.sh new file mode 100755 index 0000000000..87ec3ae714 --- /dev/null +++ b/t/t1513-rev-parse-prefix.sh @@ -0,0 +1,96 @@ +#!/bin/sh + +test_description='Tests for rev-parse --prefix' + +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir -p sub1/sub2 && + echo top >top && + echo file1 >sub1/file1 && + echo file2 >sub1/sub2/file2 && + git add top sub1/file1 sub1/sub2/file2 && + git commit -m commit +' + +test_expect_success 'empty prefix -- file' ' + git rev-parse --prefix "" -- top sub1/file1 >actual && + cat <<-\EOF >expected && + -- + top + sub1/file1 + EOF + test_cmp expected actual +' + +test_expect_success 'valid prefix -- file' ' + git rev-parse --prefix sub1/ -- file1 sub2/file2 >actual && + cat <<-\EOF >expected && + -- + sub1/file1 + sub1/sub2/file2 + EOF + test_cmp expected actual +' + +test_expect_success 'valid prefix -- ../file' ' + git rev-parse --prefix sub1/ -- ../top sub2/file2 >actual && + cat <<-\EOF >expected && + -- + sub1/../top + sub1/sub2/file2 + EOF + test_cmp expected actual +' + +test_expect_success 'empty prefix HEAD:./path' ' + git rev-parse --prefix "" HEAD:./top >actual && + git rev-parse HEAD:top >expected && + test_cmp expected actual +' + +test_expect_success 'valid prefix HEAD:./path' ' + git rev-parse --prefix sub1/ HEAD:./file1 >actual && + git rev-parse HEAD:sub1/file1 >expected && + test_cmp expected actual +' + +test_expect_success 'valid prefix HEAD:../path' ' + git rev-parse --prefix sub1/ HEAD:../top >actual && + git rev-parse HEAD:top >expected && + test_cmp expected actual +' + +test_expect_success 'prefix ignored with HEAD:top' ' + git rev-parse --prefix sub1/ HEAD:top >actual && + git rev-parse HEAD:top >expected && + test_cmp expected actual +' + +test_expect_success 'disambiguate path with valid prefix' ' + git rev-parse --prefix sub1/ file1 >actual && + cat <<-\EOF >expected && + sub1/file1 + EOF + test_cmp expected actual +' + +test_expect_success 'file and refs with prefix' ' + git rev-parse --prefix sub1/ master file1 >actual && + cat <<-EOF >expected && + $(git rev-parse master) + sub1/file1 + EOF + test_cmp expected actual +' + +test_expect_success 'two-levels deep' ' + git rev-parse --prefix sub1/sub2/ -- file2 >actual && + cat <<-\EOF >expected && + -- + sub1/sub2/file2 + EOF + test_cmp expected actual +' + +test_done diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh index 98aa73e823..1fc8e634b7 100755 --- a/t/t2001-checkout-cache-clash.sh +++ b/t/t2001-checkout-cache-clash.sh @@ -59,10 +59,9 @@ test_expect_success \ 'git read-tree -m $tree1 && git checkout-index -f -a' test_debug 'show_files $tree1' -test_expect_success SYMLINKS \ - 'git update-index --add a symlink.' \ - 'ln -s path0 path1 && - git update-index --add path1' +test_expect_success \ + 'add a symlink' \ + 'test_ln_s_add path0 path1' test_expect_success \ 'writing tree out with git write-tree' \ 'tree3=$(git write-tree)' diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh index 02a4fc5d36..ff163cf675 100755 --- a/t/t2003-checkout-cache-mkdir.sh +++ b/t/t2003-checkout-cache-mkdir.sh @@ -12,85 +12,108 @@ 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 'setup' ' + mkdir path1 && + echo frotz >path0 && + echo rezrov >path1/file1 && + git update-index --add path0 path1/file1 +' + +test_expect_success SYMLINKS '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 SYMLINKS \ - '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=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- 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 +' -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 SYMLINKS '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 +' -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' +test_expect_success SYMLINKS '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 #1 -test_expect_success SYMLINKS \ - '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' +test_expect_success SYMLINKS '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 +' -# Linus fix #2 -test_expect_success SYMLINKS \ - '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' +test_expect_success 'apply filter from working tree .gitattributes with --prefix' ' + rm -fr path0 path1 path2 tmp* && + mkdir path1 && + mkdir tmp && + git config filter.replace-all.smudge "sed -e s/./,/g" && + git config filter.replace-all.clean cat && + git config filter.replace-all.required true && + echo "file1 filter=replace-all" >path1/.gitattributes && + git checkout-index --prefix=tmp/ -f -a && + echo frotz >expected && + test_cmp expected tmp/path0 && + echo ,,,,,, >expected && + test_cmp expected tmp/path1/file1 +' -# Linus fix #3 -test_expect_success SYMLINKS \ - '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_expect_success 'apply CRLF filter from working tree .gitattributes with --prefix' ' + rm -fr path0 path1 path2 tmp* && + mkdir path1 && + mkdir tmp && + echo "file1 eol=crlf" >path1/.gitattributes && + git checkout-index --prefix=tmp/ -f -a && + echo rezrovQ >expected && + tr \\015 Q <tmp/path1/file1 >actual && + test_cmp expected actual +' test_done diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh index 36cca14d95..f171a5578b 100755 --- a/t/t2004-checkout-cache-temp.sh +++ b/t/t2004-checkout-cache-temp.sh @@ -40,7 +40,7 @@ test_expect_success \ rm -f path* .merge_* out .git/index && git read-tree $t1 && git checkout-index --temp -- path1 >out && -test $(wc -l <out) = 1 && +test_line_count = 1 out && test $(cut "-d " -f2 out) = path1 && p=$(cut "-d " -f1 out) && test -f $p && @@ -51,7 +51,7 @@ test_expect_success \ rm -f path* .merge_* out .git/index && git read-tree $t1 && git checkout-index -a --temp >out && -test $(wc -l <out) = 5 && +test_line_count = 5 out && for f in path0 path1 path3 path4 asubdir/path5 do test $(grep $f out | cut "-d " -f2) = $f && @@ -69,7 +69,7 @@ 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_line_count = 1 out && test $(cut "-d " -f2 out) = path1 && p=$(cut "-d " -f1 out) && test -f $p && @@ -79,7 +79,7 @@ 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 && +test_line_count = 3 out && for f in path1 path2 path4 do test $(grep $f out | cut "-d " -f2) = $f && @@ -92,13 +92,13 @@ 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_line_count = 0 out' 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_line_count = 1 out && test $(cut "-d " -f2 out) = path1 && cut "-d " -f1 out | (read s1 s2 s3 && test -f $s1 && @@ -112,7 +112,7 @@ 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_line_count = 1 out && test $(cut "-d " -f2 out) = path2 && cut "-d " -f1 out | (read s1 s2 s3 && test $s1 = . && @@ -125,7 +125,7 @@ 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_line_count = 5 out' test_expect_success \ '-- path0: no entry' ' @@ -185,7 +185,7 @@ test_expect_success \ 'checkout --temp within subdir' ' (cd asubdir && git checkout-index -a --stage=all >out && - test $(wc -l <out) = 1 && + test_line_count = 1 out && test $(grep path5 out | cut "-d " -f2) = path5 && grep path5 out | cut "-d " -f1 | (read s1 s2 s3 && test -f ../$s1 && @@ -194,16 +194,15 @@ test_expect_success \ test $(cat ../$s1) = tree1asubdir/path5) )' -test_expect_success SYMLINKS \ +test_expect_success \ 'checkout --temp symlink' ' rm -f path* .merge_* out .git/index && -ln -s b a && -git update-index --add a && +test_ln_s_add b 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_line_count = 1 out && test $(cut "-d " -f2 out) = a && p=$(cut "-d " -f1 out) && test -f $p && diff --git a/t/t2006-checkout-index-basic.sh b/t/t2006-checkout-index-basic.sh index b8559838b1..57cbdfe9bc 100755 --- a/t/t2006-checkout-index-basic.sh +++ b/t/t2006-checkout-index-basic.sh @@ -7,7 +7,7 @@ test_description='basic checkout-index tests test_expect_success 'checkout-index --gobbledegook' ' test_expect_code 129 git checkout-index --gobbledegook 2>err && - grep "[Uu]sage" err + test_i18ngrep "[Uu]sage" err ' test_expect_success 'checkout-index -h in broken repository' ' @@ -18,7 +18,7 @@ test_expect_success 'checkout-index -h in broken repository' ' >.git/index && test_expect_code 129 git checkout-index -h >usage 2>&1 ) && - grep "[Uu]sage" broken/usage + test_i18ngrep "[Uu]sage" broken/usage ' test_done diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh index e6f59f1914..fc9aad530e 100755 --- a/t/t2007-checkout-symlink.sh +++ b/t/t2007-checkout-symlink.sh @@ -6,7 +6,7 @@ test_description='git checkout to switch between branches with symlink<->dir' . ./test-lib.sh -test_expect_success SYMLINKS setup ' +test_expect_success setup ' mkdir frotz && echo hello >frotz/filfre && @@ -25,25 +25,25 @@ test_expect_success SYMLINKS setup ' git rm --cached frotz/filfre && mv frotz xyzzy && - ln -s xyzzy frotz && - git add xyzzy/filfre frotz && + test_ln_s_add xyzzy frotz && + git add xyzzy/filfre && test_tick && git commit -m "side moves frotz/ to xyzzy/ and adds frotz->xyzzy/" ' -test_expect_success SYMLINKS 'switch from symlink to dir' ' +test_expect_success 'switch from symlink to dir' ' git checkout master ' -test_expect_success SYMLINKS 'Remove temporary directories & switch to master' ' +test_expect_success 'Remove temporary directories & switch to master' ' rm -fr frotz xyzzy nitfol && git checkout -f master ' -test_expect_success SYMLINKS 'switch from dir to symlink' ' +test_expect_success 'switch from dir to symlink' ' git checkout side diff --git a/t/t2008-checkout-subdir.sh b/t/t2008-checkout-subdir.sh index 3e098ab31e..eadb9434ae 100755 --- a/t/t2008-checkout-subdir.sh +++ b/t/t2008-checkout-subdir.sh @@ -58,13 +58,13 @@ test_expect_success 'checkout with simple prefix' ' ' -# This is not expected to work as ls-files was not designed -# to deal with such. Enable it when ls-files is updated. -: test_expect_success 'checkout with complex relative path' ' - - rm file1 && - git checkout HEAD -- ../dir1/../dir1/file1 && test -f ./file1 - +test_expect_success 'checkout with complex relative path' ' + ( + cd dir1 && + rm file1 && + git checkout HEAD -- ../dir1/../dir1/file1 && + test "hello" = "$(cat file1)" + ) ' test_expect_success 'relative path outside tree should fail' \ diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh index 7cc0a3582e..87bdf9c96b 100755 --- a/t/t2010-checkout-ambiguous.sh +++ b/t/t2010-checkout-ambiguous.sh @@ -47,4 +47,10 @@ test_expect_success 'disambiguate checking out from a tree-ish' ' git diff --exit-code --quiet ' +test_expect_success 'accurate error message with more than one ref' ' + test_must_fail git checkout HEAD master -- 2>actual && + grep 2 actual && + test_i18ngrep "one reference expected, 2 given" actual +' + test_done diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh index b44de9dc62..e7ba8c505f 100755 --- a/t/t2012-checkout-last.sh +++ b/t/t2012-checkout-last.sh @@ -116,4 +116,38 @@ test_expect_success 'master...' ' test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)" ' +test_expect_success '"checkout -" works after a rebase A' ' + git checkout master && + git checkout other && + git rebase master && + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master" +' + +test_expect_success '"checkout -" works after a rebase A B' ' + git branch moodle master~1 && + git checkout master && + git checkout other && + git rebase master moodle && + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master" +' + +test_expect_success '"checkout -" works after a rebase -i A' ' + git checkout master && + git checkout other && + git rebase -i master && + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master" +' + +test_expect_success '"checkout -" works after a rebase -i A B' ' + git branch foodle master~1 && + git checkout master && + git checkout other && + git rebase master foodle && + git checkout - && + test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master" +' + test_done diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh index 70edbb33e2..06b18f8bc1 100755 --- a/t/t2013-checkout-submodule.sh +++ b/t/t2013-checkout-submodule.sh @@ -23,7 +23,7 @@ test_expect_success '"reset <submodule>" updates the index' ' git update-index --refresh && git diff-files --quiet && git diff-index --quiet --cached HEAD && - test_must_fail git reset HEAD^ submodule && + git reset HEAD^ submodule && test_must_fail git diff-files --quiet && git reset submodule && git diff-files --quiet diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh index 6352b74e2e..37bdcedcc9 100755 --- a/t/t2015-checkout-unborn.sh +++ b/t/t2015-checkout-unborn.sh @@ -46,4 +46,15 @@ test_expect_success 'checking out another branch from unborn state' ' test_cmp expect actual ' +test_expect_success 'checking out in a newly created repo' ' + test_create_repo empty && + ( + cd empty && + git symbolic-ref HEAD >expect && + test_must_fail git checkout && + git symbolic-ref HEAD >actual && + test_cmp expect actual + ) +' + test_done diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh index 0e3b8582f2..655f278c5f 100755 --- a/t/t2017-checkout-orphan.sh +++ b/t/t2017-checkout-orphan.sh @@ -116,4 +116,10 @@ test_expect_success '--orphan refuses to switch if a merge is needed' ' git reset --hard ' +test_expect_success 'cannot --detach on an unborn branch' ' + git checkout master && + git checkout --orphan new && + test_must_fail git checkout --detach +' + test_done diff --git a/t/t2020-checkout-detach.sh b/t/t2020-checkout-detach.sh index 068fba4c8e..5d68729d7a 100755 --- a/t/t2020-checkout-detach.sh +++ b/t/t2020-checkout-detach.sh @@ -11,14 +11,13 @@ check_not_detached () { git symbolic-ref -q HEAD >/dev/null } -ORPHAN_WARNING='you are leaving .* commit.*behind' PREV_HEAD_DESC='Previous HEAD position was' check_orphan_warning() { - test_i18ngrep "$ORPHAN_WARNING" "$1" && + test_i18ngrep "you are leaving $2 behind" "$1" && test_i18ngrep ! "$PREV_HEAD_DESC" "$1" } check_no_orphan_warning() { - test_i18ngrep ! "$ORPHAN_WARNING" "$1" && + test_i18ngrep ! "you are leaving .* commit.*behind" "$1" && test_i18ngrep "$PREV_HEAD_DESC" "$1" } @@ -110,12 +109,24 @@ test_expect_success 'checkout warns on orphan commits' ' git checkout --detach two && echo content >orphan && git add orphan && - git commit -a -m orphan && + git commit -a -m orphan1 && + echo new content >orphan && + git commit -a -m orphan2 && + orphan2=$(git rev-parse HEAD) && git checkout master 2>stderr ' test_expect_success 'checkout warns on orphan commits: output' ' - check_orphan_warning stderr + check_orphan_warning stderr "2 commits" +' + +test_expect_success 'checkout warns orphaning 1 of 2 commits' ' + git checkout "$orphan2" && + git checkout HEAD^ 2>stderr +' + +test_expect_success 'checkout warns orphaning 1 of 2 commits: output' ' + check_orphan_warning stderr "1 commit" ' test_expect_success 'checkout does not warn leaving ref tip' ' @@ -140,6 +151,7 @@ test_expect_success 'checkout does not warn leaving reachable commit' ' cat >expect <<'EOF' Your branch is behind 'master' by 1 commit, and can be fast-forwarded. + (use "git pull" to update your local branch) EOF test_expect_success 'tracking count is accurate after orphan check' ' reset && @@ -148,7 +160,7 @@ test_expect_success 'tracking count is accurate after orphan check' ' git config branch.child.merge refs/heads/master && git checkout child^ && git checkout child >stdout && - test_cmp expect stdout + test_i18ncmp expect stdout ' test_done diff --git a/t/t2021-checkout-overwrite.sh b/t/t2021-checkout-overwrite.sh index 5da63e9fa2..c2ada7de37 100755 --- a/t/t2021-checkout-overwrite.sh +++ b/t/t2021-checkout-overwrite.sh @@ -29,21 +29,25 @@ test_expect_success 'checkout commit with dir must not remove untracked a/b' ' test -f a/b ' -test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink' ' +test_expect_success 'create a commit where dir a/b changed to symlink' ' rm -rf a/b && # cleanup if previous test failed git checkout -f -b symlink start && rm -rf a/b && - ln -s foo a/b && git add -A && + test_ln_s_add foo a/b && git commit -m "dir to symlink" ' -test_expect_success SYMLINKS 'checkout commit with dir must not remove untracked a/b' ' +test_expect_success 'checkout commit with dir must not remove untracked a/b' ' git rm --cached a/b && git commit -m "un-track the symlink" && - test_must_fail git checkout start && + test_must_fail git checkout start +' + +test_expect_success SYMLINKS 'the symlink remained' ' + test -h a/b ' diff --git a/t/t2022-checkout-paths.sh b/t/t2022-checkout-paths.sh index 56090d2eba..8e3545d868 100755 --- a/t/t2022-checkout-paths.sh +++ b/t/t2022-checkout-paths.sh @@ -39,4 +39,26 @@ test_expect_success 'checking out paths out of a tree does not clobber unrelated test_cmp expect.next2 dir/next2 ' +test_expect_success 'do not touch unmerged entries matching $path but not in $tree' ' + git checkout next && + git reset --hard && + + cat dir/common >expect.common && + EMPTY_SHA1=$(git hash-object -w --stdin </dev/null) && + git rm dir/next0 && + cat >expect.next0 <<-EOF && + 100644 $EMPTY_SHA1 1 dir/next0 + 100644 $EMPTY_SHA1 2 dir/next0 + EOF + git update-index --index-info <expect.next0 && + + git checkout master dir && + + test_cmp expect.common dir/common && + test_path_is_file dir/master && + git diff --exit-code master dir/master && + git ls-files -s dir/next0 >actual.next0 && + test_cmp expect.next0 actual.next0 +' + test_done diff --git a/t/t2024-checkout-dwim.sh b/t/t2024-checkout-dwim.sh new file mode 100755 index 0000000000..6ecb559465 --- /dev/null +++ b/t/t2024-checkout-dwim.sh @@ -0,0 +1,188 @@ +#!/bin/sh + +test_description='checkout <branch> + +Ensures that checkout on an unborn branch does what the user expects' + +. ./test-lib.sh + +# Is the current branch "refs/heads/$1"? +test_branch () { + printf "%s\n" "refs/heads/$1" >expect.HEAD && + git symbolic-ref HEAD >actual.HEAD && + test_cmp expect.HEAD actual.HEAD +} + +# Is branch "refs/heads/$1" set to pull from "$2/$3"? +test_branch_upstream () { + printf "%s\n" "$2" "refs/heads/$3" >expect.upstream && + { + git config "branch.$1.remote" && + git config "branch.$1.merge" + } >actual.upstream && + test_cmp expect.upstream actual.upstream +} + +test_expect_success 'setup' ' + test_commit my_master && + git init repo_a && + ( + cd repo_a && + test_commit a_master && + git checkout -b foo && + test_commit a_foo && + git checkout -b bar && + test_commit a_bar + ) && + git init repo_b && + ( + cd repo_b && + test_commit b_master && + git checkout -b foo && + test_commit b_foo && + git checkout -b baz && + test_commit b_baz + ) && + git remote add repo_a repo_a && + git remote add repo_b repo_b && + git config remote.repo_b.fetch \ + "+refs/heads/*:refs/remotes/other_b/*" && + git fetch --all +' + +test_expect_success 'checkout of non-existing branch fails' ' + git checkout -B master && + test_might_fail git branch -D xyzzy && + + test_must_fail git checkout xyzzy && + test_must_fail git rev-parse --verify refs/heads/xyzzy && + test_branch master +' + +test_expect_success 'checkout of branch from multiple remotes fails #1' ' + git checkout -B master && + test_might_fail git branch -D foo && + + test_must_fail git checkout foo && + test_must_fail git rev-parse --verify refs/heads/foo && + test_branch master +' + +test_expect_success 'checkout of branch from a single remote succeeds #1' ' + git checkout -B master && + test_might_fail git branch -D bar && + + git checkout bar && + test_branch bar && + test_cmp_rev remotes/repo_a/bar HEAD && + test_branch_upstream bar repo_a bar +' + +test_expect_success 'checkout of branch from a single remote succeeds #2' ' + git checkout -B master && + test_might_fail git branch -D baz && + + git checkout baz && + test_branch baz && + test_cmp_rev remotes/other_b/baz HEAD && + test_branch_upstream baz repo_b baz +' + +test_expect_success '--no-guess suppresses branch auto-vivification' ' + git checkout -B master && + test_might_fail git branch -D bar && + + test_must_fail git checkout --no-guess bar && + test_must_fail git rev-parse --verify refs/heads/bar && + test_branch master +' + +test_expect_success 'setup more remotes with unconventional refspecs' ' + git checkout -B master && + git init repo_c && + ( + cd repo_c && + test_commit c_master && + git checkout -b bar && + test_commit c_bar && + git checkout -b spam && + test_commit c_spam + ) && + git init repo_d && + ( + cd repo_d && + test_commit d_master && + git checkout -b baz && + test_commit d_baz && + git checkout -b eggs && + test_commit d_eggs + ) && + git remote add repo_c repo_c && + git config remote.repo_c.fetch \ + "+refs/heads/*:refs/remotes/extra_dir/repo_c/extra_dir/*" && + git remote add repo_d repo_d && + git config remote.repo_d.fetch \ + "+refs/heads/*:refs/repo_d/*" && + git fetch --all +' + +test_expect_success 'checkout of branch from multiple remotes fails #2' ' + git checkout -B master && + test_might_fail git branch -D bar && + + test_must_fail git checkout bar && + test_must_fail git rev-parse --verify refs/heads/bar && + test_branch master +' + +test_expect_success 'checkout of branch from multiple remotes fails #3' ' + git checkout -B master && + test_might_fail git branch -D baz && + + test_must_fail git checkout baz && + test_must_fail git rev-parse --verify refs/heads/baz && + test_branch master +' + +test_expect_success 'checkout of branch from a single remote succeeds #3' ' + git checkout -B master && + test_might_fail git branch -D spam && + + git checkout spam && + test_branch spam && + test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD && + test_branch_upstream spam repo_c spam +' + +test_expect_success 'checkout of branch from a single remote succeeds #4' ' + git checkout -B master && + test_might_fail git branch -D eggs && + + git checkout eggs && + test_branch eggs && + test_cmp_rev refs/repo_d/eggs HEAD && + test_branch_upstream eggs repo_d eggs +' + +test_expect_success 'checkout of branch with a file having the same name fails' ' + git checkout -B master && + test_might_fail git branch -D spam && + + >spam && + test_must_fail git checkout spam && + test_must_fail git rev-parse --verify refs/heads/spam && + test_branch master +' + +test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' ' + git checkout -B master && + test_might_fail git branch -D spam && + + >spam && + git checkout spam -- && + test_branch spam && + test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD && + test_branch_upstream spam repo_c spam +' + +test_done diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh index cb7effe0a3..309199bca2 100755 --- a/t/t2030-unresolve-info.sh +++ b/t/t2030-unresolve-info.sh @@ -44,14 +44,21 @@ prime_resolve_undo () { test_expect_success setup ' mkdir fi && + printf "a\0a" >binary && + git add binary && test_commit initial fi/le first && git branch side && git branch another && + printf "a\0b" >binary && + git add binary && test_commit second fi/le second && git checkout side && test_commit third fi/le third && + git branch add-add && git checkout another && test_commit fourth fi/le fourth && + git checkout add-add && + test_commit fifth add-differently && git checkout master ' @@ -113,7 +120,7 @@ test_expect_success 'unmerge with plumbing' ' prime_resolve_undo && git update-index --unresolve fi/le && git ls-files -u >actual && - test $(wc -l <actual) = 3 + test_line_count = 3 actual ' test_expect_success 'rerere and rerere forget' ' @@ -167,4 +174,22 @@ test_expect_success 'rerere and rerere forget (subdirectory)' ' test_cmp expect actual ' +test_expect_success 'rerere forget (binary)' ' + git checkout -f side && + printf "a\0c" >binary && + git commit -a -m binary && + test_must_fail git merge second && + git rerere forget binary +' + +test_expect_success 'rerere forget (add-add conflict)' ' + git checkout -f master && + echo master >add-differently && + git add add-differently && + git commit -m "add differently" && + test_must_fail git merge fifth && + git rerere forget add-differently 2>actual && + test_i18ngrep "no remembered" actual +' + test_done diff --git a/t/t2107-update-index-basic.sh b/t/t2107-update-index-basic.sh index 0dbbb00d74..a6405d318d 100755 --- a/t/t2107-update-index-basic.sh +++ b/t/t2107-update-index-basic.sh @@ -15,7 +15,7 @@ test_expect_success 'update-index --nonsense fails' ' test_expect_success 'update-index --nonsense dumps usage' ' test_expect_code 129 git update-index --nonsense 2>err && - grep "[Uu]sage: git update-index" err + test_i18ngrep "[Uu]sage: git update-index" err ' test_expect_success 'update-index -h with corrupt index' ' @@ -26,7 +26,7 @@ test_expect_success 'update-index -h with corrupt index' ' >.git/index && test_expect_code 129 git update-index -h >usage 2>&1 ) && - grep "[Uu]sage: git update-index" broken/usage + test_i18ngrep "[Uu]sage: git update-index" broken/usage ' test_expect_success '--cacheinfo does not accept blob null sha1' ' diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh index 4cdebda6a5..9bf2bdffd2 100755 --- a/t/t2200-add-update.sh +++ b/t/t2200-add-update.sh @@ -80,11 +80,26 @@ test_expect_success 'change gets noticed' ' ' -test_expect_success SYMLINKS 'replace a file with a symlink' ' +# Note that this is scheduled to change in Git 2.0, when +# "git add -u" will become full-tree by default. +test_expect_success 'non-limited update in subdir leaves root alone' ' + ( + cd dir1 && + echo even more >>sub2 && + git add -u + ) && + cat >expect <<-\EOF && + check + top + EOF + git diff-files --name-only >actual && + test_cmp expect actual +' + +test_expect_success 'replace a file with a symlink' ' rm foo && - ln -s top foo && - git add -u -- foo + test_ln_s_add top foo ' @@ -150,9 +165,9 @@ test_expect_success 'add -u resolves unmerged paths' ' echo 2 >path3 && echo 2 >path5 && - # Explicit resolving by adding removed paths should fail - test_must_fail git add path4 && - test_must_fail git add path6 && + # Fail to explicitly resolve removed paths with "git add" + test_must_fail git add --no-all path4 && + test_must_fail git add --no-all path6 && # "add -u" should notice removals no matter what stages # the index entries are in. diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh index 6a8151064c..fc8b59e7f7 100755 --- a/t/t2202-add-addremove.sh +++ b/t/t2202-add-addremove.sh @@ -41,4 +41,14 @@ test_expect_success 'git add --all' ' test_cmp expect actual ' +test_expect_success 'Just "git add" is a no-op' ' + git reset --hard && + echo >will-remove && + >will-not-be-added && + git add && + git diff-index --name-status --cached HEAD >actual && + >expect && + test_cmp expect actual +' + test_done diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh index ec35409f9c..2a4a749b4f 100755 --- a/t/t2203-add-intent.sh +++ b/t/t2203-add-intent.sh @@ -62,5 +62,25 @@ test_expect_success 'can "commit -a" with an i-t-a entry' ' git commit -a -m all ' +test_expect_success 'cache-tree invalidates i-t-a paths' ' + git reset --hard && + mkdir dir && + : >dir/foo && + git add dir/foo && + git commit -m foo && + + : >dir/bar && + git add -N dir/bar && + git diff --cached --name-only >actual && + echo dir/bar >expect && + test_cmp expect actual && + + git write-tree >/dev/null && + + git diff --cached --name-only >actual && + echo dir/bar >expect && + test_cmp expect actual +' + test_done diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh index c8fe978267..b2798feef7 100755 --- a/t/t3001-ls-files-others-exclude.sh +++ b/t/t3001-ls-files-others-exclude.sh @@ -103,7 +103,7 @@ test_expect_success \ test_cmp expect output' test_expect_success 'restore gitignore' ' - git checkout $allignores && + git checkout --ignore-skip-worktree-bits $allignores && rm .git/index ' @@ -115,7 +115,7 @@ EOF git config core.excludesFile excludes-file -git status | grep "^# " > output +git -c status.displayCommentPrefix=true status | grep "^# " > output cat > expect << EOF # .gitignore @@ -175,6 +175,24 @@ test_expect_success 'negated exclude matches can override previous ones' ' grep "^a.1" output ' +test_expect_success 'excluded directory overrides content patterns' ' + + git ls-files --others --exclude="one" --exclude="!one/a.1" >output && + if grep "^one/a.1" output + then + false + fi +' + +test_expect_success 'negated directory doesn'\''t affect content patterns' ' + + git ls-files --others --exclude="!one" --exclude="one/a.1" >output && + if grep "^one/a.1" output + then + false + fi +' + test_expect_success 'subdirectory ignore (setup)' ' mkdir -p top/l1/l2 && ( @@ -214,4 +232,77 @@ test_expect_success 'subdirectory ignore (l1)' ' test_cmp expect actual ' +test_expect_success 'show/hide empty ignored directory (setup)' ' + rm top/l1/l2/l1 && + rm top/l1/.gitignore +' + +test_expect_success 'show empty ignored directory with --directory' ' + ( + cd top && + git ls-files -o -i --exclude l1 --directory + ) >actual && + echo l1/ >expect && + test_cmp expect actual +' + +test_expect_success 'hide empty ignored directory with --no-empty-directory' ' + ( + cd top && + git ls-files -o -i --exclude l1 --directory --no-empty-directory + ) >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'show/hide empty ignored sub-directory (setup)' ' + > top/l1/tracked && + ( + cd top && + git add -f l1/tracked + ) +' + +test_expect_success 'show empty ignored sub-directory with --directory' ' + ( + cd top && + git ls-files -o -i --exclude l1 --directory + ) >actual && + echo l1/l2/ >expect && + test_cmp expect actual +' + +test_expect_success 'hide empty ignored sub-directory with --no-empty-directory' ' + ( + cd top && + git ls-files -o -i --exclude l1 --directory --no-empty-directory + ) >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'pattern matches prefix completely' ' + : >expect && + git ls-files -i -o --exclude "/three/a.3[abc]" >actual && + test_cmp expect actual +' + +test_expect_success 'ls-files with "**" patterns' ' + cat <<\EOF >expect && +a.1 +one/a.1 +one/two/a.1 +three/a.1 +EOF + git ls-files -o -i --exclude "**/a.1" >actual + test_cmp expect actual +' + + +test_expect_success 'ls-files with "**" patterns and no slashes' ' + : >expect && + git ls-files -o -i --exclude "one**a.1" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3004-ls-files-basic.sh b/t/t3004-ls-files-basic.sh index 490e052875..8d9bc3c2af 100755 --- a/t/t3004-ls-files-basic.sh +++ b/t/t3004-ls-files-basic.sh @@ -22,7 +22,7 @@ test_expect_success 'ls-files with nonexistent path' ' test_expect_success 'ls-files with nonsense option' ' test_expect_code 129 git ls-files --nonsense 2>actual && - grep "[Uu]sage: git ls-files" actual + test_i18ngrep "[Uu]sage: git ls-files" actual ' test_expect_success 'ls-files -h in corrupt repository' ' @@ -33,7 +33,7 @@ test_expect_success 'ls-files -h in corrupt repository' ' >.git/index && test_expect_code 129 git ls-files -h >usage 2>&1 ) && - grep "[Uu]sage: git ls-files " broken/usage + test_i18ngrep "[Uu]sage: git ls-files " broken/usage ' test_done diff --git a/t/t3006-ls-files-long.sh b/t/t3006-ls-files-long.sh new file mode 100755 index 0000000000..202ad658b8 --- /dev/null +++ b/t/t3006-ls-files-long.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +test_description='overly long paths' +. ./test-lib.sh + +test_expect_success setup ' + p=filefilefilefilefilefilefilefile && + p=$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p && + p=$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p$p && + + path_a=${p}_a && + path_z=${p}_z && + + blob_a=$(echo frotz | git hash-object -w --stdin) && + blob_z=$(echo nitfol | git hash-object -w --stdin) && + + pat="100644 %s 0\t%s\n" +' + +test_expect_success 'overly-long path by itself is not a problem' ' + printf "$pat" "$blob_a" "$path_a" | + git update-index --add --index-info && + echo "$path_a" >expect && + git ls-files >actual && + test_cmp expect actual +' + +test_expect_success 'overly-long path does not replace another by mistake' ' + printf "$pat" "$blob_a" "$path_a" "$blob_z" "$path_z" | + git update-index --add --index-info && + ( + echo "$path_a" + echo "$path_z" + ) >expect && + git ls-files >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh index 95671c2053..6d3b828a95 100755 --- a/t/t3010-ls-files-killed-modified.sh +++ b/t/t3010-ls-files-killed-modified.sh @@ -11,6 +11,9 @@ This test prepares the following in the cache: path1 - a symlink path2/file2 - a file in a directory path3/file3 - a file in a directory + pathx/ju - a file in a directory + submod1/ - a submodule + submod2/ - another submodule and the following on the filesystem: @@ -21,9 +24,12 @@ and the following on the filesystem: path4 - a file path5 - a symlink path6/file6 - a file in a directory + pathx/ju/nk - a file in a directory to be killed + submod1/ - a submodule (modified from the cache) + submod2/ - a submodule (matches the cache) -git ls-files -k should report that existing filesystem -objects except path4, path5 and path6/file6 to be killed. +git ls-files -k should report that existing filesystem objects +path0/*, path1/*, path2 and path3 to be killed. Also for modification test, the cache and working tree have: @@ -33,75 +39,91 @@ Also for modification test, the cache and working tree have: 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. +modified without reporting path9 and path10. submod1 is also modified. ' . ./test-lib.sh -date >path0 -if test_have_prereq SYMLINKS -then - ln -s xyzzy path1 -else - date > path1 -fi -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 -if test_have_prereq SYMLINKS -then - ln -s frotz path3 - ln -s nitfol path5 -else - date > path3 - date > path5 -fi -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.' \ - 'test_cmp .expected .output' - -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.' \ - 'test_cmp .expected .output' +test_expect_success 'git update-index --add to add various paths.' ' + date >path0 && + test_ln_s_add xyzzy path1 && + mkdir path2 path3 pathx && + date >path2/file2 && + date >path3/file3 && + >pathx/ju && + : >path7 && + date >path8 && + : >path9 && + date >path10 && + git update-index --add -- path0 path?/file? pathx/ju path7 path8 path9 path10 && + for i in 1 2 + do + git init submod$i && + ( + cd submod$i && git commit --allow-empty -m "empty $i" + ) || break + done && + git update-index --add submod[12] + ( + cd submod1 && + git commit --allow-empty -m "empty 1 (updated)" + ) && + rm -fr path? # leave path10 alone +' + +test_expect_success 'git ls-files -k to show killed files.' ' + date >path2 && + if test_have_prereq SYMLINKS + then + ln -s frotz path3 && + ln -s nitfol path5 + else + date >path3 && + date >path5 + fi && + mkdir -p path0 path1 path6 pathx/ju && + date >path0/file0 && + date >path1/file1 && + date >path6/file6 && + date >path7 && + : >path8 && + : >path9 && + touch path10 && + >pathx/ju/nk && + cat >.expected <<-\EOF + path0/file0 + path1/file1 + path2 + path3 + pathx/ju/nk + EOF +' + +test_expect_success 'git ls-files -k output (w/o icase)' ' + git ls-files -k >.output + test_cmp .expected .output +' + +test_expect_success 'git ls-files -k output (w/ icase)' ' + git -c core.ignorecase=true ls-files -k >.output + test_cmp .expected .output +' + +test_expect_success 'git ls-files -m to show modified files.' ' + git ls-files -m >.output +' + +test_expect_success 'validate git ls-files -m output.' ' + cat >.expected <<-\EOF && + path0 + path1 + path2/file2 + path3/file3 + path7 + path8 + pathx/ju + submod1 + EOF + test_cmp .expected .output +' test_done diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh index a5e3da7e41..2f96100a5f 100755 --- a/t/t3030-merge-recursive.sh +++ b/t/t3030-merge-recursive.sh @@ -25,10 +25,7 @@ test_expect_success 'setup 1' ' git branch submod && git branch copy && git branch rename && - if test_have_prereq SYMLINKS - then - git branch rename-ln - fi && + git branch rename-ln && echo hello >>a && cp a d/e && @@ -260,16 +257,12 @@ test_expect_success 'setup 8' ' git add e && test_tick && git commit -m "rename a->e" && - if test_have_prereq SYMLINKS - then - git checkout rename-ln && - git mv a e && - ln -s e a && - git add a e && - test_tick && - git commit -m "rename a->e, symlink a->e" && - oln=`printf e | git hash-object --stdin` - fi + git checkout rename-ln && + git mv a e && + test_ln_s_add e a && + test_tick && + git commit -m "rename a->e, symlink a->e" && + oln=`printf e | git hash-object --stdin` ' test_expect_success 'setup 9' ' @@ -569,28 +562,25 @@ test_expect_success 'merge-recursive copy vs. rename' ' test_cmp expected actual ' -if test_have_prereq SYMLINKS -then - test_expect_failure 'merge-recursive rename vs. rename/symlink' ' - - git checkout -f rename && - git merge rename-ln && - ( git ls-tree -r HEAD ; git ls-files -s ) >actual && - ( - echo "120000 blob $oln a" - echo "100644 blob $o0 b" - echo "100644 blob $o0 c" - echo "100644 blob $o0 d/e" - echo "100644 blob $o0 e" - echo "120000 $oln 0 a" - echo "100644 $o0 0 b" - echo "100644 $o0 0 c" - echo "100644 $o0 0 d/e" - echo "100644 $o0 0 e" - ) >expected && - test_cmp expected actual - ' -fi +test_expect_failure 'merge-recursive rename vs. rename/symlink' ' + + git checkout -f rename && + git merge rename-ln && + ( git ls-tree -r HEAD ; git ls-files -s ) >actual && + ( + echo "120000 blob $oln a" + echo "100644 blob $o0 b" + echo "100644 blob $o0 c" + echo "100644 blob $o0 d/e" + echo "100644 blob $o0 e" + echo "120000 $oln 0 a" + echo "100644 $o0 0 b" + echo "100644 $o0 0 c" + echo "100644 $o0 0 d/e" + echo "100644 $o0 0 e" + ) >expected && + test_cmp expected actual +' test_done diff --git a/t/t3032-merge-recursive-options.sh b/t/t3032-merge-recursive-options.sh index 2b17311cb0..5fd7bbb652 100755 --- a/t/t3032-merge-recursive-options.sh +++ b/t/t3032-merge-recursive-options.sh @@ -14,7 +14,7 @@ test_description='merge-recursive options . ./test-lib.sh test_have_prereq SED_STRIPS_CR && SED_OPTIONS=-b -test_have_prereq MINGW && export GREP_OPTIONS=-U +test_have_prereq GREP_STRIPS_CR && export GREP_OPTIONS=-U test_expect_success 'setup' ' conflict_hunks () { diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh new file mode 100755 index 0000000000..38446a0e87 --- /dev/null +++ b/t/t3070-wildmatch.sh @@ -0,0 +1,281 @@ +#!/bin/sh + +test_description='wildmatch tests' + +. ./test-lib.sh + +match() { + if [ $1 = 1 ]; then + test_expect_success "wildmatch: match '$3' '$4'" " + test-wildmatch wildmatch '$3' '$4' + " + else + test_expect_success "wildmatch: no match '$3' '$4'" " + ! test-wildmatch wildmatch '$3' '$4' + " + fi + if [ $2 = 1 ]; then + test_expect_success "fnmatch: match '$3' '$4'" " + test-wildmatch fnmatch '$3' '$4' + " + elif [ $2 = 0 ]; then + test_expect_success "fnmatch: no match '$3' '$4'" " + ! test-wildmatch fnmatch '$3' '$4' + " +# else +# test_expect_success BROKEN_FNMATCH "fnmatch: '$3' '$4'" " +# ! test-wildmatch fnmatch '$3' '$4' +# " + fi +} + +imatch() { + if [ $1 = 1 ]; then + test_expect_success "iwildmatch: match '$2' '$3'" " + test-wildmatch iwildmatch '$2' '$3' + " + else + test_expect_success "iwildmatch: no match '$2' '$3'" " + ! test-wildmatch iwildmatch '$2' '$3' + " + fi +} + +pathmatch() { + if [ $1 = 1 ]; then + test_expect_success "pathmatch: match '$2' '$3'" " + test-wildmatch pathmatch '$2' '$3' + " + else + test_expect_success "pathmatch: no match '$2' '$3'" " + ! test-wildmatch pathmatch '$2' '$3' + " + fi +} + +# Basic wildmat features +match 1 1 foo foo +match 0 0 foo bar +match 1 1 '' "" +match 1 1 foo '???' +match 0 0 foo '??' +match 1 1 foo '*' +match 1 1 foo 'f*' +match 0 0 foo '*f' +match 1 1 foo '*foo*' +match 1 1 foobar '*ob*a*r*' +match 1 1 aaaaaaabababab '*ab' +match 1 1 'foo*' 'foo\*' +match 0 0 foobar 'foo\*bar' +match 1 1 'f\oo' 'f\\oo' +match 1 1 ball '*[al]?' +match 0 0 ten '[ten]' +match 0 1 ten '**[!te]' +match 0 0 ten '**[!ten]' +match 1 1 ten 't[a-g]n' +match 0 0 ten 't[!a-g]n' +match 1 1 ton 't[!a-g]n' +match 1 1 ton 't[^a-g]n' +match 1 x 'a]b' 'a[]]b' +match 1 x a-b 'a[]-]b' +match 1 x 'a]b' 'a[]-]b' +match 0 x aab 'a[]-]b' +match 1 x aab 'a[]a-]b' +match 1 1 ']' ']' + +# Extended slash-matching features +match 0 0 'foo/baz/bar' 'foo*bar' +match 0 0 'foo/baz/bar' 'foo**bar' +match 0 1 'foobazbar' 'foo**bar' +match 1 1 'foo/baz/bar' 'foo/**/bar' +match 1 0 'foo/baz/bar' 'foo/**/**/bar' +match 1 0 'foo/b/a/z/bar' 'foo/**/bar' +match 1 0 'foo/b/a/z/bar' 'foo/**/**/bar' +match 1 0 'foo/bar' 'foo/**/bar' +match 1 0 'foo/bar' 'foo/**/**/bar' +match 0 0 'foo/bar' 'foo?bar' +match 0 0 'foo/bar' 'foo[/]bar' +match 0 0 'foo/bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' +match 1 1 'foo-bar' 'f[^eiu][^eiu][^eiu][^eiu][^eiu]r' +match 1 0 'foo' '**/foo' +match 1 x 'XXX/foo' '**/foo' +match 1 0 'bar/baz/foo' '**/foo' +match 0 0 'bar/baz/foo' '*/foo' +match 0 0 'foo/bar/baz' '**/bar*' +match 1 0 'deep/foo/bar/baz' '**/bar/*' +match 0 0 'deep/foo/bar/baz/' '**/bar/*' +match 1 0 'deep/foo/bar/baz/' '**/bar/**' +match 0 0 'deep/foo/bar' '**/bar/*' +match 1 0 'deep/foo/bar/' '**/bar/**' +match 0 0 'foo/bar/baz' '**/bar**' +match 1 0 'foo/bar/baz/x' '*/bar/**' +match 0 0 'deep/foo/bar/baz/x' '*/bar/**' +match 1 0 'deep/foo/bar/baz/x' '**/bar/*/*' + +# Various additional tests +match 0 0 'acrt' 'a[c-c]st' +match 1 1 'acrt' 'a[c-c]rt' +match 0 0 ']' '[!]-]' +match 1 x 'a' '[!]-]' +match 0 0 '' '\' +match 0 x '\' '\' +match 0 x 'XXX/\' '*/\' +match 1 x 'XXX/\' '*/\\' +match 1 1 'foo' 'foo' +match 1 1 '@foo' '@foo' +match 0 0 'foo' '@foo' +match 1 1 '[ab]' '\[ab]' +match 1 1 '[ab]' '[[]ab]' +match 1 x '[ab]' '[[:]ab]' +match 0 x '[ab]' '[[::]ab]' +match 1 x '[ab]' '[[:digit]ab]' +match 1 x '[ab]' '[\[:]ab]' +match 1 1 '?a?b' '\??\?b' +match 1 1 'abc' '\a\b\c' +match 0 0 'foo' '' +match 1 0 'foo/bar/baz/to' '**/t[o]' + +# Character class tests +match 1 x 'a1B' '[[:alpha:]][[:digit:]][[:upper:]]' +match 0 x 'a' '[[:digit:][:upper:][:space:]]' +match 1 x 'A' '[[:digit:][:upper:][:space:]]' +match 1 x '1' '[[:digit:][:upper:][:space:]]' +match 0 x '1' '[[:digit:][:upper:][:spaci:]]' +match 1 x ' ' '[[:digit:][:upper:][:space:]]' +match 0 x '.' '[[:digit:][:upper:][:space:]]' +match 1 x '.' '[[:digit:][:punct:][:space:]]' +match 1 x '5' '[[:xdigit:]]' +match 1 x 'f' '[[:xdigit:]]' +match 1 x 'D' '[[:xdigit:]]' +match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' +match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]' +match 1 x '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]' +match 1 x '5' '[a-c[:digit:]x-z]' +match 1 x 'b' '[a-c[:digit:]x-z]' +match 1 x 'y' '[a-c[:digit:]x-z]' +match 0 x 'q' '[a-c[:digit:]x-z]' + +# Additional tests, including some malformed wildmats +match 1 x ']' '[\\-^]' +match 0 0 '[' '[\\-^]' +match 1 x '-' '[\-_]' +match 1 x ']' '[\]]' +match 0 0 '\]' '[\]]' +match 0 0 '\' '[\]]' +match 0 0 'ab' 'a[]b' +match 0 x 'a[]b' 'a[]b' +match 0 x 'ab[' 'ab[' +match 0 0 'ab' '[!' +match 0 0 'ab' '[-' +match 1 1 '-' '[-]' +match 0 0 '-' '[a-' +match 0 0 '-' '[!a-' +match 1 x '-' '[--A]' +match 1 x '5' '[--A]' +match 1 1 ' ' '[ --]' +match 1 1 '$' '[ --]' +match 1 1 '-' '[ --]' +match 0 0 '0' '[ --]' +match 1 x '-' '[---]' +match 1 x '-' '[------]' +match 0 0 'j' '[a-e-n]' +match 1 x '-' '[a-e-n]' +match 1 x 'a' '[!------]' +match 0 0 '[' '[]-a]' +match 1 x '^' '[]-a]' +match 0 0 '^' '[!]-a]' +match 1 x '[' '[!]-a]' +match 1 1 '^' '[a^bc]' +match 1 x '-b]' '[a-]b]' +match 0 0 '\' '[\]' +match 1 1 '\' '[\\]' +match 0 0 '\' '[!\\]' +match 1 1 'G' '[A-\\]' +match 0 0 'aaabbb' 'b*a' +match 0 0 'aabcaa' '*ba*' +match 1 1 ',' '[,]' +match 1 1 ',' '[\\,]' +match 1 1 '\' '[\\,]' +match 1 1 '-' '[,-.]' +match 0 0 '+' '[,-.]' +match 0 0 '-.]' '[,-.]' +match 1 1 '2' '[\1-\3]' +match 1 1 '3' '[\1-\3]' +match 0 0 '4' '[\1-\3]' +match 1 1 '\' '[[-\]]' +match 1 1 '[' '[[-\]]' +match 1 1 ']' '[[-\]]' +match 0 0 '-' '[[-\]]' + +# Test recursion and the abort code (use "wildtest -i" to see iteration counts) +match 1 1 '-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' +match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' +match 0 0 '-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1' '-*-*-*-*-*-*-12-*-*-*-m-*-*-*' +match 1 1 'XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*' +match 0 0 'XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1' 'XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*' +match 1 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt' '**/*a*b*g*n*t' +match 0 0 'abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz' '**/*a*b*g*n*t' +match 0 x foo '*/*/*' +match 0 x foo/bar '*/*/*' +match 1 x foo/bba/arr '*/*/*' +match 0 x foo/bb/aa/rr '*/*/*' +match 1 x foo/bb/aa/rr '**/**/**' +match 1 x abcXdefXghi '*X*i' +match 0 x ab/cXd/efXg/hi '*X*i' +match 1 x ab/cXd/efXg/hi '*/*X*/*/*i' +match 1 x ab/cXd/efXg/hi '**/*X*/**/*i' + +pathmatch 1 foo foo +pathmatch 0 foo fo +pathmatch 1 foo/bar foo/bar +pathmatch 1 foo/bar 'foo/*' +pathmatch 1 foo/bba/arr 'foo/*' +pathmatch 1 foo/bba/arr 'foo/**' +pathmatch 1 foo/bba/arr 'foo*' +pathmatch 1 foo/bba/arr 'foo**' +pathmatch 1 foo/bba/arr 'foo/*arr' +pathmatch 1 foo/bba/arr 'foo/**arr' +pathmatch 0 foo/bba/arr 'foo/*z' +pathmatch 0 foo/bba/arr 'foo/**z' +pathmatch 1 foo/bar 'foo?bar' +pathmatch 1 foo/bar 'foo[/]bar' +pathmatch 0 foo '*/*/*' +pathmatch 0 foo/bar '*/*/*' +pathmatch 1 foo/bba/arr '*/*/*' +pathmatch 1 foo/bb/aa/rr '*/*/*' +pathmatch 1 abcXdefXghi '*X*i' +pathmatch 1 ab/cXd/efXg/hi '*/*X*/*/*i' +pathmatch 1 ab/cXd/efXg/hi '*Xg*i' + +# Case-sensitivy features +match 0 x 'a' '[A-Z]' +match 1 x 'A' '[A-Z]' +match 0 x 'A' '[a-z]' +match 1 x 'a' '[a-z]' +match 0 x 'a' '[[:upper:]]' +match 1 x 'A' '[[:upper:]]' +match 0 x 'A' '[[:lower:]]' +match 1 x 'a' '[[:lower:]]' +match 0 x 'A' '[B-Za]' +match 1 x 'a' '[B-Za]' +match 0 x 'A' '[B-a]' +match 1 x 'a' '[B-a]' +match 0 x 'z' '[Z-y]' +match 1 x 'Z' '[Z-y]' + +imatch 1 'a' '[A-Z]' +imatch 1 'A' '[A-Z]' +imatch 1 'A' '[a-z]' +imatch 1 'a' '[a-z]' +imatch 1 'a' '[[:upper:]]' +imatch 1 'A' '[[:upper:]]' +imatch 1 'A' '[[:lower:]]' +imatch 1 'a' '[[:lower:]]' +imatch 1 'A' '[B-Za]' +imatch 1 'a' '[B-Za]' +imatch 1 'A' '[B-a]' +imatch 1 'a' '[B-a]' +imatch 1 'z' '[Z-y]' +imatch 1 'Z' '[Z-y]' + +test_done diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh index 81d90b66c5..eb73c06a4e 100755 --- a/t/t3100-ls-tree-restrict.sh +++ b/t/t3100-ls-tree-restrict.sh @@ -22,20 +22,8 @@ test_expect_success \ 'setup' \ 'mkdir path2 path2/baz && echo Hi >path0 && - if test_have_prereq SYMLINKS - then - ln -s path0 path1 && - ln -s ../path1 path2/bazbo - make_expected () { - cat >expected - } - else - printf path0 > path1 && - printf ../path1 > path2/bazbo - make_expected () { - sed -e "s/120000 /100644 /" >expected - } - fi && + test_ln_s_add path0 path1 && + test_ln_s_add ../path1 path2/bazbo && echo Lo >path2/foo && echo Mi >path2/baz/b && find path? \( -type f -o -type l \) -print | @@ -51,7 +39,7 @@ test_output () { test_expect_success \ 'ls-tree plain' \ 'git ls-tree $tree >current && - make_expected <<\EOF && + cat >expected <<\EOF && 100644 blob X path0 120000 blob X path1 040000 tree X path2 @@ -61,7 +49,7 @@ EOF test_expect_success \ 'ls-tree recursive' \ 'git ls-tree -r $tree >current && - make_expected <<\EOF && + cat >expected <<\EOF && 100644 blob X path0 120000 blob X path1 100644 blob X path2/baz/b @@ -73,7 +61,7 @@ EOF test_expect_success \ 'ls-tree recursive with -t' \ 'git ls-tree -r -t $tree >current && - make_expected <<\EOF && + cat >expected <<\EOF && 100644 blob X path0 120000 blob X path1 040000 tree X path2 @@ -87,7 +75,7 @@ EOF test_expect_success \ 'ls-tree recursive with -d' \ 'git ls-tree -r -d $tree >current && - make_expected <<\EOF && + cat >expected <<\EOF && 040000 tree X path2 040000 tree X path2/baz EOF @@ -96,7 +84,7 @@ EOF test_expect_success \ 'ls-tree filtered with path' \ 'git ls-tree $tree path >current && - make_expected <<\EOF && + cat >expected <<\EOF && EOF test_output' @@ -106,7 +94,7 @@ EOF test_expect_success \ 'ls-tree filtered with path1 path0' \ 'git ls-tree $tree path1 path0 >current && - make_expected <<\EOF && + cat >expected <<\EOF && 100644 blob X path0 120000 blob X path1 EOF @@ -115,7 +103,7 @@ EOF test_expect_success \ 'ls-tree filtered with path0/' \ 'git ls-tree $tree path0/ >current && - make_expected <<\EOF && + cat >expected <<\EOF && EOF test_output' @@ -124,7 +112,7 @@ EOF test_expect_success \ 'ls-tree filtered with path2' \ 'git ls-tree $tree path2 >current && - make_expected <<\EOF && + cat >expected <<\EOF && 040000 tree X path2 EOF test_output' @@ -133,7 +121,7 @@ EOF test_expect_success \ 'ls-tree filtered with path2/' \ 'git ls-tree $tree path2/ >current && - make_expected <<\EOF && + cat >expected <<\EOF && 040000 tree X path2/baz 120000 blob X path2/bazbo 100644 blob X path2/foo @@ -145,7 +133,7 @@ EOF test_expect_success \ 'ls-tree filtered with path2/baz' \ 'git ls-tree $tree path2/baz >current && - make_expected <<\EOF && + cat >expected <<\EOF && 040000 tree X path2/baz EOF test_output' @@ -153,14 +141,14 @@ EOF test_expect_success \ 'ls-tree filtered with path2/bak' \ 'git ls-tree $tree path2/bak >current && - make_expected <<\EOF && + cat >expected <<\EOF && EOF test_output' test_expect_success \ 'ls-tree -t filtered with path2/bak' \ 'git ls-tree -t $tree path2/bak >current && - make_expected <<\EOF && + cat >expected <<\EOF && 040000 tree X path2 EOF test_output' @@ -168,7 +156,7 @@ EOF test_expect_success \ 'ls-tree with one path a prefix of the other' \ 'git ls-tree $tree path2/baz path2/bazbo >current && - make_expected <<\EOF && + cat >expected <<\EOF && 040000 tree X path2/baz 120000 blob X path2/bazbo EOF diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 9fe1d8feab..fcdb867748 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -7,20 +7,19 @@ test_description='git branch assorted tests' . ./test-lib.sh -test_expect_success \ - 'prepare a trivial repository' \ - 'echo Hello > A && - git update-index --add A && - git commit -m "Initial commit." && - echo World >> A && - git update-index --add A && - git commit -m "Second commit." && - HEAD=$(git rev-parse --verify HEAD)' - -test_expect_success \ - 'git branch --help should not have created a bogus branch' ' - test_might_fail git branch --help </dev/null >/dev/null 2>/dev/null && - test_path_is_missing .git/refs/heads/--help +test_expect_success 'prepare a trivial repository' ' + echo Hello >A && + git update-index --add A && + git commit -m "Initial commit." && + echo World >>A && + git update-index --add A && + git commit -m "Second commit." && + HEAD=$(git rev-parse --verify HEAD) +' + +test_expect_success 'git branch --help should not have created a bogus branch' ' + test_might_fail git branch --man --help </dev/null >/dev/null 2>&1 && + test_path_is_missing .git/refs/heads/--help ' test_expect_success 'branch -h in broken repository' ' @@ -31,66 +30,70 @@ test_expect_success 'branch -h in broken repository' ' >.git/refs/heads/master && test_expect_code 129 git branch -h >usage 2>&1 ) && - grep "[Uu]sage" broken/usage + test_i18ngrep "[Uu]sage" broken/usage +' + +test_expect_success 'git branch abc should create a branch' ' + git branch abc && test_path_is_file .git/refs/heads/abc ' -test_expect_success \ - 'git branch abc should create a branch' \ - 'git branch abc && test_path_is_file .git/refs/heads/abc' +test_expect_success 'git branch a/b/c should create a branch' ' + git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c +' -test_expect_success \ - 'git branch a/b/c should create a branch' \ - 'git branch a/b/c && test_path_is_file .git/refs/heads/a/b/c' +test_expect_success 'git branch HEAD should fail' ' + test_must_fail git branch HEAD +' cat >expect <<EOF $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master 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_path_is_file .git/refs/heads/d/e/f && - test_path_is_file .git/logs/refs/heads/d/e/f && - test_cmp 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_path_is_missing .git/refs/heads/d/e/f && - test_path_is_missing .git/logs/refs/heads/d/e/f' - -test_expect_success \ - 'git branch j/k should work after branch j has been deleted' \ - 'git branch j && - git branch -d j && - git branch j/k' - -test_expect_success \ - 'git branch l should work after branch l/m has been deleted' \ - 'git branch l/m && - git branch -d l/m && - git branch l' - -test_expect_success \ - 'git branch -m dumps usage' \ - 'test_expect_code 129 git branch -m 2>err && - grep "[Uu]sage: git branch" err' - -test_expect_success \ - 'git branch -m m m/m should work' \ - 'git branch -l m && - git branch -m m m/m && - test_path_is_file .git/logs/refs/heads/m/m' - -test_expect_success \ - 'git branch -m n/n n should work' \ - 'git branch -l n/n && +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_path_is_file .git/refs/heads/d/e/f && + test_path_is_file .git/logs/refs/heads/d/e/f && + test_cmp 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_path_is_missing .git/refs/heads/d/e/f && + test_path_is_missing .git/logs/refs/heads/d/e/f +' + +test_expect_success 'git branch j/k should work after branch j has been deleted' ' + git branch j && + git branch -d j && + git branch j/k +' + +test_expect_success 'git branch l should work after branch l/m has been deleted' ' + git branch l/m && + git branch -d l/m && + git branch l +' + +test_expect_success 'git branch -m dumps usage' ' + test_expect_code 128 git branch -m 2>err && + test_i18ngrep "branch name required" err +' + +test_expect_success 'git branch -m m m/m should work' ' + git branch -l m && + git branch -m m m/m && + test_path_is_file .git/logs/refs/heads/m/m +' + +test_expect_success 'git branch -m n/n n should work' ' + git branch -l n/n && git branch -m n/n n && - test_path_is_file .git/logs/refs/heads/n' + test_path_is_file .git/logs/refs/heads/n +' test_expect_success 'git branch -m o/o o should fail when o/p exists' ' git branch o/o && - git branch o/p && + git branch o/p && test_must_fail git branch -m o/o o ' @@ -160,6 +163,83 @@ test_expect_success 'git branch --list -d t should fail' ' test_path_is_missing .git/refs/heads/t ' +test_expect_success 'git branch --column' ' + COLUMNS=81 git branch --column=column >actual && + cat >expected <<\EOF && + a/b/c bam foo l * master n o/p r + abc bar j/k m/m master2 o/o q +EOF + test_cmp expected actual +' + +test_expect_success 'git branch --column with an extremely long branch name' ' + long=this/is/a/part/of/long/branch/name && + long=z$long/$long/$long/$long && + test_when_finished "git branch -d $long" && + git branch $long && + COLUMNS=80 git branch --column=column >actual && + cat >expected <<EOF && + a/b/c + abc + bam + bar + foo + j/k + l + m/m +* master + master2 + n + o/o + o/p + q + r + $long +EOF + test_cmp expected actual +' + +test_expect_success 'git branch with column.*' ' + git config column.ui column && + git config column.branch "dense" && + COLUMNS=80 git branch >actual && + git config --unset column.branch && + git config --unset column.ui && + cat >expected <<\EOF && + a/b/c bam foo l * master n o/p r + abc bar j/k m/m master2 o/o q +EOF + test_cmp expected actual +' + +test_expect_success 'git branch --column -v should fail' ' + test_must_fail git branch --column -v +' + +test_expect_success 'git branch -v with column.ui ignored' ' + git config column.ui column && + COLUMNS=80 git branch -v | cut -c -10 | sed "s/ *$//" >actual && + git config --unset column.ui && + cat >expected <<\EOF && + a/b/c + abc + bam + bar + foo + j/k + l + m/m +* master + master2 + n + o/o + o/p + q + r +EOF + test_cmp expected actual +' + mv .git/config .git/config-saved test_expect_success 'git branch -m q q2 without config should succeed' ' @@ -171,22 +251,41 @@ mv .git/config-saved .git/config git config branch.s/s.dummy Hello -test_expect_success \ - 'git branch -m s/s s should work when s/t is deleted' \ - 'git branch -l s/s && +test_expect_success 'git branch -m s/s s should work when s/t is deleted' ' + git branch -l s/s && test_path_is_file .git/logs/refs/heads/s/s && - git branch -l s/t && + git branch -l s/t && test_path_is_file .git/logs/refs/heads/s/t && - git branch -d s/t && - git branch -m s/s s && - test_path_is_file .git/logs/refs/heads/s' + git branch -d s/t && + git branch -m s/s s && + test_path_is_file .git/logs/refs/heads/s +' -test_expect_success 'config information was renamed, too' \ - "test $(git config branch.s.dummy) = Hello && - test_must_fail git config branch.s/s/dummy" +test_expect_success 'config information was renamed, too' ' + test $(git config branch.s.dummy) = Hello && + test_must_fail git config branch.s/s/dummy +' -test_expect_success 'renaming a symref is not allowed' \ +test_expect_success 'deleting a symref' ' + git branch target && + git symbolic-ref refs/heads/symref refs/heads/target && + echo "Deleted branch symref (was refs/heads/target)." >expect && + git branch -d symref >actual && + test_path_is_file .git/refs/heads/target && + test_path_is_missing .git/refs/heads/symref && + test_i18ncmp expect actual ' + +test_expect_success 'deleting a dangling symref' ' + git symbolic-ref refs/heads/dangling-symref nowhere && + test_path_is_file .git/refs/heads/dangling-symref && + echo "Deleted branch dangling-symref (was nowhere)." >expect && + git branch -d dangling-symref >actual && + test_path_is_missing .git/refs/heads/dangling-symref && + test_i18ncmp expect actual +' + +test_expect_success 'renaming a symref is not allowed' ' git symbolic-ref refs/heads/master2 refs/heads/master && test_must_fail git branch -m master2 master3 && git symbolic-ref refs/heads/master2 && @@ -194,115 +293,231 @@ test_expect_success 'renaming a symref is not allowed' \ test_path_is_missing .git/refs/heads/master3 ' -test_expect_success SYMLINKS \ - 'git branch -m u v should fail when the reflog for u is a symlink' ' - git branch -l u && - mv .git/logs/refs/heads/u real-u && - ln -s real-u .git/logs/refs/heads/u && - test_must_fail git branch -m u v -' - -test_expect_success 'test tracking setup via --track' \ - 'git config remote.local.url . && - git config remote.local.fetch refs/heads/*:refs/remotes/local/* && - (git show-ref -q refs/remotes/local/master || git fetch local) && - git branch --track my1 local/master && - test $(git config branch.my1.remote) = local && - test $(git config branch.my1.merge) = refs/heads/master' - -test_expect_success 'test tracking setup (non-wildcard, matching)' \ - 'git config remote.local.url . && - git config remote.local.fetch refs/heads/master:refs/remotes/local/master && - (git show-ref -q refs/remotes/local/master || git fetch local) && - git branch --track my4 local/master && - test $(git config branch.my4.remote) = local && - test $(git config branch.my4.merge) = refs/heads/master' - -test_expect_success 'test tracking setup (non-wildcard, not matching)' \ - 'git config remote.local.url . && - git config remote.local.fetch refs/heads/s:refs/remotes/local/s && - (git show-ref -q refs/remotes/local/master || git fetch local) && - git branch --track my5 local/master && - ! test "$(git config branch.my5.remote)" = local && - ! test "$(git config branch.my5.merge)" = refs/heads/master' - -test_expect_success 'test tracking setup via config' \ - 'git config branch.autosetupmerge true && - git config remote.local.url . && - git config remote.local.fetch refs/heads/*:refs/remotes/local/* && - (git show-ref -q refs/remotes/local/master || git fetch local) && - git branch my3 local/master && - test $(git config branch.my3.remote) = local && - test $(git config branch.my3.merge) = refs/heads/master' - -test_expect_success 'test overriding tracking setup via --no-track' \ - 'git config branch.autosetupmerge true && - git config remote.local.url . && - git config remote.local.fetch refs/heads/*:refs/remotes/local/* && - (git show-ref -q refs/remotes/local/master || git fetch local) && - git branch --no-track my2 local/master && - git config branch.autosetupmerge false && - ! test "$(git config branch.my2.remote)" = local && - ! test "$(git config branch.my2.merge)" = refs/heads/master' - -test_expect_success 'no tracking without .fetch entries' \ - 'git config branch.autosetupmerge true && - git branch my6 s && - git config branch.automsetupmerge false && - test -z "$(git config branch.my6.remote)" && - test -z "$(git config branch.my6.merge)"' - -test_expect_success 'test tracking setup via --track but deeper' \ - 'git config remote.local.url . && - git config remote.local.fetch refs/heads/*:refs/remotes/local/* && - (git show-ref -q refs/remotes/local/o/o || git fetch local) && - git branch --track my7 local/o/o && - test "$(git config branch.my7.remote)" = local && - test "$(git config branch.my7.merge)" = refs/heads/o/o' - -test_expect_success 'test deleting branch deletes branch config' \ - 'git branch -d my7 && - test -z "$(git config branch.my7.remote)" && - test -z "$(git config branch.my7.merge)"' - -test_expect_success 'test deleting branch without config' \ - 'git branch my7 s && - sha1=$(git rev-parse my7 | cut -c 1-7) && - echo "Deleted branch my7 (was $sha1)." >expect && - git branch -d my7 >actual 2>&1 && - test_i18ncmp expect actual' - -test_expect_success 'test --track without .fetch entries' \ - 'git branch --track my8 && - test "$(git config branch.my8.remote)" && - test "$(git config branch.my8.merge)"' - -test_expect_success \ - 'branch from non-branch HEAD w/autosetupmerge=always' \ - 'git config branch.autosetupmerge always && - git branch my9 HEAD^ && - git config branch.autosetupmerge false' - -test_expect_success \ - 'branch from non-branch HEAD w/--track causes failure' \ - 'test_must_fail git branch --track my10 HEAD^' - -test_expect_success \ - 'branch from tag w/--track causes failure' \ - 'git tag foobar && - test_must_fail git branch --track my11 foobar' +test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' ' + git branch -l u && + mv .git/logs/refs/heads/u real-u && + ln -s real-u .git/logs/refs/heads/u && + test_must_fail git branch -m u v +' + +test_expect_success 'test tracking setup via --track' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git fetch local) && + git branch --track my1 local/master && + test $(git config branch.my1.remote) = local && + test $(git config branch.my1.merge) = refs/heads/master +' + +test_expect_success 'test tracking setup (non-wildcard, matching)' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/master:refs/remotes/local/master && + (git show-ref -q refs/remotes/local/master || git fetch local) && + git branch --track my4 local/master && + test $(git config branch.my4.remote) = local && + test $(git config branch.my4.merge) = refs/heads/master +' + +test_expect_success 'tracking setup fails on non-matching refspec' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git fetch local) && + git config remote.local.fetch refs/heads/s:refs/remotes/local/s && + test_must_fail git branch --track my5 local/master && + test_must_fail git config branch.my5.remote && + test_must_fail git config branch.my5.merge +' + +test_expect_success 'test tracking setup via config' ' + git config branch.autosetupmerge true && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git fetch local) && + git branch my3 local/master && + test $(git config branch.my3.remote) = local && + test $(git config branch.my3.merge) = refs/heads/master +' + +test_expect_success 'test overriding tracking setup via --no-track' ' + git config branch.autosetupmerge true && + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/master || git fetch local) && + git branch --no-track my2 local/master && + git config branch.autosetupmerge false && + ! test "$(git config branch.my2.remote)" = local && + ! test "$(git config branch.my2.merge)" = refs/heads/master +' + +test_expect_success 'no tracking without .fetch entries' ' + git config branch.autosetupmerge true && + git branch my6 s && + git config branch.autosetupmerge false && + test -z "$(git config branch.my6.remote)" && + test -z "$(git config branch.my6.merge)" +' + +test_expect_success 'test tracking setup via --track but deeper' ' + git config remote.local.url . && + git config remote.local.fetch refs/heads/*:refs/remotes/local/* && + (git show-ref -q refs/remotes/local/o/o || git fetch local) && + git branch --track my7 local/o/o && + test "$(git config branch.my7.remote)" = local && + test "$(git config branch.my7.merge)" = refs/heads/o/o +' + +test_expect_success 'test deleting branch deletes branch config' ' + git branch -d my7 && + test -z "$(git config branch.my7.remote)" && + test -z "$(git config branch.my7.merge)" +' + +test_expect_success 'test deleting branch without config' ' + git branch my7 s && + sha1=$(git rev-parse my7 | cut -c 1-7) && + echo "Deleted branch my7 (was $sha1)." >expect && + git branch -d my7 >actual 2>&1 && + test_i18ncmp expect actual +' + +test_expect_success 'test --track without .fetch entries' ' + git branch --track my8 && + test "$(git config branch.my8.remote)" && + test "$(git config branch.my8.merge)" +' + +test_expect_success 'branch from non-branch HEAD w/autosetupmerge=always' ' + git config branch.autosetupmerge always && + git branch my9 HEAD^ && + git config branch.autosetupmerge false +' + +test_expect_success 'branch from non-branch HEAD w/--track causes failure' ' + test_must_fail git branch --track my10 HEAD^ +' + +test_expect_success 'branch from tag w/--track causes failure' ' + git tag foobar && + test_must_fail git branch --track my11 foobar +' + +test_expect_success '--set-upstream-to fails on multiple branches' ' + test_must_fail git branch --set-upstream-to master a b c +' + +test_expect_success '--set-upstream-to fails on detached HEAD' ' + git checkout HEAD^{} && + test_must_fail git branch --set-upstream-to master && + git checkout - +' + +test_expect_success '--set-upstream-to fails on a missing dst branch' ' + test_must_fail git branch --set-upstream-to master does-not-exist +' + +test_expect_success '--set-upstream-to fails on a missing src branch' ' + test_must_fail git branch --set-upstream-to does-not-exist master +' + +test_expect_success '--set-upstream-to fails on a non-ref' ' + test_must_fail git branch --set-upstream-to HEAD^{} +' + +test_expect_success 'use --set-upstream-to modify HEAD' ' + test_config branch.master.remote foo && + test_config branch.master.merge foo && + git branch my12 && + git branch --set-upstream-to my12 && + test "$(git config branch.master.remote)" = "." && + test "$(git config branch.master.merge)" = "refs/heads/my12" +' + +test_expect_success 'use --set-upstream-to modify a particular branch' ' + git branch my13 && + git branch --set-upstream-to master my13 && + test "$(git config branch.my13.remote)" = "." && + test "$(git config branch.my13.merge)" = "refs/heads/master" +' + +test_expect_success '--unset-upstream should fail if given a non-existent branch' ' + test_must_fail git branch --unset-upstream i-dont-exist +' + +test_expect_success 'test --unset-upstream on HEAD' ' + git branch my14 && + test_config branch.master.remote foo && + test_config branch.master.merge foo && + git branch --set-upstream-to my14 && + git branch --unset-upstream && + test_must_fail git config branch.master.remote && + test_must_fail git config branch.master.merge && + # fail for a branch without upstream set + test_must_fail git branch --unset-upstream +' + +test_expect_success '--unset-upstream should fail on multiple branches' ' + test_must_fail git branch --unset-upstream a b c +' + +test_expect_success '--unset-upstream should fail on detached HEAD' ' + git checkout HEAD^{} && + test_must_fail git branch --unset-upstream && + git checkout - +' + +test_expect_success 'test --unset-upstream on a particular branch' ' + git branch my15 && + git branch --set-upstream-to master my14 && + git branch --unset-upstream my14 && + test_must_fail git config branch.my14.remote && + test_must_fail git config branch.my14.merge +' + +test_expect_success '--set-upstream shows message when creating a new branch that exists as remote-tracking' ' + git update-ref refs/remotes/origin/master HEAD && + git branch --set-upstream origin/master 2>actual && + test_when_finished git update-ref -d refs/remotes/origin/master && + test_when_finished git branch -d origin/master && + cat >expected <<EOF && +The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to + +If you wanted to make '"'master'"' track '"'origin/master'"', do this: + + git branch -d origin/master + git branch --set-upstream-to origin/master +EOF + test_cmp expected actual +' + +test_expect_success '--set-upstream with two args only shows the deprecation message' ' + git branch --set-upstream master my13 2>actual && + test_when_finished git branch --unset-upstream master && + cat >expected <<EOF && +The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to +EOF + test_cmp expected actual +' + +test_expect_success '--set-upstream with one arg only shows the deprecation message if the branch existed' ' + git branch --set-upstream my13 2>actual && + test_when_finished git branch --unset-upstream my13 && + cat >expected <<EOF && +The --set-upstream flag is deprecated and will be removed. Consider using --track or --set-upstream-to +EOF + test_cmp expected actual +' # Keep this test last, as it changes the current branch cat >expect <<EOF $_z40 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master 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_path_is_file .git/refs/heads/g/h/i && - test_path_is_file .git/logs/refs/heads/g/h/i && - test_cmp expect .git/logs/refs/heads/g/h/i' +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_path_is_file .git/refs/heads/g/h/i && + test_path_is_file .git/logs/refs/heads/g/h/i && + test_cmp expect .git/logs/refs/heads/g/h/i +' test_expect_success 'checkout -b makes reflog by default' ' git checkout master && @@ -573,7 +788,7 @@ test_expect_success 'detect misconfigured autosetuprebase (bad value)' ' test_expect_success 'detect misconfigured autosetuprebase (no value)' ' git config --unset branch.autosetuprebase && - echo "[branch] autosetuprebase" >> .git/config && + echo "[branch] autosetuprebase" >>.git/config && test_must_fail git branch && git config --unset branch.autosetuprebase ' @@ -657,4 +872,39 @@ test_expect_success '--merged catches invalid object names' ' test_must_fail git branch --merged 0000000000000000000000000000000000000000 ' +test_expect_success 'tracking with unexpected .fetch refspec' ' + rm -rf a b c d && + git init a && + ( + cd a && + test_commit a + ) && + git init b && + ( + cd b && + test_commit b + ) && + git init c && + ( + cd c && + test_commit c && + git remote add a ../a && + git remote add b ../b && + git fetch --all + ) && + git init d && + ( + cd d && + git remote add c ../c && + git config remote.c.fetch "+refs/remotes/*:refs/remotes/*" && + git fetch c && + git branch --track local/a/master remotes/a/master && + test "$(git config branch.local/a/master.remote)" = "c" && + test "$(git config branch.local/a/master.merge)" = "refs/remotes/a/master" && + git rev-parse --verify a >expect && + git rev-parse --verify local/a/master >actual && + test_cmp expect actual + ) +' + test_done diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh index f86f4bc5eb..141b0611ea 100755 --- a/t/t3201-branch-contains.sh +++ b/t/t3201-branch-contains.sh @@ -55,6 +55,16 @@ test_expect_success 'branch --contains=side' ' ' +test_expect_success 'branch --contains with pattern implies --list' ' + + git branch --contains=master master >actual && + { + echo " master" + } >expect && + test_cmp expect actual + +' + test_expect_success 'side: branch --merged' ' git branch --merged >actual && @@ -66,6 +76,16 @@ test_expect_success 'side: branch --merged' ' ' +test_expect_success 'branch --merged with pattern implies --list' ' + + git branch --merged=side master >actual && + { + echo " master" + } >expect && + test_cmp expect actual + +' + test_expect_success 'side: branch --no-merged' ' git branch --no-merged >actual && @@ -95,4 +115,19 @@ test_expect_success 'master: branch --no-merged' ' ' +test_expect_success 'branch --no-merged with pattern implies --list' ' + + git branch --no-merged=master master >actual && + >expect && + test_cmp expect actual + +' + +test_expect_success 'implicit --list conflicts with modification options' ' + + test_must_fail git branch --contains=master -d && + test_must_fail git branch --contains=master -m foo + +' + test_done diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index 76fe7e0060..ba4f98e800 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -94,13 +94,13 @@ test_expect_success 'git branch -v pattern does not show branch summaries' ' test_must_fail git branch -v branch* ' -cat >expect <<'EOF' -* (no branch) +test_expect_success 'git branch shows detached HEAD properly' ' + cat >expect <<EOF && +* (detached from $(git rev-parse --short HEAD^0)) branch-one branch-two master EOF -test_expect_success 'git branch shows detached HEAD properly' ' git checkout HEAD^0 && git branch >actual && test_i18ncmp expect actual diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index cd04361df8..1a2080e3dc 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -118,4 +118,37 @@ test_expect_success 'pack, prune and repack' ' test_cmp all-of-them again ' +test_expect_success 'explicit pack-refs with dangling packed reference' ' + git commit --allow-empty -m "soon to be garbage-collected" && + git pack-refs --all && + git reset --hard HEAD^ && + git reflog expire --expire=all --all && + git prune --expire=all && + git pack-refs --all 2>result && + test_cmp /dev/null result +' + +test_expect_success 'delete ref with dangling packed version' ' + git checkout -b lamb && + git commit --allow-empty -m "future garbage" && + git pack-refs --all && + git reset --hard HEAD^ && + git checkout master && + git reflog expire --expire=all --all && + git prune --expire=all && + git branch -d lamb 2>result && + test_cmp /dev/null result +' + +test_expect_success 'delete ref while another dangling packed ref' ' + git branch lamb && + git commit --allow-empty -m "future garbage" && + git pack-refs --all && + git reset --hard HEAD^ && + git reflog expire --expire=all --all && + git prune --expire=all && + git branch -d lamb 2>result && + test_cmp /dev/null result +' + test_done diff --git a/t/t3211-peel-ref.sh b/t/t3211-peel-ref.sh new file mode 100755 index 0000000000..3b7caca421 --- /dev/null +++ b/t/t3211-peel-ref.sh @@ -0,0 +1,73 @@ +#!/bin/sh + +test_description='tests for the peel_ref optimization of packed-refs' +. ./test-lib.sh + +test_expect_success 'create annotated tag in refs/tags' ' + test_commit base && + git tag -m annotated foo +' + +test_expect_success 'create annotated tag outside of refs/tags' ' + git update-ref refs/outside/foo refs/tags/foo +' + +# This matches show-ref's output +print_ref() { + echo "$(git rev-parse "$1") $1" +} + +test_expect_success 'set up expected show-ref output' ' + { + print_ref "refs/heads/master" && + print_ref "refs/outside/foo" && + print_ref "refs/outside/foo^{}" && + print_ref "refs/tags/base" && + print_ref "refs/tags/foo" && + print_ref "refs/tags/foo^{}" + } >expect +' + +test_expect_success 'refs are peeled outside of refs/tags (loose)' ' + git show-ref -d >actual && + test_cmp expect actual +' + +test_expect_success 'refs are peeled outside of refs/tags (packed)' ' + git pack-refs --all && + git show-ref -d >actual && + test_cmp expect actual +' + +test_expect_success 'create old-style pack-refs without fully-peeled' ' + # Git no longer writes without fully-peeled, so we just write our own + # from scratch; we could also munge the existing file to remove the + # fully-peeled bits, but that seems even more prone to failure, + # especially if the format ever changes again. At least this way we + # know we are emulating exactly what an older git would have written. + { + echo "# pack-refs with: peeled " && + print_ref "refs/heads/master" && + print_ref "refs/outside/foo" && + print_ref "refs/tags/base" && + print_ref "refs/tags/foo" && + echo "^$(git rev-parse "refs/tags/foo^{}")" + } >tmp && + mv tmp .git/packed-refs +' + +test_expect_success 'refs are peeled outside of refs/tags (old packed)' ' + git show-ref -d >actual && + test_cmp expect actual +' + +test_expect_success 'peeled refs survive deletion of packed ref' ' + git pack-refs --all && + cp .git/packed-refs fully-peeled && + git branch yadda && + git pack-refs --all && + git branch -d yadda && + test_cmp fully-peeled .git/packed-refs +' + +test_done diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh index 9f00ada5f7..9a146f1335 100755 --- a/t/t3300-funny-names.sh +++ b/t/t3300-funny-names.sh @@ -11,188 +11,206 @@ tree, index, and tree objects. . ./test-lib.sh +HT=' ' + +echo 2>/dev/null > "Name with an${HT}HT" +if ! test -f "Name with an${HT}HT" +then + # since FAT/NTFS does not allow tabs in filenames, skip this test + skip_all='Your filesystem does not allow tabs in filenames' + test_done +fi + 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 +test_expect_success 'setup' ' + 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 2>/dev/null >"$p1" "$p0" -echo 'Foo Bar Baz' >"$p2" + { cat "$p0" >"$p1" || :; } && + { echo "Foo Bar Baz" >"$p2" || :; } +' -if test -f "$p1" && cmp "$p0" "$p1" -then - test_set_prereq TABS_IN_FILENAMES -else - # since FAT/NTFS does not allow tabs in filenames, skip this test - say 'Your filesystem does not allow tabs in filenames' -fi +test_expect_success 'setup: populate index and tree' ' + git update-index --add "$p0" "$p2" && + t0=$(git write-tree) +' + +test_expect_success 'ls-files prints space in filename verbatim' ' + printf "%s\n" "just space" no-funny >expected && + git ls-files >current && + test_cmp expected current +' -test_expect_success TABS_IN_FILENAMES 'setup expect' " -echo 'just space -no-funny' >expected -" +test_expect_success 'setup: add funny filename' ' + git update-index --add "$p1" && + t1=$(git write-tree) +' -test_expect_success TABS_IN_FILENAMES 'git ls-files no-funny' \ - 'git update-index --add "$p0" "$p2" && +test_expect_success 'ls-files quotes funny filename' ' + cat >expected <<-\EOF && + just space + no-funny + "tabs\t,\" (dq) and spaces" + EOF git ls-files >current && - test_cmp expected current' + test_cmp expected current +' -test_expect_success TABS_IN_FILENAMES 'setup expect' ' -t0=`git write-tree` && -echo "$t0" >t0 && +test_expect_success 'ls-files -z does not quote funny filename' ' + cat >expected <<-\EOF && + just space + no-funny + tabs ," (dq) and spaces + EOF + git ls-files -z >ls-files.z && + perl -pe "y/\000/\012/" <ls-files.z >current && + test_cmp expected current +' -cat > expected <<\EOF -just space -no-funny -"tabs\t,\" (dq) and spaces" -EOF +test_expect_success 'ls-tree quotes funny filename' ' + cat >expected <<-\EOF && + just space + no-funny + "tabs\t,\" (dq) and spaces" + EOF + git ls-tree -r $t1 >ls-tree && + sed -e "s/^[^ ]* //" <ls-tree >current && + test_cmp expected current ' -test_expect_success TABS_IN_FILENAMES 'git ls-files with-funny' \ - 'git update-index --add "$p1" && - git ls-files >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' " -echo 'just space -no-funny -tabs ,\" (dq) and spaces' >expected -" - -test_expect_success TABS_IN_FILENAMES 'git ls-files -z with-funny' \ - 'git ls-files -z | perl -pe y/\\000/\\012/ >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' ' -t1=`git write-tree` && -echo "$t1" >t1 && - -cat > expected <<\EOF -just space -no-funny -"tabs\t,\" (dq) and spaces" -EOF -' - -test_expect_success TABS_IN_FILENAMES 'git ls-tree with funny' \ - 'git ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' ' -cat > expected <<\EOF -A "tabs\t,\" (dq) and spaces" -EOF -' - -test_expect_success TABS_IN_FILENAMES 'git diff-index with-funny' \ - 'git diff-index --name-status $t0 >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'git diff-tree with-funny' \ - 'git diff-tree --name-status $t0 $t1 >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' " -echo 'A -tabs ,\" (dq) and spaces' >expected -" - -test_expect_success TABS_IN_FILENAMES 'git diff-index -z with-funny' \ - 'git diff-index -z --name-status $t0 | perl -pe y/\\000/\\012/ >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'git diff-tree -z with-funny' \ - 'git diff-tree -z --name-status $t0 $t1 | perl -pe y/\\000/\\012/ >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' ' -cat > expected <<\EOF -CNUM no-funny "tabs\t,\" (dq) and spaces" -EOF -' - -test_expect_success TABS_IN_FILENAMES '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 && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' ' -cat > expected <<\EOF -RNUM no-funny "tabs\t,\" (dq) and spaces" -EOF -' - -test_expect_success TABS_IN_FILENAMES '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 && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' ' -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 TABS_IN_FILENAMES 'git diff-tree delete with-funny' \ - 'git diff-index -M -p $t0 | - sed -e "s/index [0-9]*%/index NUM%/" >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' ' -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 TABS_IN_FILENAMES 'git diff-tree delete with-funny' \ - 'git diff-index -M -p $t0 | - sed -e "s/index [0-9]*%/index NUM%/" >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' ' -cat >expected <<\EOF - "tabs\t,\" (dq) and spaces" - 1 file changed, 0 insertions(+), 0 deletions(-) -EOF -' - -test_expect_success TABS_IN_FILENAMES 'git diff-tree rename with-funny applied' \ - 'git diff-index -M -p $t0 | - git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'setup expect' ' -cat > expected <<\EOF - no-funny - "tabs\t,\" (dq) and spaces" - 2 files changed, 3 insertions(+), 3 deletions(-) -EOF -' - -test_expect_success TABS_IN_FILENAMES 'git diff-tree delete with-funny applied' \ - 'git diff-index -p $t0 | - git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current && - test_cmp expected current' - -test_expect_success TABS_IN_FILENAMES 'git apply non-git diff' \ - 'git diff-index -p $t0 | - sed -ne "/^[-+@]/p" | - git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current && - test_cmp expected current' +test_expect_success 'diff-index --name-status quotes funny filename' ' + cat >expected <<-\EOF && + A "tabs\t,\" (dq) and spaces" + EOF + git diff-index --name-status $t0 >current && + test_cmp expected current +' + +test_expect_success 'diff-tree --name-status quotes funny filename' ' + cat >expected <<-\EOF && + A "tabs\t,\" (dq) and spaces" + EOF + git diff-tree --name-status $t0 $t1 >current && + test_cmp expected current +' + +test_expect_success 'diff-index -z does not quote funny filename' ' + cat >expected <<-\EOF && + A + tabs ," (dq) and spaces + EOF + git diff-index -z --name-status $t0 >diff-index.z && + perl -pe "y/\000/\012/" <diff-index.z >current && + test_cmp expected current +' + +test_expect_success 'diff-tree -z does not quote funny filename' ' + cat >expected <<-\EOF && + A + tabs ," (dq) and spaces + EOF + git diff-tree -z --name-status $t0 $t1 >diff-tree.z && + perl -pe y/\\000/\\012/ <diff-tree.z >current && + test_cmp expected current +' + +test_expect_success 'diff-tree --find-copies-harder quotes funny filename' ' + cat >expected <<-\EOF && + CNUM no-funny "tabs\t,\" (dq) and spaces" + EOF + git diff-tree -C --find-copies-harder --name-status $t0 $t1 >out && + sed -e "s/^C[0-9]*/CNUM/" <out >current && + test_cmp expected current +' + +test_expect_success 'setup: remove unfunny index entry' ' + git update-index --force-remove "$p0" +' + +test_expect_success 'diff-tree -M quotes funny filename' ' + cat >expected <<-\EOF && + RNUM no-funny "tabs\t,\" (dq) and spaces" + EOF + git diff-index -M --name-status $t0 >out && + sed -e "s/^R[0-9]*/RNUM/" <out >current && + test_cmp expected current +' + +test_expect_success 'diff-index -M -p quotes funny filename' ' + 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 + git diff-index -M -p $t0 >diff && + sed -e "s/index [0-9]*%/index NUM%/" <diff >current && + test_cmp expected current +' + +test_expect_success 'setup: mode change' ' + chmod +x "$p1" +' + +test_expect_success 'diff-index -M -p with mode change quotes funny filename' ' + 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 + git diff-index -M -p $t0 >diff && + sed -e "s/index [0-9]*%/index NUM%/" <diff >current && + test_cmp expected current +' + +test_expect_success 'diffstat for rename quotes funny filename' ' + cat >expected <<-\EOF && + "tabs\t,\" (dq) and spaces" + 1 file changed, 0 insertions(+), 0 deletions(-) + EOF + git diff-index -M -p $t0 >diff && + git apply --stat <diff >diffstat && + sed -e "s/|.*//" -e "s/ *\$//" <diffstat >current && + test_i18ncmp expected current +' + +test_expect_success 'numstat for rename quotes funny filename' ' + cat >expected <<-\EOF && + 0 0 "tabs\t,\" (dq) and spaces" + EOF + git diff-index -M -p $t0 >diff && + git apply --numstat <diff >current && + test_cmp expected current +' + +test_expect_success 'numstat without -M quotes funny filename' ' + cat >expected <<-\EOF && + 0 3 no-funny + 3 0 "tabs\t,\" (dq) and spaces" + EOF + git diff-index -p $t0 >diff && + git apply --numstat <diff >current && + test_cmp expected current +' + +test_expect_success 'numstat for non-git rename diff quotes funny filename' ' + cat >expected <<-\EOF && + 0 3 no-funny + 3 0 "tabs\t,\" (dq) and spaces" + EOF + git diff-index -p $t0 >git-diff && + sed -ne "/^[-+@]/p" <git-diff >diff && + git apply --numstat <diff >current && + test_cmp expected current +' test_done diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh index 4367197953..195bb97f85 100755 --- a/t/t3310-notes-merge-manual-resolve.sh +++ b/t/t3310-notes-merge-manual-resolve.sh @@ -324,7 +324,7 @@ y and z notes on 4th commit EOF git notes merge --commit && # No .git/NOTES_MERGE_* files left - test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null && + test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null && test_cmp /dev/null output && # Merge commit has pre-merge y and pre-merge z as parents test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" && @@ -386,7 +386,7 @@ test_expect_success 'redo merge of z into m (== y) with default ("manual") resol test_expect_success 'abort notes merge' ' git notes merge --abort && # No .git/NOTES_MERGE_* files left - test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null && + test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null && test_cmp /dev/null output && # m has not moved (still == y) test "$(git rev-parse refs/notes/m)" = "$(cat pre_merge_y)" && @@ -453,7 +453,7 @@ EOF # Finalize merge git notes merge --commit && # No .git/NOTES_MERGE_* files left - test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null && + test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null && test_cmp /dev/null output && # Merge commit has pre-merge y and pre-merge z as parents test "$(git rev-parse refs/notes/m^1)" = "$(cat pre_merge_y)" && @@ -542,7 +542,7 @@ EOF test_expect_success 'resolve situation by aborting the notes merge' ' git notes merge --abort && # No .git/NOTES_MERGE_* files left - test_must_fail ls .git/NOTES_MERGE_* >output 2>/dev/null && + test_might_fail ls .git/NOTES_MERGE_* >output 2>/dev/null && test_cmp /dev/null output && # m has not moved (still == w) test "$(git rev-parse refs/notes/m)" = "$(git rev-parse refs/notes/w)" && @@ -553,4 +553,23 @@ test_expect_success 'resolve situation by aborting the notes merge' ' verify_notes z ' +cat >expect_notes <<EOF +foo +bar +EOF + +test_expect_success 'switch cwd before committing notes merge' ' + git notes add -m foo HEAD && + git notes --ref=other add -m bar HEAD && + test_must_fail git notes merge refs/notes/other && + ( + cd .git/NOTES_MERGE_WORKTREE && + echo "foo" > $(git rev-parse HEAD) && + echo "bar" >> $(git rev-parse HEAD) && + git notes merge --commit + ) && + git notes show HEAD > actual_notes && + test_cmp expect_notes actual_notes +' + test_done diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh index e647272a01..ebf93b0695 100755 --- a/t/t3400-rebase.sh +++ b/t/t3400-rebase.sh @@ -40,13 +40,6 @@ test_expect_success 'prepare repository with topic branches' ' echo Side >>C && git add C && git commit -m "Add C" && - git checkout -b nonlinear my-topic-branch && - echo Edit >>B && - git add B && - git commit -m "Modify B" && - git merge side && - git checkout -b upstream-merged-nonlinear && - git merge master && git checkout -f my-topic-branch && git tag topic ' @@ -66,26 +59,15 @@ test_expect_success 'rebase against master' ' git rebase master ' -test_expect_success 'rebase against master twice' ' - git rebase master >out && - grep "Current branch my-topic-branch is up to date" out -' - -test_expect_success 'rebase against master twice with --force' ' - git rebase --force-rebase master >out && - grep "Current branch my-topic-branch is up to date, rebase forced" out -' - -test_expect_success 'rebase against master twice from another branch' ' - git checkout my-topic-branch^ && - git rebase master my-topic-branch >out && - grep "Current branch my-topic-branch is up to date" out -' - -test_expect_success 'rebase fast-forward to master' ' - git checkout my-topic-branch^ && - git rebase my-topic-branch >out && - grep "Fast-forwarded HEAD to my-topic-branch" out +test_expect_success 'rebase, with <onto> and <upstream> specified as :/quuxery' ' + test_when_finished "git branch -D torebase" && + git checkout -b torebase my-topic-branch^ && + upstream=$(git rev-parse ":/Add B") && + onto=$(git rev-parse ":/Add A") && + git rebase --onto $onto $upstream && + git reset --hard my-topic-branch^ && + git rebase --onto ":/Add A" ":/Add B" && + git checkout my-topic-branch ' test_expect_success 'the rebase operation should not have destroyed author information' ' @@ -101,29 +83,14 @@ test_expect_success 'HEAD was detached during rebase' ' test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1}) ' -test_expect_success 'rebase after merge master' ' - git reset --hard topic && - git merge master && - git rebase master && - ! (git show | grep "^Merge:") -' - -test_expect_success 'rebase of history with merges is linearized' ' - git checkout nonlinear && - test 4 = $(git rev-list master.. | wc -l) && - git rebase master && - test 3 = $(git rev-list master.. | wc -l) -' - -test_expect_success 'rebase of history with merges after upstream merge is linearized' ' - git checkout upstream-merged-nonlinear && - test 5 = $(git rev-list master.. | wc -l) && - git rebase master && - test 3 = $(git rev-list master.. | wc -l) +test_expect_success 'rebase from ambiguous branch name' ' + git checkout -b topic side && + git rebase master ' test_expect_success 'rebase a single mode change' ' git checkout master && + git branch -D topic && echo 1 >X && git add X && test_tick && @@ -138,8 +105,7 @@ test_expect_success 'rebase a single mode change' ' ' test_expect_success 'rebase is not broken by diff.renames' ' - git config diff.renames copies && - test_when_finished "git config --unset diff.renames" && + test_config diff.renames copies && git checkout filemove && GIT_TRACE=1 git rebase force-3way ' @@ -160,14 +126,12 @@ rm -f B test_expect_success 'fail when upstream arg is missing and not on branch' ' git checkout topic && - test_must_fail git rebase >output.out && - grep "You are not currently on a branch" output.out + test_must_fail git rebase ' test_expect_success 'fail when upstream arg is missing and not configured' ' git checkout -b no-config topic && - test_must_fail git rebase >output.out && - grep "branch.no-config.merge" output.out + test_must_fail git rebase ' test_expect_success 'default to @{upstream} when upstream arg is missing' ' @@ -181,7 +145,7 @@ test_expect_success 'default to @{upstream} when upstream arg is missing' ' test_expect_success 'rebase -q is quiet' ' git checkout -b quiet topic && git rebase -q master >output.out 2>&1 && - test ! -s output.out + test_must_be_empty output.out ' test_expect_success 'Rebase a commit that sprinkles CRs in' ' diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh deleted file mode 100755 index 7ba17974c5..0000000000 --- a/t/t3401-rebase-partial.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/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' ' - test_commit A && - git checkout -b my-topic-branch && - test_commit B && - test_commit C && - git checkout -f master && - test_commit A2 A.t -' - -test_expect_success 'pick top patch from topic branch into master' ' - git cherry-pick C && - 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_path_is_missing .git/rebase-apply -' - -test_expect_success 'rebase --merge topic branch that was partially merged upstream' ' - git reset --hard C && - git rebase --merge master && - test_path_is_missing .git/rebase-merge -' - -test_done diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh index 826500bd18..3968020e64 100755 --- a/t/t3403-rebase-skip.sh +++ b/t/t3403-rebase-skip.sh @@ -64,10 +64,11 @@ test_expect_success 'rebase with --merge' ' test_expect_success 'rebase --skip with --merge' ' git rebase --skip - ' +' -test_expect_success 'merge and reference trees equal' \ - 'test -z "`git diff-tree skip-merge skip-reference`"' +test_expect_success 'merge and reference trees equal' ' + test -z "`git diff-tree skip-merge skip-reference`" +' test_expect_success 'moved back to branch correctly' ' test refs/heads/skip-merge = $(git symbolic-ref HEAD) diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index b981572d73..50e22b1cad 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -29,14 +29,6 @@ Initial setup: . "$TEST_DIRECTORY"/lib-rebase.sh -test_cmp_rev () { - git rev-parse --verify "$1" >expect.rev && - git rev-parse --verify "$2" >actual.rev && - test_cmp expect.rev actual.rev -} - -set_fake_editor - # WARNING: Modifications to the initial repository can change the SHA ID used # in the expect2 file for the 'stop on conflicting pick' test. @@ -78,6 +70,7 @@ export SHELL test_expect_success 'rebase -i with the exec command' ' git checkout master && ( + set_fake_editor && FAKE_LINES="1 exec_>touch-one 2 exec_>touch-two exec_false exec_>touch-three 3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" && @@ -99,6 +92,7 @@ test_expect_success 'rebase -i with the exec command' ' test_expect_success 'rebase -i with the exec command runs from tree root' ' git checkout master && mkdir subdir && (cd subdir && + set_fake_editor && FAKE_LINES="1 exec_>touch-subdir" \ git rebase -i HEAD^ ) && @@ -109,6 +103,7 @@ test_expect_success 'rebase -i with the exec command runs from tree root' ' test_expect_success 'rebase -i with the exec command checks tree cleanness' ' git checkout master && ( + set_fake_editor && FAKE_LINES="exec_echo_foo_>file1 1" && export FAKE_LINES && test_must_fail git rebase -i HEAD^ @@ -118,8 +113,21 @@ test_expect_success 'rebase -i with the exec command checks tree cleanness' ' git rebase --continue ' +test_expect_success 'rebase -i with exec of inexistent command' ' + git checkout master && + test_when_finished "git rebase --abort" && + ( + set_fake_editor && + FAKE_LINES="exec_this-command-does-not-exist 1" && + export FAKE_LINES && + test_must_fail git rebase -i HEAD^ >actual 2>&1 + ) && + ! grep "Maybe git-rebase is broken" actual +' + test_expect_success 'no changes are a nop' ' git checkout branch2 && + set_fake_editor && git rebase -i F && test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" && test $(git rev-parse I) = $(git rev-parse HEAD) @@ -129,6 +137,7 @@ test_expect_success 'test the [branch] option' ' git checkout -b dead-end && git rm file6 && git commit -m "stop here" && + set_fake_editor && git rebase -i F branch2 && test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" && test $(git rev-parse I) = $(git rev-parse branch2) && @@ -137,6 +146,7 @@ test_expect_success 'test the [branch] option' ' test_expect_success 'test --onto <branch>' ' git checkout -b test-onto branch2 && + set_fake_editor && git rebase -i --onto branch1 F && test "$(git symbolic-ref -q HEAD)" = "refs/heads/test-onto" && test $(git rev-parse HEAD^) = $(git rev-parse branch1) && @@ -146,6 +156,7 @@ test_expect_success 'test --onto <branch>' ' test_expect_success 'rebase on top of a non-conflicting commit' ' git checkout branch1 && git tag original-branch1 && + set_fake_editor && git rebase -i branch2 && test file6 = $(git diff --name-only original-branch1) && test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" && @@ -158,6 +169,7 @@ test_expect_success 'reflog for the branch shows state before rebase' ' ' test_expect_success 'exchange two commits' ' + set_fake_editor && FAKE_LINES="2 1" git rebase -i HEAD~2 && test H = $(git cat-file commit HEAD^ | sed -ne \$p) && test G = $(git cat-file commit HEAD | sed -ne \$p) @@ -183,6 +195,7 @@ EOF test_expect_success 'stop on conflicting pick' ' git tag new-branch1 && + set_fake_editor && test_must_fail git rebase -i master && test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" && test_cmp expect .git/rebase-merge/patch && @@ -203,6 +216,7 @@ test_expect_success 'abort' ' test_expect_success 'abort with error when new base cannot be checked out' ' git rm --cached file1 && git commit -m "remove file in base" && + set_fake_editor && test_must_fail git rebase -i master > output 2>&1 && grep "The following untracked working tree files would be overwritten by checkout:" \ output && @@ -217,6 +231,7 @@ test_expect_success 'retain authorship' ' test_tick && GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" && git tag twerp && + set_fake_editor && git rebase -i --onto master HEAD^ && git show HEAD | grep "^Author: Twerp Snog" ' @@ -227,6 +242,7 @@ test_expect_success 'squash' ' test_tick && GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 && echo "******************************" && + set_fake_editor && FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \ git rebase -i --onto master HEAD~2 && test B = $(cat file7) && @@ -239,6 +255,7 @@ test_expect_success 'retain authorship when squashing' ' test_expect_success '-p handles "no changes" gracefully' ' HEAD=$(git rev-parse HEAD) && + set_fake_editor && git rebase -i -p HEAD^ && git update-index --refresh && git diff-files --quiet && @@ -247,6 +264,8 @@ test_expect_success '-p handles "no changes" gracefully' ' ' test_expect_failure 'exchange two commits with -p' ' + git checkout H && + set_fake_editor && FAKE_LINES="2 1" git rebase -i -p HEAD~2 && test H = $(git cat-file commit HEAD^ | sed -ne \$p) && test G = $(git cat-file commit HEAD | sed -ne \$p) @@ -281,6 +300,7 @@ test_expect_success 'preserve merges with -p' ' git commit -m M file1 && git checkout -b to-be-rebased && test_tick && + set_fake_editor && git rebase -i -p --onto branch1 master && git update-index --refresh && git diff-files --quiet && @@ -295,6 +315,7 @@ test_expect_success 'preserve merges with -p' ' ' test_expect_success 'edit ancestor with -p' ' + set_fake_editor && FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 && echo 2 > unrelated-file && test_tick && @@ -308,6 +329,7 @@ test_expect_success 'edit ancestor with -p' ' test_expect_success '--continue tries to commit' ' test_tick && + set_fake_editor && test_must_fail git rebase -i --onto new-branch1 HEAD^ && echo resolved > file1 && git add file1 && @@ -319,15 +341,17 @@ test_expect_success '--continue tries to commit' ' test_expect_success 'verbose flag is heeded, even after --continue' ' git reset --hard master@{1} && test_tick && + set_fake_editor && test_must_fail git rebase -v -i --onto new-branch1 HEAD^ && echo resolved > file1 && git add file1 && git rebase --continue > output && - grep "^ file1 | 2 +-$" output + grep "^ file1 | 2 +-$" output ' test_expect_success 'multi-squash only fires up editor once' ' base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \ EXPECT_HEADER_COUNT=4 \ git rebase -i $base && @@ -338,6 +362,7 @@ test_expect_success 'multi-squash only fires up editor once' ' test_expect_success 'multi-fixup does not fire up editor' ' git checkout -b multi-fixup E && base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \ git rebase -i $base && test $base = $(git rev-parse HEAD^) && @@ -349,6 +374,7 @@ test_expect_success 'multi-fixup does not fire up editor' ' test_expect_success 'commit message used after conflict' ' git checkout -b conflict-fixup conflict-branch && base=$(git rev-parse HEAD~4) && + set_fake_editor && ( FAKE_LINES="1 fixup 3 fixup 4" && export FAKE_LINES && @@ -367,6 +393,7 @@ test_expect_success 'commit message used after conflict' ' test_expect_success 'commit message retained after conflict' ' git checkout -b conflict-squash conflict-branch && base=$(git rev-parse HEAD~4) && + set_fake_editor && ( FAKE_LINES="1 fixup 3 squash 4" && export FAKE_LINES && @@ -393,6 +420,7 @@ EOF test_expect_success 'squash and fixup generate correct log messages' ' git checkout -b squash-fixup E && base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \ EXPECT_HEADER_COUNT=4 \ git rebase -i $base && @@ -405,6 +433,7 @@ test_expect_success 'squash and fixup generate correct log messages' ' test_expect_success 'squash ignores comments' ' git checkout -b skip-comments E && base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \ EXPECT_HEADER_COUNT=4 \ git rebase -i $base && @@ -417,6 +446,7 @@ test_expect_success 'squash ignores comments' ' test_expect_success 'squash ignores blank lines' ' git checkout -b skip-blank-lines E && base=$(git rev-parse HEAD~4) && + set_fake_editor && FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \ EXPECT_HEADER_COUNT=4 \ git rebase -i $base && @@ -429,6 +459,7 @@ test_expect_success 'squash ignores blank lines' ' test_expect_success 'squash works as expected' ' git checkout -b squash-works no-conflict-branch && one=$(git rev-parse HEAD~3) && + set_fake_editor && FAKE_LINES="1 squash 3 2" EXPECT_HEADER_COUNT=2 \ git rebase -i HEAD~3 && test $one = $(git rev-parse HEAD~2) @@ -437,6 +468,7 @@ test_expect_success 'squash works as expected' ' test_expect_success 'interrupted squash works as expected' ' git checkout -b interrupted-squash conflict-branch && one=$(git rev-parse HEAD~3) && + set_fake_editor && ( FAKE_LINES="1 squash 3 2" && export FAKE_LINES && @@ -454,6 +486,7 @@ test_expect_success 'interrupted squash works as expected' ' test_expect_success 'interrupted squash works as expected (case 2)' ' git checkout -b interrupted-squash2 conflict-branch && one=$(git rev-parse HEAD~3) && + set_fake_editor && ( FAKE_LINES="3 squash 1 2" && export FAKE_LINES && @@ -471,21 +504,14 @@ test_expect_success 'interrupted squash works as expected (case 2)' ' test $one = $(git rev-parse HEAD~2) ' -test_expect_success 'ignore patch if in upstream' ' - HEAD=$(git rev-parse HEAD) && - git checkout -b has-cherry-picked HEAD^ && +test_expect_success '--continue tries to commit, even for "edit"' ' echo unrelated > file7 && git add file7 && test_tick && git commit -m "unrelated change" && - git cherry-pick $HEAD && - EXPECT_COUNT=1 git rebase -i $HEAD && - test $HEAD = $(git rev-parse HEAD^) -' - -test_expect_success '--continue tries to commit, even for "edit"' ' parent=$(git rev-parse HEAD^) && test_tick && + set_fake_editor && FAKE_LINES="edit 1" git rebase -i HEAD^ && echo edited > file7 && git add file7 && @@ -498,6 +524,7 @@ test_expect_success '--continue tries to commit, even for "edit"' ' test_expect_success 'aborted --continue does not squash commits after "edit"' ' old=$(git rev-parse HEAD) && test_tick && + set_fake_editor && FAKE_LINES="edit 1" git rebase -i HEAD^ && echo "edited again" > file7 && git add file7 && @@ -512,6 +539,7 @@ test_expect_success 'aborted --continue does not squash commits after "edit"' ' test_expect_success 'auto-amend only edited commits after "edit"' ' test_tick && + set_fake_editor && FAKE_LINES="edit 1" git rebase -i HEAD^ && echo "edited again" > file7 && git add file7 && @@ -530,6 +558,7 @@ test_expect_success 'auto-amend only edited commits after "edit"' ' test_expect_success 'clean error after failed "exec"' ' test_tick && test_when_finished "git rebase --abort || :" && + set_fake_editor && ( FAKE_LINES="1 exec_false" && export FAKE_LINES && @@ -545,6 +574,7 @@ test_expect_success 'rebase a detached HEAD' ' grandparent=$(git rev-parse HEAD~2) && git checkout $(git rev-parse HEAD) && test_tick && + set_fake_editor && FAKE_LINES="2 1" git rebase -i HEAD~2 && test $grandparent = $(git rev-parse HEAD~2) ' @@ -561,6 +591,7 @@ test_expect_success 'rebase a commit violating pre-commit' ' test_must_fail git commit -m doesnt-verify file1 && git commit -m doesnt-verify --no-verify file1 && test_tick && + set_fake_editor && FAKE_LINES=2 git rebase -i HEAD~2 ' @@ -582,6 +613,7 @@ test_expect_success 'rebase with a file named HEAD in worktree' ' git commit -m "Add body" ) && + set_fake_editor && FAKE_LINES="1 squash 2" git rebase -i to-be-rebased && test "$(git show -s --pretty=format:%an)" = "Squashed Away" @@ -593,6 +625,7 @@ test_expect_success 'do "noop" when there is nothing to cherry-pick' ' GIT_EDITOR=: git commit --amend \ --author="Somebody else <somebody@else.com>" && test $(git rev-parse branch3) != $(git rev-parse branch4) && + set_fake_editor && git rebase -i branch3 && test $(git rev-parse branch3) = $(git rev-parse branch4) @@ -617,18 +650,53 @@ test_expect_success 'submodule rebase setup' ' git commit -a -m "submodule second" ) && test_tick && + set_fake_editor && git commit -a -m "Three changes submodule" ' test_expect_success 'submodule rebase -i' ' + set_fake_editor && FAKE_LINES="1 squash 2 3" git rebase -i A ' +test_expect_success 'submodule conflict setup' ' + git tag submodule-base && + git checkout HEAD^ && + ( + cd sub && git checkout HEAD^ && echo 4 >elif && + git add elif && git commit -m "submodule conflict" + ) && + git add sub && + test_tick && + git commit -m "Conflict in submodule" && + git tag submodule-topic +' + +test_expect_success 'rebase -i continue with only submodule staged' ' + set_fake_editor && + test_must_fail git rebase -i submodule-base && + git add sub && + git rebase --continue && + test $(git rev-parse submodule-base) != $(git rev-parse HEAD) +' + +test_expect_success 'rebase -i continue with unstaged submodule' ' + git checkout submodule-topic && + git reset --hard && + set_fake_editor && + test_must_fail git rebase -i submodule-base && + git reset && + git rebase --continue && + test $(git rev-parse submodule-base) = $(git rev-parse HEAD) +' + test_expect_success 'avoid unnecessary reset' ' git checkout master && + git reset --hard && test-chmtime =123456789 file3 && git update-index --refresh && HEAD=$(git rev-parse HEAD) && + set_fake_editor && git rebase -i HEAD~4 && test $HEAD = $(git rev-parse HEAD) && MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') && @@ -637,6 +705,7 @@ test_expect_success 'avoid unnecessary reset' ' test_expect_success 'reword' ' git checkout -b reword-branch master && + set_fake_editor && FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" git rebase -i A && git show HEAD | grep "E changed" && test $(git rev-parse master) != $(git rev-parse HEAD) && @@ -656,7 +725,8 @@ test_expect_success 'rebase -i can copy notes' ' test_commit n2 && test_commit n3 && git notes add -m"a note" n3 && - git rebase --onto n1 n2 && + set_fake_editor && + git rebase -i --onto n1 n2 && test "a note" = "$(git notes show HEAD)" ' @@ -669,6 +739,7 @@ EOF test_expect_success 'rebase -i can copy notes over a fixup' ' git reset --hard n3 && git notes add -m"an earlier note" n2 && + set_fake_editor && GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 && git notes show > output && test_cmp expect output @@ -678,6 +749,7 @@ test_expect_success 'rebase while detaching HEAD' ' git symbolic-ref HEAD && grandparent=$(git rev-parse HEAD~2) && test_tick && + set_fake_editor && FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 && test $grandparent = $(git rev-parse HEAD~2) && test_must_fail git symbolic-ref HEAD @@ -687,6 +759,7 @@ test_tick # Ensure that the rebased commits get a different timestamp. test_expect_success 'always cherry-pick with --no-ff' ' git checkout no-ff-branch && git tag original-no-ff-branch && + set_fake_editor && git rebase -i --no-ff A && touch empty && for p in 0 1 2 @@ -719,9 +792,286 @@ test_expect_success 'set up commits with funny messages' ' test_expect_success 'rebase-i history with funny messages' ' git rev-list A..funny >expect && test_tick && + set_fake_editor && FAKE_LINES="1 2 3 4" git rebase -i A && git rev-list A.. >actual && test_cmp expect actual ' + +test_expect_success 'prepare for rebase -i --exec' ' + git checkout master && + git checkout -b execute && + test_commit one_exec main.txt one_exec && + test_commit two_exec main.txt two_exec && + test_commit three_exec main.txt three_exec +' + + +test_expect_success 'running "git rebase -i --exec git show HEAD"' ' + set_fake_editor && + git rebase -i --exec "git show HEAD" HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,9d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'running "git rebase --exec git show HEAD -i"' ' + git reset --hard execute && + set_fake_editor && + git rebase --exec "git show HEAD" -i HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,9d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'running "git rebase -ix git show HEAD"' ' + git reset --hard execute && + set_fake_editor && + git rebase -ix "git show HEAD" HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,9d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'rebase -ix with several <CMD>' ' + git reset --hard execute && + set_fake_editor && + git rebase -ix "git show HEAD; pwd" HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,9d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'rebase -ix with several instances of --exec' ' + git reset --hard execute && + set_fake_editor && + git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2 + exec_git_show_HEAD exec_pwd" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,11d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'rebase -ix with --autosquash' ' + git reset --hard execute && + git checkout -b autosquash && + echo second >second.txt && + git add second.txt && + git commit -m "fixup! two_exec" && + echo bis >bis.txt && + git add bis.txt && + git commit -m "fixup! two_exec" && + set_fake_editor && + ( + git checkout -b autosquash_actual && + git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual + ) && + git checkout autosquash && + ( + git checkout -b autosquash_expected && + FAKE_LINES="1 fixup 3 fixup 4 exec_git_show_HEAD 2 exec_git_show_HEAD" && + export FAKE_LINES && + git rebase -i HEAD~4 >expect + ) && + sed -e "1,13d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'rebase --exec without -i shows error message' ' + git reset --hard execute && + set_fake_editor && + test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual && + echo "The --exec option must be used with the --interactive option" >expected && + test_i18ncmp expected actual +' + + +test_expect_success 'rebase -i --exec without <CMD>' ' + git reset --hard execute && + set_fake_editor && + test_must_fail git rebase -i --exec 2>tmp && + sed -e "1d" tmp >actual && + test_must_fail git rebase -h >expected && + test_cmp expected actual && + git checkout master +' + +test_expect_success 'rebase -i --root re-order and drop commits' ' + git checkout E && + set_fake_editor && + FAKE_LINES="3 1 2 5" git rebase -i --root && + test E = $(git cat-file commit HEAD | sed -ne \$p) && + test B = $(git cat-file commit HEAD^ | sed -ne \$p) && + test A = $(git cat-file commit HEAD^^ | sed -ne \$p) && + test C = $(git cat-file commit HEAD^^^ | sed -ne \$p) && + test 0 = $(git cat-file commit HEAD^^^ | grep -c ^parent\ ) +' + +test_expect_success 'rebase -i --root retain root commit author and message' ' + git checkout A && + echo B >file7 && + git add file7 && + GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" && + set_fake_editor && + FAKE_LINES="2" git rebase -i --root && + git cat-file commit HEAD | grep -q "^author Twerp Snog" && + git cat-file commit HEAD | grep -q "^different author$" +' + +test_expect_success 'rebase -i --root temporary sentinel commit' ' + git checkout B && + ( + set_fake_editor && + FAKE_LINES="2" && + export FAKE_LINES && + test_must_fail git rebase -i --root + ) && + git cat-file commit HEAD | grep "^tree 4b825dc642cb" && + git rebase --abort +' + +test_expect_success 'rebase -i --root fixup root commit' ' + git checkout B && + set_fake_editor && + FAKE_LINES="1 fixup 2" git rebase -i --root && + test A = $(git cat-file commit HEAD | sed -ne \$p) && + test B = $(git show HEAD:file1) && + test 0 = $(git cat-file commit HEAD | grep -c ^parent\ ) +' + +test_expect_success 'rebase --edit-todo does not works on non-interactive rebase' ' + git reset --hard && + git checkout conflict-branch && + set_fake_editor && + test_must_fail git rebase --onto HEAD~2 HEAD~ && + test_must_fail git rebase --edit-todo && + git rebase --abort +' + +test_expect_success 'rebase --edit-todo can be used to modify todo' ' + git reset --hard && + git checkout no-conflict-branch^0 && + set_fake_editor && + FAKE_LINES="edit 1 2 3" git rebase -i HEAD~3 && + FAKE_LINES="2 1" git rebase --edit-todo && + git rebase --continue + test M = $(git cat-file commit HEAD^ | sed -ne \$p) && + test L = $(git cat-file commit HEAD | sed -ne \$p) +' + +test_expect_success 'rebase -i produces readable reflog' ' + git reset --hard && + git branch -f branch-reflog-test H && + set_fake_editor && + git rebase -i --onto I F branch-reflog-test && + cat >expect <<-\EOF && + rebase -i (start): checkout I + rebase -i (pick): G + rebase -i (pick): H + rebase -i (finish): returning to refs/heads/branch-reflog-test + EOF + tail -n 4 .git/logs/HEAD | + sed -e "s/.* //" >actual && + test_cmp expect actual +' + +test_expect_success 'rebase -i respects core.commentchar' ' + git reset --hard && + git checkout E^0 && + test_config core.commentchar "\\" && + write_script remove-all-but-first.sh <<-\EOF && + sed -e "2,\$s/^/\\\\/" "$1" >"$1.tmp" && + mv "$1.tmp" "$1" + EOF + test_set_editor "$(pwd)/remove-all-but-first.sh" && + git rebase -i B && + test B = $(git cat-file commit HEAD^ | sed -ne \$p) +' + +test_expect_success 'rebase -i, with <onto> and <upstream> specified as :/quuxery' ' + test_when_finished "git branch -D torebase" && + git checkout -b torebase branch1 && + upstream=$(git rev-parse ":/J") && + onto=$(git rev-parse ":/A") && + git rebase --onto $onto $upstream && + git reset --hard branch1 && + git rebase --onto ":/A" ":/J" && + git checkout branch1 +' + +test_expect_success 'rebase -i with --strategy and -X' ' + git checkout -b conflict-merge-use-theirs conflict-branch && + git reset --hard HEAD^ && + echo five >conflict && + echo Z >file1 && + git commit -a -m "one file conflict" && + EDITOR=true git rebase -i --strategy=recursive -Xours conflict-branch && + test $(git show conflict-branch:conflict) = $(cat conflict) && + test $(cat file1) = Z +' + +test_expect_success 'rebase -i error on commits with \ in message' ' + current_head=$(git rev-parse HEAD) + test_when_finished "git rebase --abort; git reset --hard $current_head; rm -f error" && + test_commit TO-REMOVE will-conflict old-content && + test_commit "\temp" will-conflict new-content dummy && + ( + EDITOR=true && + export EDITOR && + test_must_fail git rebase -i HEAD^ --onto HEAD^^ 2>error + ) && + test_expect_code 1 grep " emp" error +' + +test_expect_success 'short SHA-1 setup' ' + test_when_finished "git checkout master" && + git checkout --orphan collide && + git rm -rf . && + ( + unset test_tick && + test_commit collide1 collide && + test_commit --notick collide2 collide && + test_commit --notick collide3 collide + ) +' + +test_expect_success 'short SHA-1 collide' ' + test_when_finished "reset_rebase && git checkout master" && + git checkout collide && + ( + unset test_tick && + test_tick && + set_fake_editor && + FAKE_COMMIT_MESSAGE="collide2 ac4f2ee" \ + FAKE_LINES="reword 1 2" git rebase -i HEAD~2 + ) +' + test_done diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh index e5ad67c643..19eddadcf7 100755 --- a/t/t3405-rebase-malformed.sh +++ b/t/t3405-rebase-malformed.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='rebase should not insist on git message convention' +test_description='rebase should handle arbitrary git message' . ./test-lib.sh @@ -12,6 +12,11 @@ It has two paragraphs, but its first paragraph is not friendly to oneline summary format. EOF +cat >G <<\EOF +commit log message containing a diff +EOF + + test_expect_success setup ' >file1 && @@ -19,8 +24,9 @@ test_expect_success setup ' git add file1 file2 && test_tick && git commit -m "Initial commit" && + git branch diff-in-message - git checkout -b side && + git checkout -b multi-line-subject && cat F >file2 && git add file2 && test_tick && @@ -28,6 +34,17 @@ test_expect_success setup ' git cat-file commit HEAD | sed -e "1,/^\$/d" >F0 && + git checkout diff-in-message && + echo "commit log message containing a diff" >G && + echo "" >>G + cat G >file2 && + git add file2 && + git diff --cached >>G && + test_tick && + git commit -F G && + + git cat-file commit HEAD | sed -e "1,/^\$/d" >G0 && + git checkout master && echo One >file1 && @@ -36,13 +53,20 @@ test_expect_success setup ' git commit -m "Second commit" ' -test_expect_success rebase ' +test_expect_success 'rebase commit with multi-line subject' ' - git rebase master side && + git rebase master multi-line-subject && git cat-file commit HEAD | sed -e "1,/^\$/d" >F1 && test_cmp F0 F1 && test_cmp F F0 ' +test_expect_success 'rebase commit with diff in message' ' + git rebase master diff-in-message && + git cat-file commit HEAD | sed -e "1,/^$/d" >G1 && + test_cmp G0 G1 && + test_cmp G G0 +' + test_done diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh index fe5f936988..0392e36d23 100755 --- a/t/t3406-rebase-message.sh +++ b/t/t3406-rebase-message.sh @@ -4,27 +4,17 @@ test_description='messages from rebase operation' . ./test-lib.sh -quick_one () { - echo "$1" >"file$1" && - git add "file$1" && - test_tick && - git commit -m "$1" -} +test_expect_success 'setup' ' + test_commit O fileO && + test_commit X fileX && + test_commit A fileA && + test_commit B fileB && + test_commit Y fileY && -test_expect_success setup ' - quick_one O && - git branch topic && - quick_one X && - quick_one A && - quick_one B && - quick_one Y && - - git checkout topic && - quick_one A && - quick_one B && - quick_one Z && + git checkout -b topic O && + git cherry-pick A B && + test_commit Z fileZ && git tag start - ' cat >expect <<\EOF @@ -34,12 +24,32 @@ Committed: 0003 Z EOF test_expect_success 'rebase -m' ' - git rebase -m master >report && sed -n -e "/^Already applied: /p" \ -e "/^Committed: /p" report >actual && test_cmp expect actual +' + +test_expect_success 'rebase against master twice' ' + git rebase master >out && + test_i18ngrep "Current branch topic is up to date" out +' +test_expect_success 'rebase against master twice with --force' ' + git rebase --force-rebase master >out && + test_i18ngrep "Current branch topic is up to date, rebase forced" out +' + +test_expect_success 'rebase against master twice from another branch' ' + git checkout topic^ && + git rebase master topic >out && + test_i18ngrep "Current branch topic is up to date" out +' + +test_expect_success 'rebase fast-forward to master' ' + git checkout topic^ && + git rebase topic >out && + test_i18ngrep "Fast-forwarded HEAD to topic" out ' test_expect_success 'rebase --stat' ' @@ -62,4 +72,16 @@ test_expect_success 'rebase -n overrides config rebase.stat config' ' ! grep "^ fileX | *1 +$" diffstat.txt ' +# Output to stderr: +# +# "Does not point to a valid commit: invalid-ref" +# +# NEEDSWORK: This "grep" is fine in real non-C locales, but +# GETTEXT_POISON poisons the refname along with the enclosing +# error message. +test_expect_success 'rebase --onto outputs the invalid ref' ' + test_must_fail git rebase --onto invalid-ref HEAD HEAD 2>err && + test_i18ngrep "invalid-ref" err +' + test_done diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh index 6de4e2263f..8c251c57a6 100755 --- a/t/t3409-rebase-preserve-merges.sh +++ b/t/t3409-rebase-preserve-merges.sh @@ -11,14 +11,6 @@ Run "git rebase -p" and check that merges are properly carried along GIT_AUTHOR_EMAIL=bogus_email_address export GIT_AUTHOR_EMAIL -# Clone 1 (trivial merge): -# -# A1--A2 <-- origin/master -# \ \ -# B1--M <-- topic -# \ -# B2 <-- origin/topic -# # Clone 2 (conflicting merge): # # A1--A2--B3 <-- origin/master @@ -37,15 +29,7 @@ export GIT_AUTHOR_EMAIL # \ # B2 <-- origin/topic # -# Clone 4 (merge using second parent as base): -# -# A1--A2--B3 <-- origin/master -# \ -# B1--A3--M <-- topic -# \ / -# \--A4 <-- topic2 -# \ -# B2 <-- origin/topic +# Clone 4 (same as Clone 3) test_expect_success 'setup for merge-preserving rebase' \ 'echo First > A && @@ -58,20 +42,6 @@ test_expect_success 'setup for merge-preserving rebase' \ git checkout -f master && echo Third >> A && git commit -a -m "Modify A2" && - - git clone ./. clone1 && - (cd clone1 && - git checkout -b topic origin/topic && - git merge origin/master - ) && - - git clone ./. clone4 && - ( - cd clone4 && - git checkout -b topic origin/topic && - git merge origin/master - ) && - echo Fifth > B && git add B && git commit -m "Add different B" && @@ -96,21 +66,21 @@ test_expect_success 'setup for merge-preserving rebase' \ git merge --no-ff topic2 ) && + git clone ./. clone4 && + ( + cd clone4 && + git checkout -b topic2 origin/topic && + echo Sixth > A && + git commit -a -m "Modify A3" && + git checkout -b topic origin/topic && + git merge --no-ff topic2 + ) && + git checkout topic && echo Fourth >> B && git commit -a -m "Modify B2" ' -test_expect_success 'rebase -p fakes interactive rebase' ' - ( - cd clone1 && - git fetch && - git rebase -p origin/topic && - test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) && - test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l) - ) -' - test_expect_success '--continue works after a conflict' ' ( cd clone2 && @@ -138,14 +108,14 @@ test_expect_success 'rebase -p preserves no-ff merges' ' ) ' -test_expect_success 'rebase -p works when base inside second parent' ' +test_expect_success 'rebase -p ignores merge.log config' ' ( cd clone4 && git fetch && - git rebase -p HEAD^2 && - test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) && - test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) && - test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l) + git -c merge.log=1 rebase -p origin/topic && + echo >expected && + git log --format="%b" -1 >current && + test_cmp expected current ) ' diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh index ace8e54e9b..dc81bf27eb 100755 --- a/t/t3411-rebase-preserve-around-merges.sh +++ b/t/t3411-rebase-preserve-around-merges.sh @@ -56,6 +56,7 @@ test_expect_success 'squash F1 into D1' ' # And rebase G1..M1 onto E2 test_expect_success 'rebase two levels of merge' ' + git checkout A1 && test_commit G1 && test_commit H1 && test_commit I1 && diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh index 086c91c7b4..0b52105728 100755 --- a/t/t3412-rebase-root.sh +++ b/t/t3412-rebase-root.sh @@ -22,8 +22,9 @@ test_expect_success 'prepare repository' ' test_commit 4 B ' -test_expect_success 'rebase --root expects --onto' ' - test_must_fail git rebase --root +test_expect_success 'rebase --root fails with too many args' ' + git checkout -B fail other && + test_must_fail git rebase --onto master --root fail fail ' test_expect_success 'setup pre-rebase hook' ' @@ -42,7 +43,7 @@ cat > expect <<EOF EOF test_expect_success 'rebase --root --onto <newbase>' ' - git checkout -b work && + git checkout -b work other && git rebase --root --onto master && git log --pretty=tformat:"%s" > rebased && test_cmp expect rebased diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh index b38be8e937..41370ab998 100755 --- a/t/t3415-rebase-autosquash.sh +++ b/t/t3415-rebase-autosquash.sh @@ -4,6 +4,8 @@ test_description='auto squash' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-rebase.sh + test_expect_success setup ' echo 0 >file0 && git add . && @@ -33,7 +35,7 @@ test_auto_fixup () { test_tick && git rebase $2 -i HEAD^^^ && git log --oneline >actual && - test 3 = $(wc -l <actual) && + test_line_count = 3 actual && git diff --exit-code $1 && test 1 = "$(git cat-file blob HEAD^:file1)" && test 1 = $(git cat-file commit HEAD^ | grep first | wc -l) @@ -62,7 +64,7 @@ test_auto_squash () { test_tick && git rebase $2 -i HEAD^^^ && git log --oneline >actual && - test 3 = $(wc -l <actual) && + test_line_count = 3 actual && git diff --exit-code $1 && test 1 = "$(git cat-file blob HEAD^:file1)" && test 2 = $(git cat-file commit HEAD^ | grep first | wc -l) @@ -90,7 +92,7 @@ test_expect_success 'misspelled auto squash' ' test_tick && git rebase --autosquash -i HEAD^^^ && git log --oneline >actual && - test 4 = $(wc -l <actual) && + test_line_count = 4 actual && git diff --exit-code final-missquash && test 0 = $(git rev-list final-missquash...HEAD | wc -l) ' @@ -109,7 +111,7 @@ test_expect_success 'auto squash that matches 2 commits' ' test_tick && git rebase --autosquash -i HEAD~4 && git log --oneline >actual && - test 4 = $(wc -l <actual) && + test_line_count = 4 actual && git diff --exit-code final-multisquash && test 1 = "$(git cat-file blob HEAD^^:file1)" && test 2 = $(git cat-file commit HEAD^^ | grep first | wc -l) && @@ -130,7 +132,7 @@ test_expect_success 'auto squash that matches a commit after the squash' ' test_tick && git rebase --autosquash -i HEAD~4 && git log --oneline >actual && - test 5 = $(wc -l <actual) && + test_line_count = 5 actual && git diff --exit-code final-presquash && test 0 = "$(git cat-file blob HEAD^^:file1)" && test 1 = "$(git cat-file blob HEAD^:file1)" && @@ -147,7 +149,7 @@ test_expect_success 'auto squash that matches a sha1' ' test_tick && git rebase --autosquash -i HEAD^^^ && git log --oneline >actual && - test 3 = $(wc -l <actual) && + test_line_count = 3 actual && git diff --exit-code final-shasquash && test 1 = "$(git cat-file blob HEAD^:file1)" && test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l) @@ -163,7 +165,7 @@ test_expect_success 'auto squash that matches longer sha1' ' test_tick && git rebase --autosquash -i HEAD^^^ && git log --oneline >actual && - test 3 = $(wc -l <actual) && + test_line_count = 3 actual && git diff --exit-code final-longshasquash && test 1 = "$(git cat-file blob HEAD^:file1)" && test 1 = $(git cat-file commit HEAD^ | grep squash | wc -l) @@ -179,7 +181,7 @@ test_auto_commit_flags () { test_tick && git rebase --autosquash -i HEAD^^^ && git log --oneline >actual && - test 3 = $(wc -l <actual) && + test_line_count = 3 actual && git diff --exit-code final-commit-$1 && test 1 = "$(git cat-file blob HEAD^:file1)" && test $2 = $(git cat-file commit HEAD^ | grep first | wc -l) @@ -193,4 +195,59 @@ test_expect_success 'use commit --squash' ' test_auto_commit_flags squash 2 ' +test_auto_fixup_fixup () { + git reset --hard base && + echo 1 >file1 && + git add -u && + test_tick && + git commit -m "$1! first" && + echo 2 >file1 && + git add -u && + test_tick && + git commit -m "$1! $2! first" && + git tag "final-$1-$2" && + test_tick && + ( + set_cat_todo_editor && + test_must_fail git rebase --autosquash -i HEAD^^^^ >actual && + cat >expected <<-EOF && + pick $(git rev-parse --short HEAD^^^) first commit + $1 $(git rev-parse --short HEAD^) $1! first + $1 $(git rev-parse --short HEAD) $1! $2! first + pick $(git rev-parse --short HEAD^^) second commit + EOF + test_cmp expected actual + ) && + git rebase --autosquash -i HEAD^^^^ && + git log --oneline >actual && + test_line_count = 3 actual + git diff --exit-code "final-$1-$2" && + test 2 = "$(git cat-file blob HEAD^:file1)" && + if test "$1" = "fixup" + then + test 1 = $(git cat-file commit HEAD^ | grep first | wc -l) + elif test "$1" = "squash" + then + test 3 = $(git cat-file commit HEAD^ | grep first | wc -l) + else + false + fi +} + +test_expect_success 'fixup! fixup!' ' + test_auto_fixup_fixup fixup fixup +' + +test_expect_success 'fixup! squash!' ' + test_auto_fixup_fixup fixup squash +' + +test_expect_success 'squash! squash!' ' + test_auto_fixup_fixup squash squash +' + +test_expect_success 'squash! fixup!' ' + test_auto_fixup_fixup squash fixup +' + test_done diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh new file mode 100755 index 0000000000..90eb26493c --- /dev/null +++ b/t/t3420-rebase-autostash.sh @@ -0,0 +1,170 @@ +#!/bin/sh +# +# Copyright (c) 2013 Ramkumar Ramachandra +# + +test_description='git rebase --autostash tests' +. ./test-lib.sh + +test_expect_success setup ' + echo hello-world >file0 && + git add . && + test_tick && + git commit -m "initial commit" && + git checkout -b feature-branch && + echo another-hello >file1 && + echo goodbye >file2 && + git add . && + test_tick && + git commit -m "second commit" && + echo final-goodbye >file3 && + git add . && + test_tick && + git commit -m "third commit" && + git checkout -b unrelated-onto-branch master && + echo unrelated >file4 && + git add . && + test_tick && + git commit -m "unrelated commit" && + git checkout -b related-onto-branch master && + echo conflicting-change >file2 && + git add . && + test_tick && + git commit -m "related commit" +' + +testrebase() { + type=$1 + dotest=$2 + + test_expect_success "rebase$type: dirty worktree, non-conflicting rebase" ' + test_config rebase.autostash true && + git reset --hard && + git checkout -b rebased-feature-branch feature-branch && + test_when_finished git branch -D rebased-feature-branch && + echo dirty >>file3 && + git rebase$type unrelated-onto-branch && + grep unrelated file4 && + grep dirty file3 && + git checkout feature-branch + ' + + test_expect_success "rebase$type: dirty index, non-conflicting rebase" ' + test_config rebase.autostash true && + git reset --hard && + git checkout -b rebased-feature-branch feature-branch && + test_when_finished git branch -D rebased-feature-branch && + echo dirty >>file3 && + git add file3 && + git rebase$type unrelated-onto-branch && + grep unrelated file4 && + grep dirty file3 && + git checkout feature-branch + ' + + test_expect_success "rebase$type: conflicting rebase" ' + test_config rebase.autostash true && + git reset --hard && + git checkout -b rebased-feature-branch feature-branch && + test_when_finished git branch -D rebased-feature-branch && + echo dirty >>file3 && + test_must_fail git rebase$type related-onto-branch && + test_path_is_file $dotest/autostash && + ! grep dirty file3 && + rm -rf $dotest && + git reset --hard && + git checkout feature-branch + ' + + test_expect_success "rebase$type: --continue" ' + test_config rebase.autostash true && + git reset --hard && + git checkout -b rebased-feature-branch feature-branch && + test_when_finished git branch -D rebased-feature-branch && + echo dirty >>file3 && + test_must_fail git rebase$type related-onto-branch && + test_path_is_file $dotest/autostash && + ! grep dirty file3 && + echo "conflicting-plus-goodbye" >file2 && + git add file2 && + git rebase --continue && + test_path_is_missing $dotest/autostash && + grep dirty file3 && + git checkout feature-branch + ' + + test_expect_success "rebase$type: --skip" ' + test_config rebase.autostash true && + git reset --hard && + git checkout -b rebased-feature-branch feature-branch && + test_when_finished git branch -D rebased-feature-branch && + echo dirty >>file3 && + test_must_fail git rebase$type related-onto-branch && + test_path_is_file $dotest/autostash && + ! grep dirty file3 && + git rebase --skip && + test_path_is_missing $dotest/autostash && + grep dirty file3 && + git checkout feature-branch + ' + + test_expect_success "rebase$type: --abort" ' + test_config rebase.autostash true && + git reset --hard && + git checkout -b rebased-feature-branch feature-branch && + test_when_finished git branch -D rebased-feature-branch && + echo dirty >>file3 && + test_must_fail git rebase$type related-onto-branch && + test_path_is_file $dotest/autostash && + ! grep dirty file3 && + git rebase --abort && + test_path_is_missing $dotest/autostash && + grep dirty file3 && + git checkout feature-branch + ' + + test_expect_success "rebase$type: non-conflicting rebase, conflicting stash" ' + test_config rebase.autostash true && + git reset --hard && + git checkout -b rebased-feature-branch feature-branch && + test_when_finished git branch -D rebased-feature-branch && + echo dirty >file4 && + git add file4 && + git rebase$type unrelated-onto-branch && + test_path_is_missing $dotest && + git reset --hard && + grep unrelated file4 && + ! grep dirty file4 && + git checkout feature-branch && + git stash pop && + grep dirty file4 + ' +} + +test_expect_success "rebase: fast-forward rebase" ' + test_config rebase.autostash true && + git reset --hard && + git checkout -b behind-feature-branch feature-branch~1 && + test_when_finished git branch -D behind-feature-branch && + echo dirty >>file1 && + git rebase feature-branch && + grep dirty file1 && + git checkout feature-branch +' + +test_expect_success "rebase: noop rebase" ' + test_config rebase.autostash true && + git reset --hard && + git checkout -b same-feature-branch feature-branch && + test_when_finished git branch -D same-feature-branch && + echo dirty >>file1 && + git rebase feature-branch && + grep dirty file1 && + git checkout feature-branch +' + +testrebase "" .git/rebase-apply +testrebase " --merge" .git/rebase-merge +testrebase " --interactive" .git/rebase-merge + +test_done diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh new file mode 100755 index 0000000000..9c55cba198 --- /dev/null +++ b/t/t3421-rebase-topology-linear.sh @@ -0,0 +1,350 @@ +#!/bin/sh + +test_description='basic rebase topology tests' +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-rebase.sh + +# a---b---c +# \ +# d---e +test_expect_success 'setup' ' + test_commit a && + test_commit b && + test_commit c && + git checkout b && + test_commit d && + test_commit e +' + +test_run_rebase () { + result=$1 + shift + test_expect_$result "simple rebase $*" " + reset_rebase && + git rebase $* c e && + test_cmp_rev c HEAD~2 && + test_linear_range 'd e' c.. + " +} +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i +test_run_rebase success -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* is no-op if upstream is an ancestor" " + reset_rebase && + git rebase $* b e && + test_cmp_rev e HEAD + " +} +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i +test_run_rebase success -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* -f rewrites even if upstream is an ancestor" " + reset_rebase && + git rebase $* -f b e && + ! test_cmp_rev e HEAD && + test_cmp_rev b HEAD~2 && + test_linear_range 'd e' b.. + " +} +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i +test_run_rebase failure -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* fast-forwards from ancestor of upstream" " + reset_rebase && + git rebase $* e b && + test_cmp_rev e HEAD + " +} +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i +test_run_rebase success -p + +# f +# / +# a---b---c---g---h +# \ +# d---gp--i +# +# gp = cherry-picked g +# h = reverted g +# +# Reverted patches are there for tests to be able to check if a commit +# that introduced the same change as another commit is +# dropped. Without reverted commits, we could get false positives +# because applying the patch succeeds, but simply results in no +# changes. +test_expect_success 'setup of linear history for range selection tests' ' + git checkout c && + test_commit g && + revert h g && + git checkout d && + cherry_pick gp g && + test_commit i && + git checkout b && + test_commit f +' + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* drops patches in upstream" " + reset_rebase && + git rebase $* h i && + test_cmp_rev h HEAD~2 && + test_linear_range 'd i' h.. + " +} +test_run_rebase success '' +test_run_rebase failure -m +test_run_rebase success -i +test_run_rebase success -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* can drop last patch if in upstream" " + reset_rebase && + git rebase $* h gp && + test_cmp_rev h HEAD^ && + test_linear_range 'd' h.. + " +} +test_run_rebase success '' +test_run_rebase failure -m +test_run_rebase success -i +test_run_rebase success -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* --onto drops patches in upstream" " + reset_rebase && + git rebase $* --onto f h i && + test_cmp_rev f HEAD~2 && + test_linear_range 'd i' f.. + " +} +test_run_rebase success '' +test_run_rebase failure -m +test_run_rebase success -i +test_run_rebase success -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* --onto does not drop patches in onto" " + reset_rebase && + git rebase $* --onto h f i && + test_cmp_rev h HEAD~3 && + test_linear_range 'd gp i' h.. + " +} +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i +test_run_rebase success -p + +# a---b---c---j! +# \ +# d---k!--l +# +# ! = empty +test_expect_success 'setup of linear history for empty commit tests' ' + git checkout c && + make_empty j && + git checkout d && + make_empty k && + test_commit l +' + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* drops empty commit" " + reset_rebase && + git rebase $* c l && + test_cmp_rev c HEAD~2 && + test_linear_range 'd l' c.. + " +} +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i +test_run_rebase success -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* --keep-empty" " + reset_rebase && + git rebase $* --keep-empty c l && + test_cmp_rev c HEAD~3 && + test_linear_range 'd k l' c.. + " +} +test_run_rebase success '' +test_run_rebase failure -m +test_run_rebase success -i +test_run_rebase failure -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* --keep-empty keeps empty even if already in upstream" " + reset_rebase && + git rebase $* --keep-empty j l && + test_cmp_rev j HEAD~3 && + test_linear_range 'd k l' j.. + " +} +test_run_rebase success '' +test_run_rebase failure -m +test_run_rebase failure -i +test_run_rebase failure -p + +# m +# / +# a---b---c---g +# +# x---y---bp +# +# bp = cherry-picked b +# m = reverted b +# +# Reverted patches are there for tests to be able to check if a commit +# that introduced the same change as another commit is +# dropped. Without reverted commits, we could get false positives +# because applying the patch succeeds, but simply results in no +# changes. +test_expect_success 'setup of linear history for test involving root' ' + git checkout b && + revert m b && + git checkout --orphan disjoint && + git rm -rf . && + test_commit x && + test_commit y && + cherry_pick bp b +' + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* --onto --root" " + reset_rebase && + git rebase $* --onto c --root y && + test_cmp_rev c HEAD~2 && + test_linear_range 'x y' c.. + " +} +test_run_rebase success '' +test_run_rebase failure -m +test_run_rebase success -i +test_run_rebase success -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* without --onto --root with disjoint history" " + reset_rebase && + git rebase $* c y && + test_cmp_rev c HEAD~2 && + test_linear_range 'x y' c.. + " +} +test_run_rebase success '' +test_run_rebase failure -m +test_run_rebase success -i +test_run_rebase failure -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* --onto --root drops patch in onto" " + reset_rebase && + git rebase $* --onto m --root bp && + test_cmp_rev m HEAD~2 && + test_linear_range 'x y' m.. + " +} +test_run_rebase success '' +test_run_rebase failure -m +test_run_rebase success -i +test_run_rebase success -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* --onto --root with merge-base does not go to root" " + reset_rebase && + git rebase $* --onto m --root g && + test_cmp_rev m HEAD~2 && + test_linear_range 'c g' m.. + " +} + +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i +test_run_rebase failure -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* without --onto --root with disjoint history drops patch in onto" " + reset_rebase && + git rebase $* m bp && + test_cmp_rev m HEAD~2 && + test_linear_range 'x y' m.. + " +} +test_run_rebase success '' +test_run_rebase failure -m +test_run_rebase success -i +test_run_rebase failure -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* --root on linear history is a no-op" " + reset_rebase && + git rebase $* --root c && + test_cmp_rev c HEAD + " +} +test_run_rebase failure '' +test_run_rebase failure -m +test_run_rebase failure -i +test_run_rebase failure -p + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* -f --root on linear history causes re-write" " + reset_rebase && + git rebase $* -f --root c && + ! test_cmp_rev a HEAD~2 && + test_linear_range 'a b c' HEAD + " +} +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i +test_run_rebase success -p + +test_done diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh new file mode 100755 index 0000000000..1d195fbd64 --- /dev/null +++ b/t/t3425-rebase-topology-merges.sh @@ -0,0 +1,258 @@ +#!/bin/sh + +test_description='rebase topology tests with merges' +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-rebase.sh + +test_revision_subjects () { + expected="$1" + shift + set -- $(git log --format=%s --no-walk=unsorted "$@") + test "$expected" = "$*" +} + +# a---b-----------c +# \ \ +# d-------e \ +# \ \ \ +# n---o---w---v +# \ +# z +test_expect_success 'setup of non-linear-history' ' + test_commit a && + test_commit b && + test_commit c && + git checkout b && + test_commit d && + test_commit e + + git checkout c && + test_commit g && + revert h g && + git checkout d && + cherry_pick gp g && + test_commit i && + git checkout b && + test_commit f + + git checkout d && + test_commit n && + test_commit o && + test_merge w e && + test_merge v c && + git checkout o && + test_commit z +' + +test_run_rebase () { + result=$1 + shift + test_expect_$result "rebase $* after merge from upstream" " + reset_rebase && + git rebase $* e w && + test_cmp_rev e HEAD~2 && + test_linear_range 'n o' e.. + " +} +test_run_rebase success '' +test_run_rebase success -m +test_run_rebase success -i + +test_run_rebase () { + result=$1 + shift + expected=$1 + shift + test_expect_$result "rebase $* of non-linear history is linearized in place" " + reset_rebase && + git rebase $* d w && + test_cmp_rev d HEAD~3 && + test_linear_range "\'"$expected"\'" d.. + " +} +#TODO: make order consistent across all flavors of rebase +test_run_rebase success 'e n o' '' +test_run_rebase success 'e n o' -m +test_run_rebase success 'n o e' -i + +test_run_rebase () { + result=$1 + shift + expected=$1 + shift + test_expect_$result "rebase $* of non-linear history is linearized upstream" " + reset_rebase && + git rebase $* c w && + test_cmp_rev c HEAD~4 && + test_linear_range "\'"$expected"\'" c.. + " +} +#TODO: make order consistent across all flavors of rebase +test_run_rebase success 'd e n o' '' +test_run_rebase success 'd e n o' -m +test_run_rebase success 'd n o e' -i + +test_run_rebase () { + result=$1 + shift + expected=$1 + shift + test_expect_$result "rebase $* of non-linear history with merges after upstream merge is linearized" " + reset_rebase && + git rebase $* c v && + test_cmp_rev c HEAD~4 && + test_linear_range "\'"$expected"\'" c.. + " +} +#TODO: make order consistent across all flavors of rebase +test_run_rebase success 'd e n o' '' +test_run_rebase success 'd e n o' -m +test_run_rebase success 'd n o e' -i + +test_expect_success "rebase -p is no-op in non-linear history" " + reset_rebase && + git rebase -p d w && + test_cmp_rev w HEAD +" + +test_expect_success "rebase -p is no-op when base inside second parent" " + reset_rebase && + git rebase -p e w && + test_cmp_rev w HEAD +" + +test_expect_failure "rebase -p --root on non-linear history is a no-op" " + reset_rebase && + git rebase -p --root w && + test_cmp_rev w HEAD +" + +test_expect_success "rebase -p re-creates merge from side branch" " + reset_rebase && + git rebase -p z w && + test_cmp_rev z HEAD^ && + test_cmp_rev w^2 HEAD^2 +" + +test_expect_success "rebase -p re-creates internal merge" " + reset_rebase && + git rebase -p c w && + test_cmp_rev c HEAD~4 && + test_cmp_rev HEAD^2^ HEAD~3 && + test_revision_subjects 'd n e o w' HEAD~3 HEAD~2 HEAD^2 HEAD^ HEAD +" + +test_expect_success "rebase -p can re-create two branches on onto" " + reset_rebase && + git rebase -p --onto c d w && + test_cmp_rev c HEAD~3 && + test_cmp_rev c HEAD^2^ && + test_revision_subjects 'n e o w' HEAD~2 HEAD^2 HEAD^ HEAD +" + +# f +# / +# a---b---c---g---h +# \ +# d---gp--i +# \ \ +# e-------u +# +# gp = cherry-picked g +# h = reverted g +test_expect_success 'setup of non-linear-history for patch-equivalence tests' ' + git checkout e && + test_merge u i +' + +test_expect_success "rebase -p re-creates history around dropped commit matching upstream" " + reset_rebase && + git rebase -p h u && + test_cmp_rev h HEAD~3 && + test_cmp_rev HEAD^2^ HEAD~2 && + test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD +" + +test_expect_success "rebase -p --onto in merged history drops patches in upstream" " + reset_rebase && + git rebase -p --onto f h u && + test_cmp_rev f HEAD~3 && + test_cmp_rev HEAD^2^ HEAD~2 && + test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD +" + +test_expect_success "rebase -p --onto in merged history does not drop patches in onto" " + reset_rebase && + git rebase -p --onto h f u && + test_cmp_rev h HEAD~3 && + test_cmp_rev HEAD^2~2 HEAD~2 && + test_revision_subjects 'd gp i e u' HEAD~2 HEAD^2^ HEAD^2 HEAD^ HEAD +" + +# a---b---c---g---h +# \ +# d---gp--s +# \ \ / +# \ X +# \ / \ +# e---t +# +# gp = cherry-picked g +# h = reverted g +test_expect_success 'setup of non-linear-history for dropping whole side' ' + git checkout gp && + test_merge s e && + git checkout e && + test_merge t gp +' + +test_expect_failure "rebase -p drops merge commit when entire first-parent side is dropped" " + reset_rebase && + git rebase -p h s && + test_cmp_rev h HEAD~2 && + test_linear_range 'd e' h.. +" + +test_expect_success "rebase -p drops merge commit when entire second-parent side is dropped" " + reset_rebase && + git rebase -p h t && + test_cmp_rev h HEAD~2 && + test_linear_range 'd e' h.. +" + +# a---b---c +# \ +# d---e +# \ \ +# n---r +# \ +# o +# +# r = tree-same with n +test_expect_success 'setup of non-linear-history for empty commits' ' + git checkout n && + git merge --no-commit e && + git reset n . && + git commit -m r && + git reset --hard && + git clean -f && + git tag r +' + +test_expect_success "rebase -p re-creates empty internal merge commit" " + reset_rebase && + git rebase -p c r && + test_cmp_rev c HEAD~3 && + test_cmp_rev HEAD^2^ HEAD~2 && + test_revision_subjects 'd e n r' HEAD~2 HEAD^2 HEAD^ HEAD +" + +test_expect_success "rebase -p re-creates empty merge commit" " + reset_rebase && + git rebase -p o r && + test_cmp_rev e HEAD^2 && + test_cmp_rev o HEAD^ && + test_revision_subjects 'r' HEAD +" + +test_done diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh index 595d2ff990..51f3bbb8af 100755 --- a/t/t3501-revert-cherry-pick.sh +++ b/t/t3501-revert-cherry-pick.sh @@ -47,7 +47,7 @@ test_expect_success 'cherry-pick --nonsense' ' git diff --exit-code HEAD && test_must_fail git cherry-pick --nonsense 2>msg && git diff --exit-code HEAD "$pos" && - grep '[Uu]sage:' msg + test_i18ngrep '[Uu]sage:' msg ' test_expect_success 'revert --nonsense' ' @@ -56,7 +56,7 @@ test_expect_success 'revert --nonsense' ' git diff --exit-code HEAD && test_must_fail git revert --nonsense 2>msg && git diff --exit-code HEAD "$pos" && - grep '[Uu]sage:' msg + test_i18ngrep '[Uu]sage:' msg ' test_expect_success 'cherry-pick after renaming branch' ' @@ -100,4 +100,45 @@ test_expect_success 'revert forbidden on dirty working tree' ' ' +test_expect_success 'cherry-pick on unborn branch' ' + git checkout --orphan unborn && + git rm --cached -r . && + rm -rf * && + git cherry-pick initial && + git diff --quiet initial && + ! test_cmp_rev initial HEAD +' + +test_expect_success 'cherry-pick "-" to pick from previous branch' ' + git checkout unborn && + test_commit to-pick actual content && + git checkout master && + git cherry-pick - && + echo content >expect && + test_cmp expect actual +' + +test_expect_success 'cherry-pick "-" is meaningless without checkout' ' + test_create_repo afresh && + ( + cd afresh && + test_commit one && + test_commit two && + test_commit three && + test_must_fail git cherry-pick - + ) +' + +test_expect_success 'cherry-pick "-" works with arguments' ' + git checkout -b side-branch && + test_commit change actual change && + git checkout master && + git cherry-pick -s - && + echo "Signed-off-by: C O Mitter <committer@example.com>" >expect && + git cat-file commit HEAD | grep ^Signed-off-by: >signoff && + test_cmp expect signoff && + echo change >expect && + test_cmp expect actual +' + test_done diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh index c10b28cf57..fbdc47cfbd 100755 --- a/t/t3505-cherry-pick-empty.sh +++ b/t/t3505-cherry-pick-empty.sh @@ -18,34 +18,84 @@ test_expect_success setup ' echo third >> file1 && git add file1 && test_tick && - git commit --allow-empty-message -m "" + git commit --allow-empty-message -m "" && + + git checkout master && + git checkout -b empty-branch2 && + test_tick && + git commit --allow-empty -m "empty" ' test_expect_success 'cherry-pick an empty commit' ' - git checkout master && { - git cherry-pick empty-branch^ - test "$?" = 1 - } + git checkout master && + test_expect_code 1 git cherry-pick empty-branch^ ' test_expect_success 'index lockfile was removed' ' - test ! -f .git/index.lock - ' test_expect_success 'cherry-pick a commit with an empty message' ' - git checkout master && { - git cherry-pick empty-branch - test "$?" = 1 - } + git checkout master && + test_expect_code 1 git cherry-pick empty-branch ' test_expect_success 'index lockfile was removed' ' - test ! -f .git/index.lock +' + +test_expect_success 'cherry-pick a commit with an empty message with --allow-empty-message' ' + git checkout -f master && + git cherry-pick --allow-empty-message empty-branch +' + +test_expect_success 'cherry pick an empty non-ff commit without --allow-empty' ' + git checkout master && + echo fourth >>file2 && + git add file2 && + git commit -m "fourth" && + test_must_fail git cherry-pick empty-branch2 +' + +test_expect_success 'cherry pick an empty non-ff commit with --allow-empty' ' + git checkout master && + git cherry-pick --allow-empty empty-branch2 +' + +test_expect_success 'cherry pick with --keep-redundant-commits' ' + git checkout master && + git cherry-pick --keep-redundant-commits HEAD^ +' + +test_expect_success 'cherry-pick a commit that becomes no-op (prep)' ' + git checkout master && + git branch fork && + echo foo >file2 && + git add file2 && + test_tick && + git commit -m "add file2 on master" && + + git checkout fork && + echo foo >file2 && + git add file2 && + test_tick && + git commit -m "add file2 on the side" +' + +test_expect_success 'cherry-pick a no-op without --keep-redundant' ' + git reset --hard && + git checkout fork^0 && + test_must_fail git cherry-pick master +' +test_expect_success 'cherry-pick a no-op with --keep-redundant' ' + git reset --hard && + git checkout fork^0 && + git cherry-pick --keep-redundant-commits master && + git show -s --format=%s >actual && + echo "add file2 on master" >expect && + test_cmp expect actual ' test_done diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh index 51ca391e47..fb889ac6f0 100755 --- a/t/t3506-cherry-pick-ff.sh +++ b/t/t3506-cherry-pick-ff.sh @@ -105,4 +105,12 @@ test_expect_success 'cherry pick a root commit with --ff' ' test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1" ' +test_expect_success 'cherry-pick --ff on unborn branch' ' + git checkout --orphan unborn && + git rm --cached -r . && + rm -rf * && + git cherry-pick --ff first && + test_cmp_rev first HEAD +' + test_done diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh index ee1659c178..223b98433c 100755 --- a/t/t3507-cherry-pick-conflict.sh +++ b/t/t3507-cherry-pick-conflict.sh @@ -11,12 +11,6 @@ test_description='test cherry-pick and revert with conflicts . ./test-lib.sh -test_cmp_rev () { - git rev-parse --verify "$1" >expect.rev && - git rev-parse --verify "$2" >actual.rev && - test_cmp expect.rev actual.rev -} - pristine_detach () { git checkout -f "$1^0" && git read-tree -u --reset HEAD && @@ -30,6 +24,7 @@ test_expect_success setup ' test_commit initial foo a && test_commit base foo b && test_commit picked foo c && + test_commit --signoff picked-signed foo d && git config advice.detachedhead false ' @@ -59,6 +54,20 @@ test_expect_success 'advice from failed cherry-pick' " test_i18ncmp expected actual " +test_expect_success 'advice from failed cherry-pick --no-commit' " + pristine_detach initial && + + picked=\$(git rev-parse --short picked) && + cat <<-EOF >expected && + error: could not apply \$picked... picked + hint: after resolving the conflicts, mark the corrected paths + hint: with 'git add <paths>' or 'git rm <paths>' + EOF + test_must_fail git cherry-pick --no-commit picked 2>actual && + + test_i18ncmp expected actual +" + test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' ' pristine_detach initial && test_must_fail git cherry-pick picked && @@ -326,4 +335,35 @@ test_expect_success 'revert conflict, diff3 -m style' ' test_cmp expected actual ' +test_expect_success 'failed cherry-pick does not forget -s' ' + pristine_detach initial && + test_must_fail git cherry-pick -s picked && + test_i18ngrep -e "Signed-off-by" .git/MERGE_MSG +' + +test_expect_success 'commit after failed cherry-pick does not add duplicated -s' ' + pristine_detach initial && + test_must_fail git cherry-pick -s picked-signed && + git commit -a -s && + test $(git show -s |grep -c "Signed-off-by") = 1 +' + +test_expect_success 'commit after failed cherry-pick adds -s at the right place' ' + pristine_detach initial && + test_must_fail git cherry-pick picked && + git commit -a -s && + pwd && + cat <<EOF > expected && +picked + +Signed-off-by: C O Mitter <committer@example.com> + +Conflicts: + foo +EOF + + git show -s --pretty=format:%B > actual && + test_cmp expected actual +' + test_done diff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh index 1b3a344158..19c99d7ef1 100755 --- a/t/t3508-cherry-pick-many-commits.sh +++ b/t/t3508-cherry-pick-many-commits.sh @@ -5,15 +5,11 @@ test_description='test cherry-picking many commits' . ./test-lib.sh check_head_differs_from() { - head=$(git rev-parse --verify HEAD) && - arg=$(git rev-parse --verify "$1") && - test "$head" != "$arg" + ! test_cmp_rev HEAD "$1" } check_head_equals() { - head=$(git rev-parse --verify HEAD) && - arg=$(git rev-parse --verify "$1") && - test "$head" = "$arg" + test_cmp_rev HEAD "$1" } test_expect_success setup ' @@ -35,6 +31,37 @@ test_expect_success setup ' ' test_expect_success 'cherry-pick first..fourth works' ' + git checkout -f master && + git reset --hard first && + test_tick && + git cherry-pick first..fourth && + git diff --quiet other && + git diff --quiet HEAD other && + check_head_differs_from fourth +' + +test_expect_success 'cherry-pick three one two works' ' + git checkout -f first && + test_commit one && + test_commit two && + test_commit three && + git checkout -f master && + git reset --hard first && + git cherry-pick three one two && + git diff --quiet three && + git diff --quiet HEAD three && + test "$(git log --reverse --format=%s first..)" = "three +one +two" +' + +test_expect_success 'cherry-pick three one two: fails' ' + git checkout -f master && + git reset --hard first && + test_must_fail git cherry-pick three one two: +' + +test_expect_success 'output to keep user entertained during multi-pick' ' cat <<-\EOF >expected && [master OBJID] second Author: A U Thor <author@example.com> @@ -51,15 +78,22 @@ test_expect_success 'cherry-pick first..fourth works' ' git reset --hard first && test_tick && git cherry-pick first..fourth >actual && + sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy && + test_line_count -ge 3 actual.fuzzy && + test_i18ncmp expected actual.fuzzy +' + +test_expect_success 'cherry-pick --strategy resolve first..fourth works' ' + git checkout -f master && + git reset --hard first && + test_tick && + git cherry-pick --strategy resolve first..fourth && git diff --quiet other && git diff --quiet HEAD other && - - sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy && - test_cmp expected actual.fuzzy && check_head_differs_from fourth ' -test_expect_success 'cherry-pick --strategy resolve first..fourth works' ' +test_expect_success 'output during multi-pick indicates merge strategy' ' cat <<-\EOF >expected && Trying simple merge. [master OBJID] second @@ -79,11 +113,8 @@ test_expect_success 'cherry-pick --strategy resolve first..fourth works' ' git reset --hard first && test_tick && git cherry-pick --strategy resolve first..fourth >actual && - git diff --quiet other && - git diff --quiet HEAD other && sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy && - test_cmp expected actual.fuzzy && - check_head_differs_from fourth + test_i18ncmp expected actual.fuzzy ' test_expect_success 'cherry-pick --ff first..fourth works' ' diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh index df921d1f33..1e5b3948df 100755 --- a/t/t3509-cherry-pick-merge-df.sh +++ b/t/t3509-cherry-pick-merge-df.sh @@ -10,17 +10,15 @@ test_expect_success 'Initialize repository' ' git commit -m a ' -test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' ' +test_expect_success 'Setup rename across paths each below D/F conflicts' ' mkdir b && - ln -s ../a b/a && - git add b && + test_ln_s_add ../a b/a && git commit -m b && git checkout -b branch && rm b/a && - mv a b/a && - ln -s b/a a && - git add . && + git mv a b/a && + test_ln_s_add b/a a && git commit -m swap && >f1 && @@ -28,7 +26,7 @@ test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts git commit -m f1 ' -test_expect_success SYMLINKS 'Cherry-pick succeeds with rename across D/F conflicts' ' +test_expect_success 'Cherry-pick succeeds with rename across D/F conflicts' ' git reset --hard && git checkout master^0 && git cherry-pick branch @@ -76,7 +74,7 @@ test_expect_success 'Setup rename with file on one side matching different dirna echo content > sub/file && echo foo > othersub/whatever && git add -A && - git commit -m "Common commmit" && + git commit -m "Common commit" && git rm -rf othersub && git mv sub/file othersub && diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index 97f3710700..7b7a89dbd5 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -24,14 +24,8 @@ pristine_detach () { git clean -d -f -f -q -x } -test_cmp_rev () { - git rev-parse --verify "$1" >expect.rev && - git rev-parse --verify "$2" >actual.rev && - test_cmp expect.rev actual.rev -} - test_expect_success setup ' - git config advice.detachedhead false + git config advice.detachedhead false && echo unrelated >unrelated && git add unrelated && test_commit initial foo a && @@ -410,7 +404,7 @@ test_expect_success '--continue respects -x in first commit in multi-pick' ' grep "cherry picked from.*$picked" msg ' -test_expect_success '--signoff is not automatically propagated to resolved conflict' ' +test_expect_failure '--signoff is automatically propagated to resolved conflict' ' pristine_detach initial && test_expect_code 1 git cherry-pick --signoff base..anotherpick && echo "c" >foo && @@ -428,7 +422,7 @@ test_expect_success '--signoff is not automatically propagated to resolved confl grep "Signed-off-by:" anotherpick_msg ' -test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' ' +test_expect_failure '--signoff dropped for implicit commit of resolution, multi-pick case' ' pristine_detach initial && test_must_fail git cherry-pick -s picked anotherpick && echo c >foo && @@ -441,7 +435,7 @@ test_expect_success '--signoff dropped for implicit commit of resolution, multi- ! grep Signed-off-by: msg ' -test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' ' +test_expect_failure 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' ' pristine_detach initial && test_must_fail git cherry-pick -s picked && echo c >foo && diff --git a/t/t3511-cherry-pick-x.sh b/t/t3511-cherry-pick-x.sh new file mode 100755 index 0000000000..f97727975b --- /dev/null +++ b/t/t3511-cherry-pick-x.sh @@ -0,0 +1,219 @@ +#!/bin/sh + +test_description='Test cherry-pick -x and -s' + +. ./test-lib.sh + +pristine_detach () { + git cherry-pick --quit && + git checkout -f "$1^0" && + git read-tree -u --reset HEAD && + git clean -d -f -f -q -x +} + +mesg_one_line='base: commit message' + +mesg_no_footer="$mesg_one_line + +OneWordBodyThatsNotA-S-o-B" + +mesg_with_footer="$mesg_no_footer + +Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> +Signed-off-by: A.U. Thor <author@example.com> +Signed-off-by: B.U. Thor <buthor@example.com>" + +mesg_broken_footer="$mesg_no_footer + +The signed-off-by string should begin with the words Signed-off-by followed +by a colon and space, and then the signers name and email address. e.g. +Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" + +mesg_with_footer_sob="$mesg_with_footer +Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" + +mesg_with_cherry_footer="$mesg_with_footer_sob +(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709) +Tested-by: C.U. Thor <cuthor@example.com>" + + +test_expect_success setup ' + git config advice.detachedhead false && + echo unrelated >unrelated && + git add unrelated && + test_commit initial foo a && + test_commit "$mesg_one_line" foo b mesg-one-line && + git reset --hard initial && + test_commit "$mesg_no_footer" foo b mesg-no-footer && + git reset --hard initial && + test_commit "$mesg_broken_footer" foo b mesg-broken-footer && + git reset --hard initial && + test_commit "$mesg_with_footer" foo b mesg-with-footer && + git reset --hard initial && + test_commit "$mesg_with_footer_sob" foo b mesg-with-footer-sob && + git reset --hard initial && + test_commit "$mesg_with_cherry_footer" foo b mesg-with-cherry-footer && + pristine_detach initial && + test_commit conflicting unrelated +' + +test_expect_success 'cherry-pick -x inserts blank line after one line subject' ' + pristine_detach initial && + sha1=`git rev-parse mesg-one-line^0` && + git cherry-pick -x mesg-one-line && + cat <<-EOF >expect && + $mesg_one_line + + (cherry picked from commit $sha1) + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -s inserts blank line after one line subject' ' + pristine_detach initial && + git cherry-pick -s mesg-one-line && + cat <<-EOF >expect && + $mesg_one_line + + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -s inserts blank line after non-conforming footer' ' + pristine_detach initial && + git cherry-pick -s mesg-broken-footer && + cat <<-EOF >expect && + $mesg_broken_footer + + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -x inserts blank line when conforming footer not found' ' + pristine_detach initial && + sha1=`git rev-parse mesg-no-footer^0` && + git cherry-pick -x mesg-no-footer && + cat <<-EOF >expect && + $mesg_no_footer + + (cherry picked from commit $sha1) + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -s inserts blank line when conforming footer not found' ' + pristine_detach initial && + git cherry-pick -s mesg-no-footer && + cat <<-EOF >expect && + $mesg_no_footer + + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -x -s inserts blank line when conforming footer not found' ' + pristine_detach initial && + sha1=`git rev-parse mesg-no-footer^0` && + git cherry-pick -x -s mesg-no-footer && + cat <<-EOF >expect && + $mesg_no_footer + + (cherry picked from commit $sha1) + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -s adds sob when last sob doesnt match committer' ' + pristine_detach initial && + git cherry-pick -s mesg-with-footer && + cat <<-EOF >expect && + $mesg_with_footer + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -x -s adds sob when last sob doesnt match committer' ' + pristine_detach initial && + sha1=`git rev-parse mesg-with-footer^0` && + git cherry-pick -x -s mesg-with-footer && + cat <<-EOF >expect && + $mesg_with_footer + (cherry picked from commit $sha1) + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -s refrains from adding duplicate trailing sob' ' + pristine_detach initial && + git cherry-pick -s mesg-with-footer-sob && + cat <<-EOF >expect && + $mesg_with_footer_sob + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists for committer' ' + pristine_detach initial && + sha1=`git rev-parse mesg-with-footer-sob^0` && + git cherry-pick -x -s mesg-with-footer-sob && + cat <<-EOF >expect && + $mesg_with_footer_sob + (cherry picked from commit $sha1) + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -x treats "(cherry picked from..." line as part of footer' ' + pristine_detach initial && + sha1=`git rev-parse mesg-with-cherry-footer^0` && + git cherry-pick -x mesg-with-cherry-footer && + cat <<-EOF >expect && + $mesg_with_cherry_footer + (cherry picked from commit $sha1) + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -s treats "(cherry picked from..." line as part of footer' ' + pristine_detach initial && + git cherry-pick -s mesg-with-cherry-footer && + cat <<-EOF >expect && + $mesg_with_cherry_footer + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_expect_success 'cherry-pick -x -s treats "(cherry picked from..." line as part of footer' ' + pristine_detach initial && + sha1=`git rev-parse mesg-with-cherry-footer^0` && + git cherry-pick -x -s mesg-with-cherry-footer && + cat <<-EOF >expect && + $mesg_with_cherry_footer + (cherry picked from commit $sha1) + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + EOF + git log -1 --pretty=format:%B >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index 9fd28bcf34..540c49bab6 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -240,18 +240,15 @@ test_expect_success 'refresh index before checking if it is up-to-date' ' test_expect_success 'choking "git rm" should not let it die with cruft' ' git reset -q --hard && + test_when_finished "rm -f .git/index.lock && git reset -q --hard" && i=0 && while test $i -lt 12000 do - echo "100644 $_z40 0 some-file-$i" + echo "100644 1234567890123456789012345678901234567890 0 some-file-$i" i=$(( $i + 1 )) done | git update-index --index-info && - git rm -n "some-file-*" | :; - test -f .git/index.lock - status=$? - rm -f .git/index.lock - git reset -q --hard - test "$status" != 0 + git rm -n "some-file-*" | : && + test_path_is_missing .git/index.lock ' test_expect_success 'rm removes subdirectories recursively' ' @@ -262,4 +259,611 @@ test_expect_success 'rm removes subdirectories recursively' ' ! test -d dir ' +cat >expect <<EOF +M .gitmodules +D submod +EOF + +cat >expect.modified <<EOF + M submod +EOF + +cat >expect.cached <<EOF +D submod +EOF + +cat >expect.both_deleted<<EOF +D .gitmodules +D submod +EOF + +test_expect_success 'rm removes empty submodules from work tree' ' + mkdir submod && + git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) submod && + git config -f .gitmodules submodule.sub.url ./. && + git config -f .gitmodules submodule.sub.path submod && + git submodule init && + git add .gitmodules && + git commit -m "add submodule" && + git rm submod && + test ! -e submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm removes removed submodule from index and .gitmodules' ' + git reset --hard && + git submodule update && + rm -rf submod && + git rm submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm removes work tree of unmodified submodules' ' + git reset --hard && + git submodule update && + git rm submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm removes a submodule with a trailing /' ' + git reset --hard && + git submodule update && + git rm submod/ && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'rm fails when given a file with a trailing /' ' + test_must_fail git rm empty/ +' + +test_expect_success 'rm succeeds when given a directory with a trailing /' ' + git rm -r frotz/ +' + +test_expect_success 'rm of a populated submodule with different HEAD fails unless forced' ' + git reset --hard && + git submodule update && + (cd submod && + git checkout HEAD^ + ) && + test_must_fail git rm submod && + test -d submod && + test -f submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.modified actual && + git rm -f submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm --cached leaves work tree of populated submodules and .gitmodules alone' ' + git reset --hard && + git submodule update && + git rm --cached submod && + test -d submod && + test -f submod/.git && + git status -s -uno >actual && + test_cmp expect.cached actual && + git config -f .gitmodules submodule.sub.url && + git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm --dry-run does not touch the submodule or .gitmodules' ' + git reset --hard && + git submodule update && + git rm -n submod && + test -f submod/.git && + git diff-index --exit-code HEAD +' + +test_expect_success 'rm does not complain when no .gitmodules file is found' ' + git reset --hard && + git submodule update && + git rm .gitmodules && + git rm submod >actual 2>actual.err && + ! test -s actual.err && + ! test -d submod && + ! test -f submod/.git && + git status -s -uno >actual && + test_cmp expect.both_deleted actual +' + +test_expect_success 'rm will error out on a modified .gitmodules file unless staged' ' + git reset --hard && + git submodule update && + git config -f .gitmodules foo.bar true && + test_must_fail git rm submod >actual 2>actual.err && + test -s actual.err && + test -d submod && + test -f submod/.git && + git diff-files --quiet -- submod && + git add .gitmodules && + git rm submod >actual 2>actual.err && + ! test -s actual.err && + ! test -d submod && + ! test -f submod/.git && + git status -s -uno >actual && + test_cmp expect actual +' + +test_expect_success 'rm issues a warning when section is not found in .gitmodules' ' + git reset --hard && + git submodule update && + git config -f .gitmodules --remove-section submodule.sub && + git add .gitmodules && + echo "warning: Could not find section in .gitmodules where path=submod" >expect.err && + git rm submod >actual 2>actual.err && + test_i18ncmp expect.err actual.err && + ! test -d submod && + ! test -f submod/.git && + git status -s -uno >actual && + test_cmp expect actual +' + +test_expect_success 'rm of a populated submodule with modifications fails unless forced' ' + git reset --hard && + git submodule update && + (cd submod && + echo X >empty + ) && + test_must_fail git rm submod && + test -d submod && + test -f submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.modified actual && + git rm -f submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'rm of a populated submodule with untracked files fails unless forced' ' + git reset --hard && + git submodule update && + (cd submod && + echo X >untracked + ) && + test_must_fail git rm submod && + test -d submod && + test -f submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.modified actual && + git rm -f submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'setup submodule conflict' ' + git reset --hard && + git submodule update && + git checkout -b branch1 && + echo 1 >nitfol && + git add nitfol && + git commit -m "added nitfol 1" && + git checkout -b branch2 master && + echo 2 >nitfol && + git add nitfol && + git commit -m "added nitfol 2" && + git checkout -b conflict1 master && + (cd submod && + git fetch && + git checkout branch1 + ) && + git add submod && + git commit -m "submod 1" && + git checkout -b conflict2 master && + (cd submod && + git checkout branch2 + ) && + git add submod && + git commit -m "submod 2" +' + +cat >expect.conflict <<EOF +UU submod +EOF + +test_expect_success 'rm removes work tree of unmodified conflicted submodule' ' + git checkout conflict1 && + git reset --hard && + git submodule update && + test_must_fail git merge conflict2 && + git rm submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'rm of a conflicted populated submodule with different HEAD fails unless forced' ' + git checkout conflict1 && + git reset --hard && + git submodule update && + (cd submod && + git checkout HEAD^ + ) && + test_must_fail git merge conflict2 && + test_must_fail git rm submod && + test -d submod && + test -f submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.conflict actual && + git rm -f submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm of a conflicted populated submodule with modifications fails unless forced' ' + git checkout conflict1 && + git reset --hard && + git submodule update && + (cd submod && + echo X >empty + ) && + test_must_fail git merge conflict2 && + test_must_fail git rm submod && + test -d submod && + test -f submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.conflict actual && + git rm -f submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual && + test_must_fail git config -f .gitmodules submodule.sub.url && + test_must_fail git config -f .gitmodules submodule.sub.path +' + +test_expect_success 'rm of a conflicted populated submodule with untracked files fails unless forced' ' + git checkout conflict1 && + git reset --hard && + git submodule update && + (cd submod && + echo X >untracked + ) && + test_must_fail git merge conflict2 && + test_must_fail git rm submod && + test -d submod && + test -f submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.conflict actual && + git rm -f submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'rm of a conflicted populated submodule with a .git directory fails even when forced' ' + git checkout conflict1 && + git reset --hard && + git submodule update && + (cd submod && + rm .git && + cp -R ../.git/modules/sub .git && + GIT_WORK_TREE=. git config --unset core.worktree + ) && + test_must_fail git merge conflict2 && + test_must_fail git rm submod && + test -d submod && + test -d submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.conflict actual && + test_must_fail git rm -f submod && + test -d submod && + test -d submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.conflict actual && + git merge --abort && + rm -rf submod +' + +test_expect_success 'rm of a conflicted unpopulated submodule succeeds' ' + git checkout conflict1 && + git reset --hard && + test_must_fail git merge conflict2 && + git rm submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'rm of a populated submodule with a .git directory fails even when forced' ' + git checkout -f master && + git reset --hard && + git submodule update && + (cd submod && + rm .git && + cp -R ../.git/modules/sub .git && + GIT_WORK_TREE=. git config --unset core.worktree + ) && + test_must_fail git rm submod && + test -d submod && + test -d submod/.git && + git status -s -uno --ignore-submodules=none > actual && + ! test -s actual && + test_must_fail git rm -f submod && + test -d submod && + test -d submod/.git && + git status -s -uno --ignore-submodules=none > actual && + ! test -s actual && + rm -rf submod +' + +cat >expect.deepmodified <<EOF + M submod/subsubmod +EOF + +test_expect_success 'setup subsubmodule' ' + git reset --hard && + git submodule update && + (cd submod && + git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) subsubmod && + git config -f .gitmodules submodule.sub.url ../. && + git config -f .gitmodules submodule.sub.path subsubmod && + git submodule init && + git add .gitmodules && + git commit -m "add subsubmodule" && + git submodule update subsubmod + ) && + git commit -a -m "added deep submodule" +' + +test_expect_success 'rm recursively removes work tree of unmodified submodules' ' + git rm submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'rm of a populated nested submodule with different nested HEAD fails unless forced' ' + git reset --hard && + git submodule update --recursive && + (cd submod/subsubmod && + git checkout HEAD^ + ) && + test_must_fail git rm submod && + test -d submod && + test -f submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.modified actual && + git rm -f submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'rm of a populated nested submodule with nested modifications fails unless forced' ' + git reset --hard && + git submodule update --recursive && + (cd submod/subsubmod && + echo X >empty + ) && + test_must_fail git rm submod && + test -d submod && + test -f submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.modified actual && + git rm -f submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'rm of a populated nested submodule with nested untracked files fails unless forced' ' + git reset --hard && + git submodule update --recursive && + (cd submod/subsubmod && + echo X >untracked + ) && + test_must_fail git rm submod && + test -d submod && + test -f submod/.git && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect.modified actual && + git rm -f submod && + test ! -d submod && + git status -s -uno --ignore-submodules=none > actual && + test_cmp expect actual +' + +test_expect_success 'rm of a populated nested submodule with a nested .git directory fails even when forced' ' + git reset --hard && + git submodule update --recursive && + (cd submod/subsubmod && + rm .git && + cp -R ../../.git/modules/sub/modules/sub .git && + GIT_WORK_TREE=. git config --unset core.worktree + ) && + test_must_fail git rm submod && + test -d submod && + test -d submod/subsubmod/.git && + git status -s -uno --ignore-submodules=none > actual && + ! test -s actual && + test_must_fail git rm -f submod && + test -d submod && + test -d submod/subsubmod/.git && + git status -s -uno --ignore-submodules=none > actual && + ! test -s actual && + rm -rf submod +' + +test_expect_success 'rm of d/f when d has become a non-directory' ' + rm -rf d && + mkdir d && + >d/f && + git add d && + rm -rf d && + >d && + git rm d/f && + test_must_fail git rev-parse --verify :d/f && + test_path_is_file d +' + +test_expect_success SYMLINKS 'rm of d/f when d has become a dangling symlink' ' + rm -rf d && + mkdir d && + >d/f && + git add d && + rm -rf d && + ln -s nonexistent d && + git rm d/f && + test_must_fail git rev-parse --verify :d/f && + test -h d && + test_path_is_missing d +' + +test_expect_success 'rm of file when it has become a directory' ' + rm -rf d && + >d && + git add d && + rm -f d && + mkdir d && + >d/f && + test_must_fail git rm d && + git rev-parse --verify :d && + test_path_is_file d/f +' + +test_expect_success SYMLINKS 'rm across a symlinked leading path (no index)' ' + rm -rf d e && + mkdir e && + echo content >e/f && + ln -s e d && + git add -A e d && + git commit -m "symlink d to e, e/f exists" && + test_must_fail git rm d/f && + git rev-parse --verify :d && + git rev-parse --verify :e/f && + test -h d && + test_path_is_file e/f +' + +test_expect_failure SYMLINKS 'rm across a symlinked leading path (w/ index)' ' + rm -rf d e && + mkdir d && + echo content >d/f && + git add -A e d && + git commit -m "d/f exists" && + mv d e && + ln -s e d && + test_must_fail git rm d/f && + git rev-parse --verify :d/f && + test -h d && + test_path_is_file e/f +' + +test_expect_success 'setup for testing rm messages' ' + >bar.txt && + >foo.txt && + git add bar.txt foo.txt +' + +test_expect_success 'rm files with different staged content' ' + cat >expect <<-\EOF && + error: the following files have staged content different from both the + file and the HEAD: + bar.txt + foo.txt + (use -f to force removal) + EOF + echo content1 >foo.txt && + echo content1 >bar.txt && + test_must_fail git rm foo.txt bar.txt 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'rm files with different staged content without hints' ' + cat >expect <<-\EOF && + error: the following files have staged content different from both the + file and the HEAD: + bar.txt + foo.txt + EOF + echo content2 >foo.txt && + echo content2 >bar.txt && + test_must_fail git -c advice.rmhints=false rm foo.txt bar.txt 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'rm file with local modification' ' + cat >expect <<-\EOF && + error: the following file has local modifications: + foo.txt + (use --cached to keep the file, or -f to force removal) + EOF + git commit -m "testing rm 3" && + echo content3 >foo.txt && + test_must_fail git rm foo.txt 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'rm file with local modification without hints' ' + cat >expect <<-\EOF && + error: the following file has local modifications: + bar.txt + EOF + echo content4 >bar.txt && + test_must_fail git -c advice.rmhints=false rm bar.txt 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'rm file with changes in the index' ' + cat >expect <<-\EOF && + error: the following file has changes staged in the index: + foo.txt + (use --cached to keep the file, or -f to force removal) + EOF + git reset --hard && + echo content5 >foo.txt && + git add foo.txt && + test_must_fail git rm foo.txt 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'rm file with changes in the index without hints' ' + cat >expect <<-\EOF && + error: the following file has changes staged in the index: + foo.txt + EOF + test_must_fail git -c advice.rmhints=false rm foo.txt 2>actual && + test_i18ncmp expect actual +' + +test_expect_success 'rm files with two different errors' ' + cat >expect <<-\EOF && + error: the following file has staged content different from both the + file and the HEAD: + foo1.txt + (use -f to force removal) + error: the following file has changes staged in the index: + bar1.txt + (use --cached to keep the file, or -f to force removal) + EOF + echo content >foo1.txt && + git add foo1.txt && + echo content6 >foo1.txt && + echo content6 >bar1.txt && + git add bar1.txt && + test_must_fail git rm bar1.txt foo1.txt 2>actual && + test_i18ncmp expect actual +' + test_done diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 874b3a6444..aab86e838b 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -30,10 +30,9 @@ test_expect_success \ *) echo fail; git ls-files --stage xfoo1; (exit 1);; esac' -test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' ' +test_expect_success 'git add: filemode=0 should not get confused by symlink' ' rm -f xfoo1 && - ln -s foo xfoo1 && - git add xfoo1 && + test_ln_s_add foo xfoo1 && case "`git ls-files --stage xfoo1`" in 120000" "*xfoo1) echo pass;; *) echo fail; git ls-files --stage xfoo1; (exit 1);; @@ -51,21 +50,19 @@ test_expect_success \ *) echo fail; git ls-files --stage xfoo2; (exit 1);; esac' -test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' ' +test_expect_success 'git add: filemode=0 should not get confused by symlink' ' rm -f xfoo2 && - ln -s foo xfoo2 && - git update-index --add xfoo2 && + test_ln_s_add foo xfoo2 && case "`git ls-files --stage xfoo2`" in 120000" "*xfoo2) echo pass;; *) echo fail; git ls-files --stage xfoo2; (exit 1);; esac ' -test_expect_success SYMLINKS \ +test_expect_success \ 'git update-index --add: Test that executable bit is not used...' \ 'git config core.filemode 0 && - ln -s xfoo2 xfoo3 && - git update-index --add xfoo3 && + test_ln_s_add xfoo2 xfoo3 && # runs git update-index --add case "`git ls-files --stage xfoo3`" in 120000" "*xfoo3) echo pass;; *) echo fail; git ls-files --stage xfoo3; (exit 1);; diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 9e236f9cc0..9dc91d09d7 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -4,18 +4,24 @@ test_description='add -i basic tests' . ./test-lib.sh . "$TEST_DIRECTORY"/lib-prereq-FILEMODE.sh -test_expect_success PERL 'setup (initial)' ' +if ! test_have_prereq PERL +then + skip_all='skipping add -i tests, perl not available' + test_done +fi + +test_expect_success 'setup (initial)' ' echo content >file && git add file && echo more >>file && echo lines >>file ' -test_expect_success PERL 'status works (initial)' ' +test_expect_success 'status works (initial)' ' git add -i </dev/null >output && grep "+1/-0 *+2/-0 file" output ' -test_expect_success PERL 'setup expected' ' +test_expect_success 'setup expected' ' cat >expected <<EOF new file mode 100644 index 0000000..d95f3ad @@ -26,19 +32,19 @@ index 0000000..d95f3ad EOF ' -test_expect_success PERL 'diff works (initial)' ' +test_expect_success 'diff works (initial)' ' (echo d; echo 1) | git add -i >output && sed -ne "/new file/,/content/p" <output >diff && test_cmp expected diff ' -test_expect_success PERL 'revert works (initial)' ' +test_expect_success 'revert works (initial)' ' git add file && (echo r; echo 1) | git add -i && git ls-files >output && ! grep . output ' -test_expect_success PERL 'setup (commit)' ' +test_expect_success 'setup (commit)' ' echo baseline >file && git add file && git commit -m commit && @@ -47,12 +53,12 @@ test_expect_success PERL 'setup (commit)' ' echo more >>file && echo lines >>file ' -test_expect_success PERL 'status works (commit)' ' +test_expect_success 'status works (commit)' ' git add -i </dev/null >output && grep "+1/-0 *+2/-0 file" output ' -test_expect_success PERL 'setup expected' ' +test_expect_success 'setup expected' ' cat >expected <<EOF index 180b47c..b6f2c08 100644 --- a/file @@ -63,12 +69,12 @@ index 180b47c..b6f2c08 100644 EOF ' -test_expect_success PERL 'diff works (commit)' ' +test_expect_success 'diff works (commit)' ' (echo d; echo 1) | git add -i >output && sed -ne "/^index/,/content/p" <output >diff && test_cmp expected diff ' -test_expect_success PERL 'revert works (commit)' ' +test_expect_success 'revert works (commit)' ' git add file && (echo r; echo 1) | git add -i && git add -i </dev/null >output && @@ -76,24 +82,24 @@ test_expect_success PERL 'revert works (commit)' ' ' -test_expect_success PERL 'setup expected' ' +test_expect_success 'setup expected' ' cat >expected <<EOF EOF ' -test_expect_success PERL 'setup fake editor' ' +test_expect_success 'setup fake editor' ' >fake_editor.sh && chmod a+x fake_editor.sh && test_set_editor "$(pwd)/fake_editor.sh" ' -test_expect_success PERL 'dummy edit works' ' +test_expect_success 'dummy edit works' ' (echo e; echo a) | git add -p && git diff > diff && test_cmp expected diff ' -test_expect_success PERL 'setup patch' ' +test_expect_success 'setup patch' ' cat >patch <<EOF @@ -1,1 +1,4 @@ this @@ -103,7 +109,7 @@ cat >patch <<EOF EOF ' -test_expect_success PERL 'setup fake editor' ' +test_expect_success 'setup fake editor' ' echo "#!$SHELL_PATH" >fake_editor.sh && cat >>fake_editor.sh <<\EOF && mv -f "$1" oldpatch && @@ -113,26 +119,26 @@ EOF test_set_editor "$(pwd)/fake_editor.sh" ' -test_expect_success PERL 'bad edit rejected' ' +test_expect_success 'bad edit rejected' ' git reset && (echo e; echo n; echo d) | git add -p >output && grep "hunk does not apply" output ' -test_expect_success PERL 'setup patch' ' +test_expect_success 'setup patch' ' cat >patch <<EOF this patch is garbage EOF ' -test_expect_success PERL 'garbage edit rejected' ' +test_expect_success 'garbage edit rejected' ' git reset && (echo e; echo n; echo d) | git add -p >output && grep "hunk does not apply" output ' -test_expect_success PERL 'setup patch' ' +test_expect_success 'setup patch' ' cat >patch <<EOF @@ -1,0 +1,0 @@ baseline @@ -142,7 +148,7 @@ cat >patch <<EOF EOF ' -test_expect_success PERL 'setup expected' ' +test_expect_success 'setup expected' ' cat >expected <<EOF diff --git a/file b/file index b5dd6c9..f910ae9 100644 @@ -157,13 +163,13 @@ index b5dd6c9..f910ae9 100644 EOF ' -test_expect_success PERL 'real edit works' ' +test_expect_success 'real edit works' ' (echo e; echo n; echo d) | git add -p && git diff >output && test_cmp expected output ' -test_expect_success PERL 'skip files similarly as commit -a' ' +test_expect_success 'skip files similarly as commit -a' ' git reset && echo file >.gitignore && echo changed >file && @@ -177,7 +183,7 @@ test_expect_success PERL 'skip files similarly as commit -a' ' ' rm -f .gitignore -test_expect_success PERL,FILEMODE 'patch does not affect mode' ' +test_expect_success FILEMODE 'patch does not affect mode' ' git reset --hard && echo content >>file && chmod +x file && @@ -186,7 +192,7 @@ test_expect_success PERL,FILEMODE 'patch does not affect mode' ' git diff file | grep "new mode" ' -test_expect_success PERL,FILEMODE 'stage mode but not hunk' ' +test_expect_success FILEMODE 'stage mode but not hunk' ' git reset --hard && echo content >>file && chmod +x file && @@ -196,7 +202,7 @@ test_expect_success PERL,FILEMODE 'stage mode but not hunk' ' ' -test_expect_success PERL,FILEMODE 'stage mode and hunk' ' +test_expect_success FILEMODE 'stage mode and hunk' ' git reset --hard && echo content >>file && chmod +x file && @@ -208,14 +214,14 @@ test_expect_success PERL,FILEMODE 'stage mode and hunk' ' # end of tests disabled when filemode is not usable -test_expect_success PERL 'setup again' ' +test_expect_success 'setup again' ' git reset --hard && test_chmod +x file && echo content >>file ' # Write the patch file with a new line at the top and bottom -test_expect_success PERL 'setup patch' ' +test_expect_success 'setup patch' ' cat >patch <<EOF index 180b47c..b6f2c08 100644 --- a/file @@ -229,7 +235,7 @@ EOF ' # Expected output, similar to the patch but w/ diff at the top -test_expect_success PERL 'setup expected' ' +test_expect_success 'setup expected' ' cat >expected <<EOF diff --git a/file b/file index b6f2c08..61b9053 100755 @@ -244,7 +250,7 @@ EOF ' # Test splitting the first patch, then adding both -test_expect_success PERL 'add first line works' ' +test_expect_success 'add first line works' ' git commit -am "clear local changes" && git apply patch && (echo s; echo y; echo y) | git add -p file && @@ -252,7 +258,7 @@ test_expect_success PERL 'add first line works' ' test_cmp expected diff ' -test_expect_success PERL 'setup expected' ' +test_expect_success 'setup expected' ' cat >expected <<EOF diff --git a/non-empty b/non-empty deleted file mode 100644 @@ -264,7 +270,7 @@ index d95f3ad..0000000 EOF ' -test_expect_success PERL 'deleting a non-empty file' ' +test_expect_success 'deleting a non-empty file' ' git reset --hard && echo content >non-empty && git add non-empty && @@ -275,7 +281,7 @@ test_expect_success PERL 'deleting a non-empty file' ' test_cmp expected diff ' -test_expect_success PERL 'setup expected' ' +test_expect_success 'setup expected' ' cat >expected <<EOF diff --git a/empty b/empty deleted file mode 100644 @@ -283,7 +289,7 @@ index e69de29..0000000 EOF ' -test_expect_success PERL 'deleting an empty file' ' +test_expect_success 'deleting an empty file' ' git reset --hard && > empty && git add empty && @@ -294,7 +300,7 @@ test_expect_success PERL 'deleting an empty file' ' test_cmp expected diff ' -test_expect_success PERL 'split hunk setup' ' +test_expect_success 'split hunk setup' ' git reset --hard && for i in 10 20 30 40 50 60 do @@ -310,7 +316,7 @@ test_expect_success PERL 'split hunk setup' ' done >test ' -test_expect_success PERL 'split hunk "add -p (edit)"' ' +test_expect_success 'split hunk "add -p (edit)"' ' # Split, say Edit and do nothing. Then: # # 1. Broken version results in a patch that does not apply and @@ -319,7 +325,7 @@ test_expect_success PERL 'split hunk "add -p (edit)"' ' # times to get out. # # 2. Correct version applies the (not)edited version, and asks - # about the next hunk, against wich we say q and program + # about the next hunk, against which we say q and program # exits. for a in s e q n q q do @@ -330,4 +336,30 @@ test_expect_success PERL 'split hunk "add -p (edit)"' ' ! grep "^+15" actual ' +test_expect_success 'patch mode ignores unmerged entries' ' + git reset --hard && + test_commit conflict && + test_commit non-conflict && + git checkout -b side && + test_commit side conflict.t && + git checkout master && + test_commit master conflict.t && + test_must_fail git merge side && + echo changed >non-conflict.t && + echo y | git add -p >output && + ! grep a/conflict.t output && + cat >expected <<-\EOF && + * Unmerged path conflict.t + diff --git a/non-conflict.t b/non-conflict.t + index f766221..5ea2ed4 100644 + --- a/non-conflict.t + +++ b/non-conflict.t + @@ -1 +1 @@ + -non-conflict + +changed + EOF + git diff --cached >diff && + test_cmp expected diff +' + test_done diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh index d48a7c002d..4bf1dbe9c9 100755 --- a/t/t3900-i18n-commit.sh +++ b/t/t3900-i18n-commit.sh @@ -34,11 +34,47 @@ test_expect_success 'no encoding header for base case' ' test z = "z$E" ' -test_expect_failure 'UTF-16 refused because of NULs' ' +test_expect_success 'UTF-16 refused because of NULs' ' echo UTF-16 >F && - git commit -a -F "$TEST_DIRECTORY"/t3900/UTF-16.txt + test_must_fail git commit -a -F "$TEST_DIRECTORY"/t3900/UTF-16.txt ' +test_expect_success 'UTF-8 invalid characters refused' ' + test_when_finished "rm -f $HOME/stderr $HOME/invalid" && + echo "UTF-8 characters" >F && + printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \ + >"$HOME/invalid" && + git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr && + grep "did not conform" "$HOME"/stderr +' + +test_expect_success 'UTF-8 overlong sequences rejected' ' + test_when_finished "rm -f $HOME/stderr $HOME/invalid" && + rm -f "$HOME/stderr" "$HOME/invalid" && + echo "UTF-8 overlong" >F && + printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \ + >"$HOME/invalid" && + git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr && + grep "did not conform" "$HOME"/stderr +' + +test_expect_success 'UTF-8 non-characters refused' ' + test_when_finished "rm -f $HOME/stderr $HOME/invalid" && + echo "UTF-8 non-character 1" >F && + printf "Commit message\n\nNon-character:\364\217\277\276\n" \ + >"$HOME/invalid" && + git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr && + grep "did not conform" "$HOME"/stderr +' + +test_expect_success 'UTF-8 non-characters refused' ' + test_when_finished "rm -f $HOME/stderr $HOME/invalid" && + echo "UTF-8 non-character 2." >F && + printf "Commit message\n\nNon-character:\357\267\220\n" \ + >"$HOME/invalid" && + git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr && + grep "did not conform" "$HOME"/stderr +' for H in ISO8859-1 eucJP ISO-2022-JP do @@ -160,7 +196,7 @@ test_commit_autosquash_flags () { git config --unset-all i18n.commitencoding && git rebase --autosquash -i HEAD^^^ && git log --oneline >actual && - test 3 = $(wc -l <actual) + test_line_count = 3 actual ' } diff --git a/t/t3900/UTF-16.txt b/t/t3900/UTF-16.txt Binary files differnew file mode 100644 index 0000000000..2257f05a99 --- /dev/null +++ b/t/t3900/UTF-16.txt diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh index 534ee08a44..892f567844 100755 --- a/t/t3902-quoted.sh +++ b/t/t3902-quoted.sh @@ -16,9 +16,8 @@ echo foo 2>/dev/null > "Name and an${HT}HT" if ! test -f "Name and an${HT}HT" then # FAT/NTFS does not allow tabs in filenames - say 'Your filesystem does not allow tabs in filenames' -else - test_set_prereq TABS_IN_FILENAMES + skip_all='Your filesystem does not allow tabs in filenames' + test_done fi for_each_name () { @@ -31,7 +30,7 @@ for_each_name () { done } -test_expect_success TABS_IN_FILENAMES 'setup' ' +test_expect_success 'setup' ' mkdir "$FN" && for_each_name "echo initial >\"\$name\"" && @@ -45,7 +44,7 @@ test_expect_success TABS_IN_FILENAMES 'setup' ' ' -test_expect_success TABS_IN_FILENAMES 'setup expected files' ' +test_expect_success 'setup expected files' ' cat >expect.quoted <<\EOF && Name "Name and a\nLF" @@ -75,74 +74,74 @@ With SP in it EOF ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-files' ' +test_expect_success 'check fully quoted output from ls-files' ' git ls-files >current && test_cmp expect.quoted current ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-files' ' +test_expect_success 'check fully quoted output from diff-files' ' git diff --name-only >current && test_cmp expect.quoted current ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-index' ' +test_expect_success 'check fully quoted output from diff-index' ' git diff --name-only HEAD >current && test_cmp expect.quoted current ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-tree' ' +test_expect_success 'check fully quoted output from diff-tree' ' git diff --name-only HEAD^ HEAD >current && test_cmp expect.quoted current ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-tree' ' +test_expect_success 'check fully quoted output from ls-tree' ' git ls-tree --name-only -r HEAD >current && test_cmp expect.quoted current ' -test_expect_success TABS_IN_FILENAMES 'setting core.quotepath' ' +test_expect_success 'setting core.quotepath' ' git config --bool core.quotepath false ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-files' ' +test_expect_success 'check fully quoted output from ls-files' ' git ls-files >current && test_cmp expect.raw current ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-files' ' +test_expect_success 'check fully quoted output from diff-files' ' git diff --name-only >current && test_cmp expect.raw current ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-index' ' +test_expect_success 'check fully quoted output from diff-index' ' git diff --name-only HEAD >current && test_cmp expect.raw current ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from diff-tree' ' +test_expect_success 'check fully quoted output from diff-tree' ' git diff --name-only HEAD^ HEAD >current && test_cmp expect.raw current ' -test_expect_success TABS_IN_FILENAMES 'check fully quoted output from ls-tree' ' +test_expect_success 'check fully quoted output from ls-tree' ' git ls-tree --name-only -r HEAD >current && test_cmp expect.raw current diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 663c60a12e..debda7a678 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -200,17 +200,17 @@ test_expect_success 'apply -q is quiet' ' echo foo > file && git stash && git stash apply -q > output.out 2>&1 && - test ! -s output.out + test_must_be_empty output.out ' test_expect_success 'save -q is quiet' ' git stash save --quiet > output.out 2>&1 && - test ! -s output.out + test_must_be_empty output.out ' test_expect_success 'pop -q is quiet' ' git stash pop -q > output.out 2>&1 && - test ! -s output.out + test_must_be_empty output.out ' test_expect_success 'pop -q --index works and is quiet' ' @@ -219,13 +219,13 @@ test_expect_success 'pop -q --index works and is quiet' ' git stash save --quiet && git stash pop -q --index > output.out 2>&1 && test foo = "$(git show :file)" && - test ! -s output.out + test_must_be_empty output.out ' test_expect_success 'drop -q is quiet' ' git stash && git stash drop -q > output.out 2>&1 && - test ! -s output.out + test_must_be_empty output.out ' test_expect_success 'stash -k' ' @@ -336,41 +336,58 @@ test_expect_success SYMLINKS 'stash file to symlink (full stage)' ' # This test creates a commit with a symlink used for the following tests -test_expect_success SYMLINKS 'stash symlink to file' ' +test_expect_success 'stash symlink to file' ' git reset --hard && - ln -s file filelink && - git add filelink && + test_ln_s_add file filelink && git commit -m "Add symlink" && rm filelink && cp file filelink && - git stash save "symlink to file" && + git stash save "symlink to file" +' + +test_expect_success SYMLINKS 'this must have re-created the symlink' ' test -h filelink && - case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac && + case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac +' + +test_expect_success 'unstash must re-create the file' ' git stash apply && ! test -h filelink && test bar = "$(cat file)" ' -test_expect_success SYMLINKS 'stash symlink to file (stage rm)' ' +test_expect_success 'stash symlink to file (stage rm)' ' git reset --hard && git rm filelink && cp file filelink && - git stash save "symlink to file (stage rm)" && + git stash save "symlink to file (stage rm)" +' + +test_expect_success SYMLINKS 'this must have re-created the symlink' ' test -h filelink && - case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac && + case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac +' + +test_expect_success 'unstash must re-create the file' ' git stash apply && ! test -h filelink && test bar = "$(cat file)" ' -test_expect_success SYMLINKS 'stash symlink to file (full stage)' ' +test_expect_success 'stash symlink to file (full stage)' ' git reset --hard && rm filelink && cp file filelink && git add filelink && - git stash save "symlink to file (full stage)" && + git stash save "symlink to file (full stage)" +' + +test_expect_success SYMLINKS 'this must have re-created the symlink' ' test -h filelink && - case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac && + case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac +' + +test_expect_success 'unstash must re-create the file' ' git stash apply && ! test -h filelink && test bar = "$(cat file)" @@ -432,7 +449,7 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' ' test $(git ls-files --modified | wc -l) -eq 1 ' -test_expect_success 'stash show - stashes on stack, stash-like argument' ' +test_expect_success 'stash show format defaults to --stat' ' git stash clear && test_when_finished "git reset --hard HEAD" && git reset --hard && @@ -443,10 +460,25 @@ test_expect_success 'stash show - stashes on stack, stash-like argument' ' STASH_ID=$(git stash create) && git reset --hard && cat >expected <<-EOF && - file | 1 + + file | 1 + 1 file changed, 1 insertion(+) EOF git stash show ${STASH_ID} >actual && + test_i18ncmp expected actual +' + +test_expect_success 'stash show - stashes on stack, stash-like argument' ' + git stash clear && + test_when_finished "git reset --hard HEAD" && + git reset --hard && + echo foo >> file && + git stash && + test_when_finished "git stash drop" && + echo bar >> file && + STASH_ID=$(git stash create) && + git reset --hard && + echo "1 0 file" >expected && + git stash show --numstat ${STASH_ID} >actual && test_cmp expected actual ' @@ -480,11 +512,8 @@ test_expect_success 'stash show - no stashes on stack, stash-like argument' ' echo foo >> file && STASH_ID=$(git stash create) && git reset --hard && - cat >expected <<-EOF && - file | 1 + - 1 file changed, 1 insertion(+) - EOF - git stash show ${STASH_ID} >actual && + echo "1 0 file" >expected && + git stash show --numstat ${STASH_ID} >actual && test_cmp expected actual ' @@ -598,7 +627,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu git stash apply ) | sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..." - test_cmp expect actual + test_i18ncmp expect actual ' cat > expect << EOF @@ -625,4 +654,23 @@ test_expect_success 'stash where working directory contains "HEAD" file' ' test_cmp output expect ' +test_expect_success 'store called with invalid commit' ' + test_must_fail git stash store foo +' + +test_expect_success 'store updates stash ref and reflog' ' + git stash clear && + git reset --hard && + echo quux >bazzy && + git add bazzy && + STASH_ID=$(git stash create) && + git reset --hard && + ! grep quux bazzy && + git stash store -m quuxery $STASH_ID && + test $(cat .git/refs/stash) = $STASH_ID && + grep $STASH_ID .git/logs/refs/stash && + git stash pop && + grep quux bazzy +' + test_done diff --git a/t/t3910-mac-os-precompose.sh b/t/t3910-mac-os-precompose.sh new file mode 100755 index 0000000000..e4ba6013e4 --- /dev/null +++ b/t/t3910-mac-os-precompose.sh @@ -0,0 +1,153 @@ +#!/bin/sh +# +# Copyright (c) 2012 Torsten Bögershausen +# + +test_description='utf-8 decomposed (nfd) converted to precomposed (nfc)' + +. ./test-lib.sh + +if ! test_have_prereq UTF8_NFD_TO_NFC +then + skip_all="filesystem does not corrupt utf-8" + test_done +fi + +# create utf-8 variables +Adiarnfc=`printf '\303\204'` +Adiarnfd=`printf 'A\314\210'` + +Odiarnfc=`printf '\303\226'` +Odiarnfd=`printf 'O\314\210'` +AEligatu=`printf '\303\206'` +Invalidu=`printf '\303\377'` + + +#Create a string with 255 bytes (decomposed) +Alongd=$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd$Adiarnfd #21 Byte +Alongd=$Alongd$Alongd$Alongd #63 Byte +Alongd=$Alongd$Alongd$Alongd$Alongd$Adiarnfd #255 Byte + +#Create a string with 254 bytes (precomposed) +Alongc=$AEligatu$AEligatu$AEligatu$AEligatu$AEligatu #10 Byte +Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc #50 Byte +Alongc=$Alongc$Alongc$Alongc$Alongc$Alongc #250 Byte +Alongc=$Alongc$AEligatu$AEligatu #254 Byte + +test_expect_success "detect if nfd needed" ' + precomposeunicode=`git config core.precomposeunicode` && + test "$precomposeunicode" = true && + git config core.precomposeunicode true +' +test_expect_success "setup" ' + >x && + git add x && + git commit -m "1st commit" && + git rm x && + git commit -m "rm x" +' +test_expect_success "setup case mac" ' + git checkout -b mac_os +' +# This will test nfd2nfc in readdir() +test_expect_success "add file Adiarnfc" ' + echo f.Adiarnfc >f.$Adiarnfc && + git add f.$Adiarnfc && + git commit -m "add f.$Adiarnfc" +' +# This will test nfd2nfc in git stage() +test_expect_success "stage file d.Adiarnfd/f.Adiarnfd" ' + mkdir d.$Adiarnfd && + echo d.$Adiarnfd/f.$Adiarnfd >d.$Adiarnfd/f.$Adiarnfd && + git stage d.$Adiarnfd/f.$Adiarnfd && + git commit -m "add d.$Adiarnfd/f.$Adiarnfd" +' +test_expect_success "add link Adiarnfc" ' + ln -s d.$Adiarnfd/f.$Adiarnfd l.$Adiarnfc && + git add l.$Adiarnfc && + git commit -m "add l.Adiarnfc" +' +# This will test git log +test_expect_success "git log f.Adiar" ' + git log f.$Adiarnfc > f.Adiarnfc.log && + git log f.$Adiarnfd > f.Adiarnfd.log && + test -s f.Adiarnfc.log && + test -s f.Adiarnfd.log && + test_cmp f.Adiarnfc.log f.Adiarnfd.log && + rm f.Adiarnfc.log f.Adiarnfd.log +' +# This will test git ls-files +test_expect_success "git lsfiles f.Adiar" ' + git ls-files f.$Adiarnfc > f.Adiarnfc.log && + git ls-files f.$Adiarnfd > f.Adiarnfd.log && + test -s f.Adiarnfc.log && + test -s f.Adiarnfd.log && + test_cmp f.Adiarnfc.log f.Adiarnfd.log && + rm f.Adiarnfc.log f.Adiarnfd.log +' +# This will test git mv +test_expect_success "git mv" ' + git mv f.$Adiarnfd f.$Odiarnfc && + git mv d.$Adiarnfd d.$Odiarnfc && + git mv l.$Adiarnfd l.$Odiarnfc && + git commit -m "mv Adiarnfd Odiarnfc" +' +# Files can be checked out as nfc +# And the link has been corrected from nfd to nfc +test_expect_success "git checkout nfc" ' + rm f.$Odiarnfc && + git checkout f.$Odiarnfc +' +# Make it possible to checkout files with their NFD names +test_expect_success "git checkout file nfd" ' + rm -f f.* && + git checkout f.$Odiarnfd +' +# Make it possible to checkout links with their NFD names +test_expect_success "git checkout link nfd" ' + rm l.* && + git checkout l.$Odiarnfd +' +test_expect_success "setup case mac2" ' + git checkout master && + git reset --hard && + git checkout -b mac_os_2 +' +# This will test nfd2nfc in git commit +test_expect_success "commit file d2.Adiarnfd/f.Adiarnfd" ' + mkdir d2.$Adiarnfd && + echo d2.$Adiarnfd/f.$Adiarnfd >d2.$Adiarnfd/f.$Adiarnfd && + git add d2.$Adiarnfd/f.$Adiarnfd && + git commit -m "add d2.$Adiarnfd/f.$Adiarnfd" -- d2.$Adiarnfd/f.$Adiarnfd +' +test_expect_success "setup for long decomposed filename" ' + git checkout master && + git reset --hard && + git checkout -b mac_os_long_nfd_fn +' +test_expect_success "Add long decomposed filename" ' + echo longd >$Alongd && + git add * && + git commit -m "Long filename" +' +test_expect_success "setup for long precomposed filename" ' + git checkout master && + git reset --hard && + git checkout -b mac_os_long_nfc_fn +' +test_expect_success "Add long precomposed filename" ' + echo longc >$Alongc && + git add * && + git commit -m "Long filename" +' +# Test if the global core.precomposeunicode stops autosensing +# Must be the last test case +test_expect_success "respect git config --global core.precomposeunicode" ' + git config --global core.precomposeunicode true && + rm -rf .git && + git init && + precomposeunicode=`git config core.precomposeunicode` && + test "$precomposeunicode" = "true" +' + +test_done diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh index 6ddd46915d..8de36b7d12 100755 --- a/t/t4000-diff-format.sh +++ b/t/t4000-diff-format.sh @@ -15,17 +15,17 @@ line 3' cat path0 >path1 chmod +x path1 -test_expect_success \ - 'update-index --add two files with and without +x.' \ - 'git update-index --add path0 path1' +test_expect_success 'update-index --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' +test_expect_success 'git diff-files -p after editing work tree.' ' + git diff-files -p >actual +' # that's as far as it comes if [ "$(git config --get core.filemode)" = false ] @@ -55,8 +55,38 @@ deleted file mode 100755 -line 3 EOF -test_expect_success \ - 'validate git diff-files -p output.' \ - 'compare_diff_patch current expected' +test_expect_success 'validate git diff-files -p output.' ' + compare_diff_patch expected actual +' + +test_expect_success 'git diff-files -s after editing work tree' ' + git diff-files -s >actual 2>err && + test_must_be_empty actual && + test_must_be_empty err +' + +test_expect_success 'git diff-files --no-patch as synonym for -s' ' + git diff-files --no-patch >actual 2>err && + test_must_be_empty actual && + test_must_be_empty err +' + +test_expect_success 'git diff-files --no-patch --patch shows the patch' ' + git diff-files --no-patch --patch >actual && + compare_diff_patch expected actual +' + +test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' ' + git diff-files --no-patch --patch-with-raw >actual && + grep -q "^:100644 100755 .* 0000000000000000000000000000000000000000 M path0\$" actual && + tail -n +4 actual >actual-patch && + compare_diff_patch expected actual-patch +' + +test_expect_success 'git diff-files --patch --no-patch does not show the patch' ' + git diff-files --patch --no-patch >actual 2>err && + test_must_be_empty actual && + test_must_be_empty err +' test_done diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index 844277cfa6..2f327b7495 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -102,4 +102,58 @@ test_expect_success 'setup for many rename source candidates' ' grep warning actual.err ' +test_expect_success 'rename pretty print with nothing in common' ' + mkdir -p a/b/ && + : >a/b/c && + git add a/b/c && + git commit -m "create a/b/c" && + mkdir -p c/b/ && + git mv a/b/c c/b/a && + git commit -m "a/b/c -> c/b/a" && + git diff -M --summary HEAD^ HEAD >output && + test_i18ngrep " a/b/c => c/b/a " output && + git diff -M --stat HEAD^ HEAD >output && + test_i18ngrep " a/b/c => c/b/a " output +' + +test_expect_success 'rename pretty print with common prefix' ' + mkdir -p c/d && + git mv c/b/a c/d/e && + git commit -m "c/b/a -> c/d/e" && + git diff -M --summary HEAD^ HEAD >output && + test_i18ngrep " c/{b/a => d/e} " output && + git diff -M --stat HEAD^ HEAD >output && + test_i18ngrep " c/{b/a => d/e} " output +' + +test_expect_success 'rename pretty print with common suffix' ' + mkdir d && + git mv c/d/e d/e && + git commit -m "c/d/e -> d/e" && + git diff -M --summary HEAD^ HEAD >output && + test_i18ngrep " {c/d => d}/e " output && + git diff -M --stat HEAD^ HEAD >output && + test_i18ngrep " {c/d => d}/e " output +' + +test_expect_success 'rename pretty print with common prefix and suffix' ' + mkdir d/f && + git mv d/e d/f/e && + git commit -m "d/e -> d/f/e" && + git diff -M --summary HEAD^ HEAD >output && + test_i18ngrep " d/{ => f}/e " output && + git diff -M --stat HEAD^ HEAD >output && + test_i18ngrep " d/{ => f}/e " output +' + +test_expect_success 'rename pretty print common prefix and suffix overlap' ' + mkdir d/f/f && + git mv d/f/e d/f/f/e && + git commit -m "d/f/e d/f/f/e" && + git diff -M --summary HEAD^ HEAD >output && + test_i18ngrep " d/f/{ => f}/e " output && + git diff -M --stat HEAD^ HEAD >output && + test_i18ngrep " d/f/{ => f}/e " output +' + test_done diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh index ff8c2f7532..05911492ca 100755 --- a/t/t4006-diff-mode.sh +++ b/t/t4006-diff-mode.sh @@ -8,23 +8,52 @@ 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' - -test_expect_success \ - 'chmod' \ - 'test_chmod +x rezrov && - git diff-index $tree >current' - -sed -e 's/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /' <current >check -echo ":100644 100755 X X M rezrov" >expected - -test_expect_success \ - 'verify' \ - 'test_cmp expected check' +sed_script='s/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /' + +test_expect_success 'setup' ' + echo frotz >rezrov && + git update-index --add rezrov && + tree=`git write-tree` && + echo $tree +' + +test_expect_success 'chmod' ' + test_chmod +x rezrov && + git diff-index $tree >current && + sed -e "$sed_script" <current >check && + echo ":100644 100755 X X M rezrov" >expected && + test_cmp expected check +' + +test_expect_success 'prepare binary file' ' + git commit -m rezrov && + printf "\00\01\02\03\04\05\06" >binbin && + git add binbin && + git commit -m binbin +' + +# test_expect_success '--stat output after text chmod' ' +# test_chmod -x rezrov && +# echo " 0 files changed" >expect && +# git diff HEAD --stat >actual && +# test_i18ncmp expect actual +# ' +# +# test_expect_success '--shortstat output after text chmod' ' +# git diff HEAD --shortstat >actual && +# test_i18ncmp expect actual +# ' +# +# test_expect_success '--stat output after binary chmod' ' +# test_chmod +x binbin && +# echo " 0 files changed" >expect && +# git diff HEAD --stat >actual && +# test_i18ncmp expect actual +# ' +# +# test_expect_success '--shortstat output after binary chmod' ' +# git diff HEAD --shortstat >actual && +# test_i18ncmp expect actual +# ' test_done diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh index 73b4a24f5e..27e98a8f9d 100755 --- a/t/t4008-diff-break-rewrite.sh +++ b/t/t4008-diff-break-rewrite.sh @@ -99,11 +99,11 @@ test_expect_success \ 'validate result of -B -M (#4)' \ 'compare_diff_raw expected current' -test_expect_success SYMLINKS \ +test_expect_success \ 'make file0 into something completely different' \ 'rm -f file0 && - ln -s frotz file0 && - git update-index file0 file1' + test_ln_s_add frotz file0 && + git update-index file1' test_expect_success \ 'run diff with -B' \ @@ -114,7 +114,7 @@ cat >expected <<\EOF :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1 EOF -test_expect_success SYMLINKS \ +test_expect_success \ 'validate result of -B (#5)' \ 'compare_diff_raw expected current' @@ -129,7 +129,7 @@ cat >expected <<\EOF :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R file0 file1 EOF -test_expect_success SYMLINKS \ +test_expect_success \ 'validate result of -B -M (#6)' \ 'compare_diff_raw expected current' @@ -144,7 +144,7 @@ cat >expected <<\EOF :100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M file1 EOF -test_expect_success SYMLINKS \ +test_expect_success \ 'validate result of -M (#7)' \ 'compare_diff_raw expected current' diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh index f0d5041c11..13e7f621ab 100755 --- a/t/t4011-diff-symlink.sh +++ b/t/t4011-diff-symlink.sh @@ -9,7 +9,7 @@ test_description='Test diff of symlinks. . ./test-lib.sh . "$TEST_DIRECTORY"/diff-lib.sh -test_expect_success SYMLINKS 'diff new symlink and file' ' +test_expect_success 'diff new symlink and file' ' cat >expected <<-\EOF && diff --git a/frotz b/frotz new file mode 120000 @@ -27,22 +27,25 @@ test_expect_success SYMLINKS 'diff new symlink and file' ' @@ -0,0 +1 @@ +xyzzy EOF - ln -s xyzzy frotz && - echo xyzzy >nitfol && + + # the empty tree git update-index && tree=$(git write-tree) && - git update-index --add frotz nitfol && + + test_ln_s_add xyzzy frotz && + echo xyzzy >nitfol && + git update-index --add nitfol && GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current && compare_diff_patch expected current ' -test_expect_success SYMLINKS 'diff unchanged symlink and file' ' +test_expect_success 'diff unchanged symlink and file' ' tree=$(git write-tree) && git update-index frotz nitfol && test -z "$(git diff-index --name-only $tree)" ' -test_expect_success SYMLINKS 'diff removed symlink and file' ' +test_expect_success 'diff removed symlink and file' ' cat >expected <<-\EOF && diff --git a/frotz b/frotz deleted file mode 120000 @@ -66,12 +69,18 @@ test_expect_success SYMLINKS 'diff removed symlink and file' ' compare_diff_patch expected current ' -test_expect_success SYMLINKS 'diff identical, but newly created symlink and file' ' +test_expect_success 'diff identical, but newly created symlink and file' ' >expected && rm -f frotz nitfol && echo xyzzy >nitfol && test-chmtime +10 nitfol && - ln -s xyzzy frotz && + if test_have_prereq SYMLINKS + then + ln -s xyzzy frotz + else + printf xyzzy >frotz + # the symlink property propagates from the index + fi && git diff-index -M -p $tree >current && compare_diff_patch expected current && @@ -80,7 +89,7 @@ test_expect_success SYMLINKS 'diff identical, but newly created symlink and file compare_diff_patch expected current ' -test_expect_success SYMLINKS 'diff different symlink and file' ' +test_expect_success 'diff different symlink and file' ' cat >expected <<-\EOF && diff --git a/frotz b/frotz index 7c465af..df1db54 120000 @@ -100,7 +109,13 @@ test_expect_success SYMLINKS 'diff different symlink and file' ' +yxyyz EOF rm -f frotz && - ln -s yxyyz frotz && + if test_have_prereq SYMLINKS + then + ln -s yxyyz frotz + else + printf yxyyz >frotz + # the symlink property propagates from the index + fi && echo yxyyz >nitfol && git diff-index -M -p $tree >current && compare_diff_patch expected current diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh index 2d9f9a0cf1..1215ae544b 100755 --- a/t/t4012-diff-binary.sh +++ b/t/t4012-diff-binary.sh @@ -8,13 +8,21 @@ 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 "$TEST_DIRECTORY"/test-binary-1.png >b && - echo git >c && - cat b b >d' +cat >expect.binary-numstat <<\EOF +1 1 a +- - b +1 1 c +- - d +EOF + +test_expect_success 'prepare repository' ' + echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d && + git update-index --add a b c d && + echo git >a && + cat "$TEST_DIRECTORY"/test-binary-1.png >b && + echo git >c && + cat b b >d +' cat > expected <<\EOF a | 2 +- @@ -23,59 +31,74 @@ cat > expected <<\EOF d | Bin 4 files changed, 2 insertions(+), 2 deletions(-) EOF -test_expect_success 'diff without --binary' \ - 'git diff | git apply --stat --summary >current && - test_cmp expected current' +test_expect_success 'apply --stat output for binary file change' ' + git diff >diff && + git apply --stat --summary <diff >current && + test_i18ncmp expected current +' + +test_expect_success 'diff --shortstat output for binary file change' ' + tail -n 1 expected >expect && + git diff --shortstat >current && + test_i18ncmp expect current +' -test_expect_success 'diff with --binary' \ - 'git diff --binary | git apply --stat --summary >current && - test_cmp expected current' +test_expect_success 'diff --shortstat output for binary file change only' ' + echo " 1 file changed, 0 insertions(+), 0 deletions(-)" >expected && + git diff --shortstat -- b >current && + test_i18ncmp expected current +' + +test_expect_success 'apply --numstat notices binary file change' ' + git diff >diff && + git apply --numstat <diff >current && + test_cmp expect.binary-numstat current +' + +test_expect_success 'apply --numstat understands diff --binary format' ' + git diff --binary >diff && + git apply --numstat <diff >current && + test_cmp expect.binary-numstat current +' # 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 C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' ' + git diff >output && + sed -e "s/-CIT/xCIT/" <output >broken && + test_must_fail git apply --stat --summary broken 2>detected && + detected=`cat detected` && + detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` && + detected=`sed -ne "${detected}p" broken` && + test "$detected" = xCIT +' + +test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' ' + git diff --binary | sed -e "s/-CIT/xCIT/" >broken && + test_must_fail git apply --stat --summary broken 2>detected && + detected=`cat detected` && + detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` && + detected=`sed -ne "${detected}p" broken` && + test "$detected" = xCIT +' test_expect_success 'initial commit' 'git commit -a -m initial' # Try removal (b), modification (d), and creation (e). -test_expect_success 'diff-index with --binary' \ - 'echo AIT >a && mv b e && echo CIT >c && cat e >d && - git update-index --add --remove a b c d e && - tree0=`git write-tree` && - git diff --cached --binary >current && - git apply --stat --summary current' - -test_expect_success 'apply binary patch' \ - 'git reset --hard && - git apply --binary --index <current && - tree1=`git write-tree` && - test "$tree1" = "$tree0"' +test_expect_success 'diff-index with --binary' ' + echo AIT >a && mv b e && echo CIT >c && cat e >d && + git update-index --add --remove a b c d e && + tree0=`git write-tree` && + git diff --cached --binary >current && + git apply --stat --summary current +' + +test_expect_success 'apply binary patch' ' + git reset --hard && + git apply --binary --index <current && + tree1=`git write-tree` && + test "$tree1" = "$tree0" +' test_expect_success 'diff --no-index with binary creation' ' echo Q | q_to_nul >binary && @@ -90,4 +113,23 @@ test_expect_success 'diff --no-index with binary creation' ' test_cmp expected actual ' +cat >expect <<EOF + binfile | Bin 0 -> 1026 bytes + textfile | 10000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +EOF + +test_expect_success 'diff --stat with binary files and big change count' ' + printf "\01\00%1024d" 1 >binfile && + git add binfile && + i=0 && + while test $i -lt 10000; do + echo $i && + i=$(($i + 1)) + done >textfile && + git add textfile && + git diff --cached --stat binfile textfile >output && + grep " | " output >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 93a6f20871..e77c09c37e 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -128,7 +128,12 @@ do } >"$actual" && if test -f "$expect" then - test_cmp "$expect" "$actual" && + case $cmd in + *format-patch* | *-stat*) + test_i18ncmp "$expect" "$actual";; + *) + test_cmp "$expect" "$actual";; + esac && rm -f "$actual" else # this is to help developing new tests. diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master index 2f8560c369..9951e3677d 100644 --- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master @@ -1,7 +1,7 @@ $ git diff-tree --cc --patch-with-stat --summary master 59d314ad6f356dd08601a4cd5e530381da3e3c64 - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) diff --cc dir/sub diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side index 72e03c14fb..cec33fa3f0 100644 --- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side +++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side @@ -1,8 +1,8 @@ $ git diff-tree --cc --patch-with-stat --summary side c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master index 8b357d964b..db3c0a7b2c 100644 --- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master +++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master @@ -1,7 +1,7 @@ $ git diff-tree --cc --patch-with-stat master 59d314ad6f356dd08601a4cd5e530381da3e3c64 - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) diff --cc dir/sub diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master index e0568d6883..d019867dd9 100644 --- a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master +++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master @@ -1,6 +1,6 @@ $ git diff-tree --cc --stat --summary master 59d314ad6f356dd08601a4cd5e530381da3e3c64 - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) $ diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_side b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side index 5afc8239a1..12b2eee17e 100644 --- a/t/t4013/diff.diff-tree_--cc_--stat_--summary_side +++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side @@ -1,8 +1,8 @@ $ git diff-tree --cc --stat --summary side c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 $ diff --git a/t/t4013/diff.diff-tree_--cc_--stat_master b/t/t4013/diff.diff-tree_--cc_--stat_master index f48367a89a..40b91796b3 100644 --- a/t/t4013/diff.diff-tree_--cc_--stat_master +++ b/t/t4013/diff.diff-tree_--cc_--stat_master @@ -1,6 +1,6 @@ $ git diff-tree --cc --stat master 59d314ad6f356dd08601a4cd5e530381da3e3c64 - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) $ diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial index 590864c2d7..817ed06f82 100644 --- a/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial +++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial @@ -1,8 +1,8 @@ $ git diff-tree --pretty=oneline --root --patch-with-stat initial 444ac553ac7612cc88969031b02b3767fb8a353a Initial - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side index e05e77875c..fe3f6b7c7e 100644 --- a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side +++ b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side @@ -5,9 +5,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial index 0e2c956633..06eb77e386 100644 --- a/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial +++ b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial @@ -5,9 +5,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial index 384fa44ddd..680eab5f27 100644 --- a/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial +++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial @@ -5,9 +5,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 dir/sub create mode 100644 file0 diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial b/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial index 10384a83d3..9722d1b3a7 100644 --- a/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial +++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial @@ -5,8 +5,8 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) $ diff --git a/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial index f57062ea07..ad69ffe647 100644 --- a/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial +++ b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial @@ -1,8 +1,8 @@ $ git diff-tree --root --patch-with-stat initial 444ac553ac7612cc88969031b02b3767fb8a353a - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_master b/t/t4013/diff.diff-tree_-c_--stat_--summary_master index 7088683444..81c3021541 100644 --- a/t/t4013/diff.diff-tree_-c_--stat_--summary_master +++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_master @@ -1,6 +1,6 @@ $ git diff-tree -c --stat --summary master 59d314ad6f356dd08601a4cd5e530381da3e3c64 - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) $ diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_side b/t/t4013/diff.diff-tree_-c_--stat_--summary_side index ef216abb1d..e8dc12bfbf 100644 --- a/t/t4013/diff.diff-tree_-c_--stat_--summary_side +++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_side @@ -1,8 +1,8 @@ $ git diff-tree -c --stat --summary side c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 $ diff --git a/t/t4013/diff.diff-tree_-c_--stat_master b/t/t4013/diff.diff-tree_-c_--stat_master index ad19f103eb..89d59b1548 100644 --- a/t/t4013/diff.diff-tree_-c_--stat_master +++ b/t/t4013/diff.diff-tree_-c_--stat_master @@ -1,6 +1,6 @@ $ git diff-tree -c --stat master 59d314ad6f356dd08601a4cd5e530381da3e3c64 - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) $ diff --git a/t/t4013/diff.diff_--patch-with-stat_-r_initial..side b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side index ddad917ae8..be8d1ea1bd 100644 --- a/t/t4013/diff.diff_--patch-with-stat_-r_initial..side +++ b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side @@ -1,7 +1,7 @@ $ git diff --patch-with-stat -r initial..side - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.diff_--patch-with-stat_initial..side b/t/t4013/diff.diff_--patch-with-stat_initial..side index bdbd114d8e..5424e6d566 100644 --- a/t/t4013/diff.diff_--patch-with-stat_initial..side +++ b/t/t4013/diff.diff_--patch-with-stat_initial..side @@ -1,7 +1,7 @@ $ git diff --patch-with-stat initial..side - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.diff_--stat_initial..side b/t/t4013/diff.diff_--stat_initial..side index 6d08f3d355..b7741e2b83 100644 --- a/t/t4013/diff.diff_--stat_initial..side +++ b/t/t4013/diff.diff_--stat_initial..side @@ -1,6 +1,6 @@ $ git diff --stat initial..side - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) $ diff --git a/t/t4013/diff.diff_-r_--stat_initial..side b/t/t4013/diff.diff_-r_--stat_initial..side index 2ddb2540e6..5d514f55b9 100644 --- a/t/t4013/diff.diff_-r_--stat_initial..side +++ b/t/t4013/diff.diff_-r_--stat_initial..side @@ -1,6 +1,6 @@ $ git diff -r --stat initial..side - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) $ diff --git a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side index 3cab049f7d..547ca065a5 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side +++ b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side @@ -12,9 +12,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master index 564a4d38f2..52fedc179e 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master @@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -121,9 +121,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ index 4f28460b83..1c3cde251b 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ @@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side index b10cc2e251..4717bd8313 100644 --- a/t/t4013/diff.format-patch_--attach_--stdout_initial..side +++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side @@ -12,9 +12,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master index a976a8aaf4..02c4db7ec5 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master @@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -121,9 +121,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master index b4fd66477a..c7677c5951 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master @@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -121,9 +121,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..master index 0d31036e7f..5b3e34e2c0 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master @@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -121,9 +121,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ index 18d4714423..d13f8a8128 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ @@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -73,8 +73,8 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ index 29e00ab8af..caec5537de 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ @@ -14,9 +14,9 @@ Content-Transfer-Encoding: 8bit This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..side b/t/t4013/diff.format-patch_--inline_--stdout_initial..side index 3572f20b5d..d3a6762130 100644 --- a/t/t4013/diff.format-patch_--inline_--stdout_initial..side +++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..side @@ -12,9 +12,9 @@ Content-Type: text/plain; charset=UTF-8; format=fixed Content-Transfer-Encoding: 8bit --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ index 54cdcdab40..244d964fc6 100644 --- a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ +++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ @@ -10,10 +10,10 @@ A U Thor (2): Second Third - dir/sub | 4 ++++ - file0 | 3 +++ - file1 | 3 +++ - file2 | 3 --- + dir/sub | 4 ++++ + file0 | 3 +++ + file1 | 3 +++ + file2 | 3 --- 4 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 file1 delete mode 100644 file2 @@ -28,9 +28,9 @@ Subject: [DIFFERENT_PREFIX 1/2] Second This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -73,8 +73,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000 Subject: [DIFFERENT_PREFIX 2/2] Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 diff --git a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master index 23194ebdaa..bfc287a147 100644 --- a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master +++ b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master @@ -6,9 +6,9 @@ Subject: [PATCH] Second This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -51,8 +51,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000 Subject: [PATCH] Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -85,9 +85,9 @@ Date: Mon, 26 Jun 2006 00:03:00 +0000 Subject: [PATCH] Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master index 78f1a80a97..568f6f584e 100644 --- a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master +++ b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master @@ -6,9 +6,9 @@ Subject: [PATCH 1/3] Second This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -51,8 +51,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000 Subject: [PATCH 2/3] Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -85,9 +85,9 @@ Date: Mon, 26 Jun 2006 00:03:00 +0000 Subject: [PATCH 3/3] Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--stdout_initial..master b/t/t4013/diff.format-patch_--stdout_initial..master index a3dab7f773..5f0352f9f7 100644 --- a/t/t4013/diff.format-patch_--stdout_initial..master +++ b/t/t4013/diff.format-patch_--stdout_initial..master @@ -6,9 +6,9 @@ Subject: [PATCH 1/3] Second This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -51,8 +51,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000 Subject: [PATCH 2/3] Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -85,9 +85,9 @@ Date: Mon, 26 Jun 2006 00:03:00 +0000 Subject: [PATCH 3/3] Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.format-patch_--stdout_initial..master^ b/t/t4013/diff.format-patch_--stdout_initial..master^ index 39f4a3f2d1..2ae454d807 100644 --- a/t/t4013/diff.format-patch_--stdout_initial..master^ +++ b/t/t4013/diff.format-patch_--stdout_initial..master^ @@ -6,9 +6,9 @@ Subject: [PATCH 1/2] Second This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -51,8 +51,8 @@ Date: Mon, 26 Jun 2006 00:02:00 +0000 Subject: [PATCH 2/2] Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 diff --git a/t/t4013/diff.format-patch_--stdout_initial..side b/t/t4013/diff.format-patch_--stdout_initial..side index 88109209db..a7d52fbeea 100644 --- a/t/t4013/diff.format-patch_--stdout_initial..side +++ b/t/t4013/diff.format-patch_--stdout_initial..side @@ -5,9 +5,9 @@ Date: Mon, 26 Jun 2006 00:03:00 +0000 Subject: [PATCH] Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ index 4085bbde87..a18f1472a9 100644 --- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ +++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ @@ -12,7 +12,7 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub @@ -31,7 +31,7 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub @@ -53,7 +53,7 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master index 458627953e..ae425c4672 100644 --- a/t/t4013/diff.log_--patch-with-stat_master +++ b/t/t4013/diff.log_--patch-with-stat_master @@ -12,9 +12,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/dir/sub b/dir/sub @@ -54,8 +54,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) diff --git a/dir/sub b/dir/sub @@ -86,9 +86,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_ index 6e172cfadd..d5207cadf4 100644 --- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ +++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_ @@ -12,7 +12,7 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub @@ -31,7 +31,7 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub @@ -53,7 +53,7 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master index 48b0d4b91d..0fc1e8cd71 100644 --- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master @@ -6,8 +6,8 @@ Date: Mon Jun 26 00:04:00 2006 +0000 Merge branch 'side' - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) diff --cc dir/sub @@ -44,9 +44,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 @@ -87,8 +87,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -120,9 +120,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -162,9 +162,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 dir/sub create mode 100644 file0 diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master index f9dc5122e2..dffc09dde9 100644 --- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master @@ -12,9 +12,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 @@ -55,8 +55,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -88,9 +88,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -130,9 +130,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 dir/sub create mode 100644 file0 diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master index 0807ece234..55aa98012d 100644 --- a/t/t4013/diff.log_--root_--patch-with-stat_master +++ b/t/t4013/diff.log_--root_--patch-with-stat_master @@ -12,9 +12,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/dir/sub b/dir/sub @@ -54,8 +54,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) diff --git a/dir/sub b/dir/sub @@ -86,9 +86,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dir/sub b/dir/sub @@ -127,9 +127,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master index 84f5ef6911..019d85f7de 100644 --- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master +++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master @@ -6,8 +6,8 @@ Date: Mon Jun 26 00:04:00 2006 +0000 Merge branch 'side' - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) diff --combined dir/sub @@ -44,9 +44,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 @@ -87,8 +87,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -120,9 +120,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -162,9 +162,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 dir/sub create mode 100644 file0 diff --git a/t/t4013/diff.show_--patch-with-stat_--summary_side b/t/t4013/diff.show_--patch-with-stat_--summary_side index e60384d24d..95a474ef1d 100644 --- a/t/t4013/diff.show_--patch-with-stat_--summary_side +++ b/t/t4013/diff.show_--patch-with-stat_--summary_side @@ -5,9 +5,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 diff --git a/t/t4013/diff.show_--patch-with-stat_side b/t/t4013/diff.show_--patch-with-stat_side index a3a3255fd3..974e99be82 100644 --- a/t/t4013/diff.show_--patch-with-stat_side +++ b/t/t4013/diff.show_--patch-with-stat_side @@ -5,9 +5,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.show_--stat_--summary_side b/t/t4013/diff.show_--stat_--summary_side index d16f464aca..a71492f9bf 100644 --- a/t/t4013/diff.show_--stat_--summary_side +++ b/t/t4013/diff.show_--stat_--summary_side @@ -5,9 +5,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 $ diff --git a/t/t4013/diff.show_--stat_side b/t/t4013/diff.show_--stat_side index 6300c0535f..9be712458f 100644 --- a/t/t4013/diff.show_--stat_side +++ b/t/t4013/diff.show_--stat_side @@ -5,8 +5,8 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) $ diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ index 16ae54345f..c8b6af2f43 100644 --- a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ +++ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ @@ -5,7 +5,7 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub @@ -24,7 +24,7 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub @@ -46,7 +46,7 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master b/t/t4013/diff.whatchanged_--patch-with-stat_master index f3e45ec270..1ac431ba92 100644 --- a/t/t4013/diff.whatchanged_--patch-with-stat_master +++ b/t/t4013/diff.whatchanged_--patch-with-stat_master @@ -5,9 +5,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/dir/sub b/dir/sub @@ -47,8 +47,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) diff --git a/dir/sub b/dir/sub @@ -79,9 +79,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ index c77f0bc320..b30c28588f 100644 --- a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ +++ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ @@ -5,7 +5,7 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub @@ -24,7 +24,7 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub @@ -46,7 +46,7 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ + dir/sub | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master index 8d03efea6c..30aae7817b 100644 --- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master @@ -6,8 +6,8 @@ Date: Mon Jun 26 00:04:00 2006 +0000 Merge branch 'side' - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) diff --cc dir/sub @@ -44,9 +44,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 @@ -87,8 +87,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -120,9 +120,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -162,9 +162,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 dir/sub create mode 100644 file0 diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master index 1874d0616c..db90e51525 100644 --- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master @@ -5,9 +5,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 @@ -48,8 +48,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -81,9 +81,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -123,9 +123,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 dir/sub create mode 100644 file0 diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master index 5211ff2a75..9a6cc92ce7 100644 --- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master +++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master @@ -5,9 +5,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/dir/sub b/dir/sub @@ -47,8 +47,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) diff --git a/dir/sub b/dir/sub @@ -79,9 +79,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/dir/sub b/dir/sub @@ -120,9 +120,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) diff --git a/dir/sub b/dir/sub diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master index ad30245a59..d1d32bd34c 100644 --- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master +++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master @@ -6,8 +6,8 @@ Date: Mon Jun 26 00:04:00 2006 +0000 Merge branch 'side' - dir/sub | 2 ++ - file0 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ 2 files changed, 5 insertions(+) diff --combined dir/sub @@ -44,9 +44,9 @@ Date: Mon Jun 26 00:03:00 2006 +0000 Side --- - dir/sub | 2 ++ - file0 | 3 +++ - file3 | 4 ++++ + dir/sub | 2 ++ + file0 | 3 +++ + file3 | 4 ++++ 3 files changed, 9 insertions(+) create mode 100644 file3 @@ -87,8 +87,8 @@ Date: Mon Jun 26 00:02:00 2006 +0000 Third --- - dir/sub | 2 ++ - file1 | 3 +++ + dir/sub | 2 ++ + file1 | 3 +++ 2 files changed, 5 insertions(+) create mode 100644 file1 @@ -120,9 +120,9 @@ Date: Mon Jun 26 00:01:00 2006 +0000 This is the second commit. --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 --- + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 --- 3 files changed, 5 insertions(+), 3 deletions(-) delete mode 100644 file2 @@ -162,9 +162,9 @@ Date: Mon Jun 26 00:00:00 2006 +0000 Initial --- - dir/sub | 2 ++ - file0 | 3 +++ - file2 | 3 +++ + dir/sub | 2 ++ + file0 | 3 +++ + file2 | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 dir/sub create mode 100644 file0 diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 7dfe716cf9..73194b2c3d 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -110,73 +110,107 @@ test_expect_success 'replay did not screw up the log message' ' test_expect_success 'extra headers' ' - git config format.headers "To: R. E. Cipient <rcipient@example.com> + git config format.headers "To: R E Cipient <rcipient@example.com> " && - git config --add format.headers "Cc: S. E. Cipient <scipient@example.com> + git config --add format.headers "Cc: S E Cipient <scipient@example.com> " && git format-patch --stdout master..side > patch2 && sed -e "/^\$/q" patch2 > hdrs2 && - grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs2 && - grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs2 + grep "^To: R E Cipient <rcipient@example.com>\$" hdrs2 && + grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs2 ' test_expect_success 'extra headers without newlines' ' - git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" && - git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" && + git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" && + git config --add format.headers "Cc: S E Cipient <scipient@example.com>" && git format-patch --stdout master..side >patch3 && sed -e "/^\$/q" patch3 > hdrs3 && - grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs3 && - grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs3 + grep "^To: R E Cipient <rcipient@example.com>\$" hdrs3 && + grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs3 ' test_expect_success 'extra headers with multiple To:s' ' - git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" && - git config --add format.headers "To: S. E. Cipient <scipient@example.com>" && + git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" && + git config --add format.headers "To: S E Cipient <scipient@example.com>" && git format-patch --stdout master..side > patch4 && sed -e "/^\$/q" patch4 > hdrs4 && - grep "^To: R. E. Cipient <rcipient@example.com>,\$" hdrs4 && - grep "^ *S. E. Cipient <scipient@example.com>\$" hdrs4 + grep "^To: R E Cipient <rcipient@example.com>,\$" hdrs4 && + grep "^ *S E Cipient <scipient@example.com>\$" hdrs4 ' -test_expect_success 'additional command line cc' ' +test_expect_success 'additional command line cc (ascii)' ' - git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" && + git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" && + git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 && + grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 && + grep "^ *S E Cipient <scipient@example.com>\$" patch5 +' + +test_expect_failure 'additional command line cc (rfc822)' ' + + git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" && git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 && - grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch5 && - grep "^ *S. E. Cipient <scipient@example.com>\$" patch5 + grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 && + grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" patch5 ' test_expect_success 'command line headers' ' git config --unset-all format.headers && - git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 && - grep "^Cc: R. E. Cipient <rcipient@example.com>\$" patch6 + git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 && + grep "^Cc: R E Cipient <rcipient@example.com>\$" patch6 ' test_expect_success 'configuration headers and command line headers' ' - git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" && - git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 && - grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch7 && - grep "^ *S. E. Cipient <scipient@example.com>\$" patch7 + git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" && + git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 && + grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch7 && + grep "^ *S E Cipient <scipient@example.com>\$" patch7 ' -test_expect_success 'command line To: header' ' +test_expect_success 'command line To: header (ascii)' ' git config --unset-all format.headers && + git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 && + grep "^To: R E Cipient <rcipient@example.com>\$" patch8 +' + +test_expect_failure 'command line To: header (rfc822)' ' + git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 && - grep "^To: R. E. Cipient <rcipient@example.com>\$" patch8 + grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch8 +' + +test_expect_failure 'command line To: header (rfc2047)' ' + + git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 && + grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch8 +' + +test_expect_success 'configuration To: header (ascii)' ' + + git config format.to "R E Cipient <rcipient@example.com>" && + git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 && + grep "^To: R E Cipient <rcipient@example.com>\$" patch9 ' -test_expect_success 'configuration To: header' ' +test_expect_failure 'configuration To: header (rfc822)' ' git config format.to "R. E. Cipient <rcipient@example.com>" && git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 && - grep "^To: R. E. Cipient <rcipient@example.com>\$" patch9 + grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch9 +' + +test_expect_failure 'configuration To: header (rfc2047)' ' + + git config format.to "R Ä Cipient <rcipient@example.com>" && + git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 && + grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch9 ' # check_patch <patch>: Verify that <patch> looks like a half-sane @@ -190,11 +224,11 @@ check_patch () { test_expect_success '--no-to overrides config.to' ' git config --replace-all format.to \ - "R. E. Cipient <rcipient@example.com>" && + "R E Cipient <rcipient@example.com>" && git format-patch --no-to --stdout master..side | sed -e "/^\$/q" >patch10 && check_patch patch10 && - ! grep "^To: R. E. Cipient <rcipient@example.com>\$" patch10 + ! grep "^To: R E Cipient <rcipient@example.com>\$" patch10 ' test_expect_success '--no-to and --to replaces config.to' ' @@ -212,21 +246,21 @@ test_expect_success '--no-to and --to replaces config.to' ' test_expect_success '--no-cc overrides config.cc' ' git config --replace-all format.cc \ - "C. E. Cipient <rcipient@example.com>" && + "C E Cipient <rcipient@example.com>" && git format-patch --no-cc --stdout master..side | sed -e "/^\$/q" >patch12 && check_patch patch12 && - ! grep "^Cc: C. E. Cipient <rcipient@example.com>\$" patch12 + ! grep "^Cc: C E Cipient <rcipient@example.com>\$" patch12 ' test_expect_success '--no-add-header overrides config.headers' ' git config --replace-all format.headers \ - "Header1: B. E. Cipient <rcipient@example.com>" && + "Header1: B E Cipient <rcipient@example.com>" && git format-patch --no-add-header --stdout master..side | sed -e "/^\$/q" >patch13 && check_patch patch13 && - ! grep "^Header1: B. E. Cipient <rcipient@example.com>\$" patch13 + ! grep "^Header1: B E Cipient <rcipient@example.com>\$" patch13 ' test_expect_success 'multiple files' ' @@ -237,6 +271,22 @@ test_expect_success 'multiple files' ' ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch ' +test_expect_success 'reroll count' ' + rm -fr patches && + git format-patch -o patches --cover-letter --reroll-count 4 master..side >list && + ! grep -v "^patches/v4-000[0-3]-" list && + sed -n -e "/^Subject: /p" $(cat list) >subjects && + ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects +' + +test_expect_success 'reroll count (-v)' ' + rm -fr patches && + git format-patch -o patches --cover-letter -v4 master..side >list && + ! grep -v "^patches/v4-000[0-3]-" list && + sed -n -e "/^Subject: /p" $(cat list) >subjects && + ! grep -v "^Subject: \[PATCH v4 [0-3]/3\] " subjects +' + check_threading () { expect="$1" && shift && @@ -518,11 +568,6 @@ test_expect_success 'shortlog of cover-letter wraps overly-long onelines' ' ' cat > expect << EOF ---- - file | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) - -diff --git a/file b/file index 40f36c6..2dc5c23 100644 --- a/file +++ b/file @@ -537,7 +582,9 @@ EOF test_expect_success 'format-patch respects -U' ' git format-patch -U4 -2 && - sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output && + sed -e "1,/^diff/d" -e "/^+5/q" \ + <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch \ + >output && test_cmp expect output ' @@ -619,8 +666,19 @@ test_expect_success 'format-patch --in-reply-to' ' ' test_expect_success 'format-patch --signoff' ' - git format-patch -1 --signoff --stdout | - grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" + git format-patch -1 --signoff --stdout >out && + grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" out +' + +test_expect_success 'format-patch --notes --signoff' ' + git notes --ref test add -m "test message" HEAD && + git format-patch -1 --signoff --stdout --notes=test >out && + # Three dashes must come after S-o-b + ! sed "/^Signed-off-by: /q" out | grep "test message" && + sed "1,/^Signed-off-by: /d" out | grep "test message" && + # Notes message must come after three dashes + ! sed "/^---$/q" out | grep "test message" && + sed "1,/^---$/d" out | grep "test message" ' echo "fatal: --name-only does not make sense" > expect.name-only @@ -684,21 +742,21 @@ test_expect_success 'format-patch --signature --cover-letter' ' test 2 = $(grep "my sig" output | wc -l) ' -test_expect_success 'format.signature="" supresses signatures' ' +test_expect_success 'format.signature="" suppresses signatures' ' git config format.signature "" && git format-patch --stdout -1 >output && check_patch output && ! grep "^-- \$" output ' -test_expect_success 'format-patch --no-signature supresses signatures' ' +test_expect_success 'format-patch --no-signature suppresses signatures' ' git config --unset-all format.signature && git format-patch --stdout --no-signature -1 >output && check_patch output && ! grep "^-- \$" output ' -test_expect_success 'format-patch --signature="" supresses signatures' ' +test_expect_success 'format-patch --signature="" suppresses signatures' ' git format-patch --stdout --signature="" -1 >output && check_patch output && ! grep "^-- \$" output @@ -755,16 +813,14 @@ M64=$M8$M8$M8$M8$M8$M8$M8$M8 M512=$M64$M64$M64$M64$M64$M64$M64$M64 cat >expect <<'EOF' Subject: [PATCH] foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo - bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar - foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo - bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar - foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo - bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar - foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo - bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar - foo bar foo bar foo bar foo bar -EOF -test_expect_success 'format-patch wraps extremely long headers (ascii)' ' + bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo + bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo + bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar + foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar foo bar +EOF +test_expect_success 'format-patch wraps extremely long subject (ascii)' ' echo content >>file && git add file && git commit -m "$M512" && @@ -777,30 +833,32 @@ M8="föö bar " M64=$M8$M8$M8$M8$M8$M8$M8$M8 M512=$M64$M64$M64$M64$M64$M64$M64$M64 cat >expect <<'EOF' -Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= - =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?= -EOF -test_expect_success 'format-patch wraps extremely long headers (rfc2047)' ' +Subject: [PATCH] =?UTF-8?q?f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?= + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?= + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?= + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?= + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?= + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?= + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?= + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?= + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?= + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?= + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?= + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?= + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?= + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?= + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?= + =?UTF-8?q?bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6?= + =?UTF-8?q?=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6?= + =?UTF-8?q?=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f?= + =?UTF-8?q?=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar?= + =?UTF-8?q?=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20bar=20f=C3=B6=C3=B6=20?= + =?UTF-8?q?bar?= +EOF +test_expect_success 'format-patch wraps extremely long subject (rfc2047)' ' rm -rf patches/ && echo content >>file && git add file && @@ -810,52 +868,80 @@ test_expect_success 'format-patch wraps extremely long headers (rfc2047)' ' test_cmp expect subject ' -M8="foo_bar_" -M64=$M8$M8$M8$M8$M8$M8$M8$M8 -cat >expect <<EOF -From: $M64 - <foobar@foo.bar> -EOF -test_expect_success 'format-patch wraps non-quotable headers' ' - rm -rf patches/ && - echo content >>file && - git add file && - git commit -mfoo --author "$M64 <foobar@foo.bar>" && - git format-patch --stdout -1 >patch && - sed -n "/^From: /p; /^ /p; /^$/q" <patch >from && - test_cmp expect from -' - check_author() { echo content >>file && git add file && GIT_AUTHOR_NAME=$1 git commit -m author-check && git format-patch --stdout -1 >patch && - grep ^From: patch >actual && + sed -n "/^From: /p; /^ /p; /^$/q" <patch >actual && test_cmp expect actual } cat >expect <<'EOF' From: "Foo B. Bar" <author@example.com> EOF -test_expect_success 'format-patch quotes dot in headers' ' +test_expect_success 'format-patch quotes dot in from-headers' ' check_author "Foo B. Bar" ' cat >expect <<'EOF' From: "Foo \"The Baz\" Bar" <author@example.com> EOF -test_expect_success 'format-patch quotes double-quote in headers' ' +test_expect_success 'format-patch quotes double-quote in from-headers' ' check_author "Foo \"The Baz\" Bar" ' cat >expect <<'EOF' -From: =?UTF-8?q?"F=C3=B6o=20B.=20Bar"?= <author@example.com> +From: =?UTF-8?q?F=C3=B6o=20Bar?= <author@example.com> EOF -test_expect_success 'rfc2047-encoded headers also double-quote 822 specials' ' +test_expect_success 'format-patch uses rfc2047-encoded from-headers when necessary' ' + check_author "Föo Bar" +' + +cat >expect <<'EOF' +From: =?UTF-8?q?F=C3=B6o=20B=2E=20Bar?= <author@example.com> +EOF +test_expect_success 'rfc2047-encoded from-headers leave no rfc822 specials' ' check_author "Föo B. Bar" ' +cat >expect <<EOF +From: foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_ + <author@example.com> +EOF +test_expect_success 'format-patch wraps moderately long from-header (ascii)' ' + check_author "foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_foo_bar_" +' + +cat >expect <<'EOF' +From: Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar + Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo + Bar Foo Bar Foo Bar Foo Bar <author@example.com> +EOF +test_expect_success 'format-patch wraps extremely long from-header (ascii)' ' + check_author "Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar" +' + +cat >expect <<'EOF' +From: "Foo.Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar + Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo + Bar Foo Bar Foo Bar Foo Bar" <author@example.com> +EOF +test_expect_success 'format-patch wraps extremely long from-header (rfc822)' ' + check_author "Foo.Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar" +' + +cat >expect <<'EOF' +From: =?UTF-8?q?Fo=C3=B6=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo?= + =?UTF-8?q?=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20?= + =?UTF-8?q?Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar?= + =?UTF-8?q?=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20Foo=20Bar=20?= + =?UTF-8?q?Foo=20Bar=20Foo=20Bar?= <author@example.com> +EOF +test_expect_success 'format-patch wraps extremely long from-header (rfc2047)' ' + check_author "Foö Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar Foo Bar" +' + cat >expect <<'EOF' Subject: header with . in it EOF @@ -886,6 +972,321 @@ test_expect_success 'empty subject prefix does not have extra space' ' test_cmp expect actual ' +test_expect_success '--from=ident notices bogus ident' ' + test_must_fail git format-patch -1 --stdout --from=foo >patch +' + +test_expect_success '--from=ident replaces author' ' + git format-patch -1 --stdout --from="Me <me@example.com>" >patch && + cat >expect <<-\EOF && + From: Me <me@example.com> + + From: A U Thor <author@example.com> + + EOF + sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head && + test_cmp expect patch.head +' + +test_expect_success '--from uses committer ident' ' + git format-patch -1 --stdout --from >patch && + cat >expect <<-\EOF && + From: C O Mitter <committer@example.com> + + From: A U Thor <author@example.com> + + EOF + sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head && + test_cmp expect patch.head +' + +test_expect_success '--from omits redundant in-body header' ' + git format-patch -1 --stdout --from="A U Thor <author@example.com>" >patch && + cat >expect <<-\EOF && + From: A U Thor <author@example.com> + + EOF + sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head && + test_cmp expect patch.head +' + +test_expect_success 'in-body headers trigger content encoding' ' + GIT_AUTHOR_NAME="éxötìc" test_commit exotic && + test_when_finished "git reset --hard HEAD^" && + git format-patch -1 --stdout --from >patch && + cat >expect <<-\EOF && + From: C O Mitter <committer@example.com> + Content-Type: text/plain; charset=UTF-8 + + From: éxötìc <author@example.com> + + EOF + sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head && + test_cmp expect patch.head +' + +append_signoff() +{ + C=$(git commit-tree HEAD^^{tree} -p HEAD) && + git format-patch --stdout --signoff $C^..$C >append_signoff.patch && + sed -n -e "1,/^---$/p" append_signoff.patch | + egrep -n "^Subject|Sign|^$" +} + +test_expect_success 'signoff: commit with no body' ' + append_signoff </dev/null >actual && + cat <<\EOF | sed "s/EOL$//" >expected && +4:Subject: [PATCH] EOL +8: +9:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: commit with only subject' ' + echo subject | append_signoff >actual && + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +9:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: commit with only subject that does not end with NL' ' + printf subject | append_signoff >actual && + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +9:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: no existing signoffs' ' + append_signoff <<\EOF >actual && +subject + +body +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +10: +11:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: no existing signoffs and no trailing NL' ' + printf "subject\n\nbody" | append_signoff >actual && + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +10: +11:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: some random signoff' ' + append_signoff <<\EOF >actual && +subject + +body + +Signed-off-by: my@house +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +10: +11:Signed-off-by: my@house +12:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: misc conforming footer elements' ' + append_signoff <<\EOF >actual && +subject + +body + +Signed-off-by: my@house +(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709) +Tested-by: Some One <someone@example.com> +Bug: 1234 +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +10: +11:Signed-off-by: my@house +15:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: some random signoff-alike' ' + append_signoff <<\EOF >actual && +subject + +body +Fooled-by-me: my@house +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +11: +12:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: not really a signoff' ' + append_signoff <<\EOF >actual && +subject + +I want to mention about Signed-off-by: here. +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +9:I want to mention about Signed-off-by: here. +10: +11:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: not really a signoff (2)' ' + append_signoff <<\EOF >actual && +subject + +My unfortunate +Signed-off-by: example happens to be wrapped here. +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +10:Signed-off-by: example happens to be wrapped here. +11: +12:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: valid S-o-b paragraph in the middle' ' + append_signoff <<\EOF >actual && +subject + +Signed-off-by: my@house +Signed-off-by: your@house + +A lot of houses. +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +9:Signed-off-by: my@house +10:Signed-off-by: your@house +11: +13: +14:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: the same signoff at the end' ' + append_signoff <<\EOF >actual && +subject + +body + +Signed-off-by: C O Mitter <committer@example.com> +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +10: +11:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: the same signoff at the end, no trailing NL' ' + printf "subject\n\nSigned-off-by: C O Mitter <committer@example.com>" | + append_signoff >actual && + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +9:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: the same signoff NOT at the end' ' + append_signoff <<\EOF >actual && +subject + +body + +Signed-off-by: C O Mitter <committer@example.com> +Signed-off-by: my@house +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +10: +11:Signed-off-by: C O Mitter <committer@example.com> +12:Signed-off-by: my@house +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: detect garbage in non-conforming footer' ' + append_signoff <<\EOF >actual && +subject + +body + +Tested-by: my@house +Some Trash +Signed-off-by: C O Mitter <committer@example.com> +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +10: +13:Signed-off-by: C O Mitter <committer@example.com> +14: +15:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + +test_expect_success 'signoff: footer begins with non-signoff without @ sign' ' + append_signoff <<\EOF >actual && +subject + +body + +Reviewed-id: Noone +Tested-by: my@house +Change-id: Ideadbeef +Signed-off-by: C O Mitter <committer@example.com> +Bug: 1234 +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +10: +14:Signed-off-by: C O Mitter <committer@example.com> +EOF + test_cmp expected actual +' + test_expect_success 'format patch ignores color.ui' ' test_unconfig color.ui && git format-patch --stdout -1 >expect && @@ -894,4 +1295,79 @@ test_expect_success 'format patch ignores color.ui' ' test_cmp expect actual ' +test_expect_success 'cover letter using branch description (1)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter master >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (2)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (3)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter ^master rebuild-1 >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (4)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter master.. >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (5)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter -2 HEAD >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter using branch description (6)' ' + git checkout rebuild-1 && + test_config branch.rebuild-1.description hello && + git format-patch --stdout --cover-letter -2 >actual && + grep hello actual >/dev/null +' + +test_expect_success 'cover letter with nothing' ' + git format-patch --stdout --cover-letter >actual && + test_line_count = 0 actual +' + +test_expect_success 'cover letter auto' ' + mkdir -p tmp && + test_when_finished "rm -rf tmp; + git config --unset format.coverletter" && + + git config format.coverletter auto && + git format-patch -o tmp -1 >list && + test_line_count = 1 list && + git format-patch -o tmp -2 >list && + test_line_count = 3 list +' + +test_expect_success 'cover letter auto user override' ' + mkdir -p tmp && + test_when_finished "rm -rf tmp; + git config --unset format.coverletter" && + + git config format.coverletter auto && + git format-patch -o tmp --cover-letter -1 >list && + test_line_count = 2 list && + git format-patch -o tmp --cover-letter -2 >list && + test_line_count = 3 list && + git format-patch -o tmp --no-cover-letter -1 >list && + test_line_count = 1 list && + git format-patch -o tmp --no-cover-letter -2 >list && + test_line_count = 2 list +' + test_done diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index 9059bcd69e..604a838c1a 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -103,7 +103,7 @@ test_expect_success 'another test, with -w --ignore-space-at-eol' 'test_cmp expe git diff -w -b --ignore-space-at-eol > out test_expect_success 'another test, with -w -b --ignore-space-at-eol' 'test_cmp expect out' -tr 'Q' '\015' << EOF > expect +tr 'Q_' '\015 ' << EOF > expect diff --git a/x b/x index d99af23..8b32fb5 100644 --- a/x @@ -111,19 +111,19 @@ index d99af23..8b32fb5 100644 @@ -1,6 +1,6 @@ -whitespace at beginning + whitespace at beginning - whitespace change + whitespace change -whitespace in the middle +white space in the middle - whitespace at end + whitespace at end__ unchanged line - CR at endQ + CR at end EOF git diff -b > out test_expect_success 'another test, with -b' 'test_cmp expect out' git diff -b --ignore-space-at-eol > out test_expect_success 'another test, with -b --ignore-space-at-eol' 'test_cmp expect out' -tr 'Q' '\015' << EOF > expect +tr 'Q_' '\015 ' << EOF > expect diff --git a/x b/x index d99af23..8b32fb5 100644 --- a/x @@ -135,13 +135,359 @@ index d99af23..8b32fb5 100644 + whitespace at beginning +whitespace change +white space in the middle - whitespace at end + whitespace at end__ unchanged line - CR at endQ + CR at end EOF git diff --ignore-space-at-eol > out test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out' +test_expect_success 'ignore-blank-lines: only new lines' ' + test_seq 5 >x && + git update-index x && + test_seq 5 | sed "/3/i\\ +" >x && + git diff --ignore-blank-lines >out && + >expect && + test_cmp out expect +' + +test_expect_success 'ignore-blank-lines: only new lines with space' ' + test_seq 5 >x && + git update-index x && + test_seq 5 | sed "/3/i\\ + " >x && + git diff -w --ignore-blank-lines >out && + >expect && + test_cmp out expect +' + +test_expect_success 'ignore-blank-lines: after change' ' + cat <<-\EOF >x && + 1 + 2 + + 3 + 4 + 5 + + 6 + 7 + EOF + git update-index x && + cat <<-\EOF >x && + change + + 1 + 2 + 3 + 4 + 5 + 6 + + 7 + EOF + git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp && + cat <<-\EOF >expected && + diff --git a/x b/x + --- a/x + +++ b/x + @@ -1,6 +1,7 @@ + +change + + + 1 + 2 + - + 3 + 4 + 5 + EOF + compare_diff_patch expected out.tmp +' + +test_expect_success 'ignore-blank-lines: before change' ' + cat <<-\EOF >x && + 1 + 2 + + 3 + 4 + 5 + 6 + 7 + EOF + git update-index x && + cat <<-\EOF >x && + + 1 + 2 + 3 + 4 + 5 + + 6 + 7 + change + EOF + git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp && + cat <<-\EOF >expected && + diff --git a/x b/x + --- a/x + +++ b/x + @@ -4,5 +4,7 @@ + 3 + 4 + 5 + + + 6 + 7 + +change + EOF + compare_diff_patch expected out.tmp +' + +test_expect_success 'ignore-blank-lines: between changes' ' + cat <<-\EOF >x && + 1 + 2 + 3 + 4 + 5 + + + 6 + 7 + 8 + 9 + 10 + EOF + git update-index x && + cat <<-\EOF >x && + change + 1 + 2 + + 3 + 4 + 5 + 6 + 7 + 8 + + 9 + 10 + change + EOF + git diff --ignore-blank-lines >out.tmp && + cat <<-\EOF >expected && + diff --git a/x b/x + --- a/x + +++ b/x + @@ -1,5 +1,7 @@ + +change + 1 + 2 + + + 3 + 4 + 5 + @@ -8,5 +8,7 @@ + 6 + 7 + 8 + + + 9 + 10 + +change + EOF + compare_diff_patch expected out.tmp +' + +test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' ' + test_seq 10 >x && + git update-index x && + cat <<-\EOF >x && + change + 1 + 2 + + 3 + 4 + 5 + + 6 + 7 + 8 + 9 + + 10 + change + EOF + git diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp && + cat <<-\EOF >expected && + diff --git a/x b/x + --- a/x + +++ b/x + @@ -1,10 +1,15 @@ + +change + 1 + 2 + + + 3 + 4 + 5 + + + 6 + 7 + 8 + 9 + + + 10 + +change + EOF + compare_diff_patch expected out.tmp +' + +test_expect_success 'ignore-blank-lines: scattered spaces' ' + test_seq 10 >x && + git update-index x && + cat <<-\EOF >x && + change + 1 + 2 + 3 + + 4 + + 5 + + 6 + + + 7 + + 8 + 9 + 10 + change + EOF + git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp && + cat <<-\EOF >expected && + diff --git a/x b/x + --- a/x + +++ b/x + @@ -1,3 +1,4 @@ + +change + 1 + 2 + 3 + @@ -8,3 +15,4 @@ + 8 + 9 + 10 + +change + EOF + compare_diff_patch expected out.tmp +' + +test_expect_success 'ignore-blank-lines: spaces coalesce' ' + test_seq 6 >x && + git update-index x && + cat <<-\EOF >x && + change + 1 + 2 + 3 + + 4 + + 5 + + 6 + change + EOF + git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp && + cat <<-\EOF >expected && + diff --git a/x b/x + --- a/x + +++ b/x + @@ -1,6 +1,11 @@ + +change + 1 + 2 + 3 + + + 4 + + + 5 + + + 6 + +change + EOF + compare_diff_patch expected out.tmp +' + +test_expect_success 'ignore-blank-lines: mix changes and blank lines' ' + test_seq 16 >x && + git update-index x && + cat <<-\EOF >x && + change + 1 + 2 + + 3 + 4 + 5 + change + 6 + 7 + 8 + + 9 + 10 + 11 + change + 12 + 13 + 14 + + 15 + 16 + change + EOF + git diff --ignore-blank-lines >out.tmp && + cat <<-\EOF >expected && + diff --git a/x b/x + --- a/x + +++ b/x + @@ -1,8 +1,11 @@ + +change + 1 + 2 + + + 3 + 4 + 5 + +change + 6 + 7 + 8 + @@ -9,8 +13,11 @@ + 9 + 10 + 11 + +change + 12 + 13 + 14 + + + 15 + 16 + +change + EOF + compare_diff_patch expected out.tmp +' + test_expect_success 'check mixed spaces and tabs in indent' ' # This is indented with SP HT SP. diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh index ab0c2f0574..cd543ecc54 100755 --- a/t/t4016-diff-quote.sh +++ b/t/t4016-diff-quote.sh @@ -13,14 +13,12 @@ P1='pathname with HT' P2='pathname with SP' P3='pathname with LF' -if : 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1" -then - test_set_prereq TABS_IN_FILENAMES -else - say 'Your filesystem does not allow tabs in filenames' -fi +echo 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1" || { + skip_all='Your filesystem does not allow tabs in filenames' + test_done +} -test_expect_success TABS_IN_FILENAMES setup ' +test_expect_success setup ' echo P0.0 >"$P0.0" && echo P0.1 >"$P0.1" && echo P0.2 >"$P0.2" && @@ -40,7 +38,7 @@ test_expect_success TABS_IN_FILENAMES setup ' : ' -test_expect_success TABS_IN_FILENAMES 'setup expected files' ' +test_expect_success 'setup expected files' ' cat >expect <<\EOF rename pathname.1 => "Rpathname\twith HT.0" (100%) rename pathname.3 => "Rpathname\nwith LF.0" (100%) @@ -52,27 +50,38 @@ cat >expect <<\EOF EOF ' -test_expect_success TABS_IN_FILENAMES 'git diff --summary -M HEAD' ' +test_expect_success 'git diff --summary -M HEAD' ' git diff --summary -M HEAD >actual && test_cmp expect actual ' -test_expect_success TABS_IN_FILENAMES 'setup expected files' ' -cat >expect <<\EOF - pathname.1 => "Rpathname\twith HT.0" | 0 - pathname.3 => "Rpathname\nwith LF.0" | 0 - "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0 - pathname.2 => Rpathname with SP.0 | 0 - "pathname\twith HT.2" => Rpathname with SP.1 | 0 - pathname.0 => Rpathname.0 | 0 - "pathname\twith HT.0" => Rpathname.1 | 0 - 7 files changed, 0 insertions(+), 0 deletions(-) -EOF +test_expect_success 'git diff --numstat -M HEAD' ' + cat >expect <<-\EOF && + 0 0 pathname.1 => "Rpathname\twith HT.0" + 0 0 pathname.3 => "Rpathname\nwith LF.0" + 0 0 "pathname\twith HT.3" => "Rpathname\nwith LF.1" + 0 0 pathname.2 => Rpathname with SP.0 + 0 0 "pathname\twith HT.2" => Rpathname with SP.1 + 0 0 pathname.0 => Rpathname.0 + 0 0 "pathname\twith HT.0" => Rpathname.1 + EOF + git diff --numstat -M HEAD >actual && + test_cmp expect actual ' -test_expect_success TABS_IN_FILENAMES 'git diff --stat -M HEAD' ' +test_expect_success 'git diff --stat -M HEAD' ' + cat >expect <<-\EOF && + pathname.1 => "Rpathname\twith HT.0" | 0 + pathname.3 => "Rpathname\nwith LF.0" | 0 + "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0 + pathname.2 => Rpathname with SP.0 | 0 + "pathname\twith HT.2" => Rpathname with SP.1 | 0 + pathname.0 => Rpathname.0 | 0 + "pathname\twith HT.0" => Rpathname.1 | 0 + 7 files changed, 0 insertions(+), 0 deletions(-) + EOF git diff --stat -M HEAD >actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_done diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh index 4bd2a1c838..38a092a0da 100755 --- a/t/t4018-diff-funcname.sh +++ b/t/t4018-diff-funcname.sh @@ -93,11 +93,6 @@ sed -e ' s/song;/song();/ ' <Beer.perl >Beer-correct.perl -test_config () { - git config "$1" "$2" && - test_when_finished "git config --unset $1" -} - test_expect_funcname () { lang=${2-java} test_expect_code 1 git diff --no-index -U1 \ @@ -105,7 +100,7 @@ test_expect_funcname () { grep "^@@.*@@ $1" diff } -for p in bibtex cpp csharp fortran html java matlab objc pascal perl php python ruby tex +for p in ada bibtex cpp csharp fortran html java matlab objc pascal perl php python ruby tex do test_expect_success "builtin $p pattern compiles" ' echo "*.java diff=$p" >.gitattributes && diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh index 083f62d1d6..8a309795c9 100755 --- a/t/t4020-diff-external.sh +++ b/t/t4020-diff-external.sh @@ -48,7 +48,53 @@ test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' ' ' +test_expect_success SYMLINKS 'typechange diff' ' + rm -f file && + ln -s elif file && + GIT_EXTERNAL_DIFF=echo git diff | { + read path oldfile oldhex oldmode newfile newhex newmode && + test "z$path" = zfile && + test "z$oldmode" = z100644 && + test "z$newhex" = "z$_z40" && + test "z$newmode" = z120000 && + oh=$(git rev-parse --verify HEAD:file) && + test "z$oh" = "z$oldhex" + } && + GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff >actual && + git diff >expect && + test_cmp expect actual +' + +test_expect_success 'diff.external' ' + git reset --hard && + echo third >file && + test_config diff.external echo && + git diff | { + read path oldfile oldhex oldmode newfile newhex newmode && + test "z$path" = zfile && + test "z$oldmode" = z100644 && + test "z$newhex" = "z$_z40" && + test "z$newmode" = z100644 && + oh=$(git rev-parse --verify HEAD:file) && + test "z$oh" = "z$oldhex" + } +' + +test_expect_success 'diff.external should apply only to diff' ' + test_config diff.external echo && + git log -p -1 HEAD | + grep "^diff --git a/file b/file" +' + +test_expect_success 'diff.external and --no-ext-diff' ' + test_config diff.external echo && + git diff --no-ext-diff | + grep "^diff --git a/file b/file" +' + test_expect_success 'diff attribute' ' + git reset --hard && + echo third >file && git config diff.parrot.command echo && @@ -113,6 +159,19 @@ test_expect_success 'diff attribute and --no-ext-diff' ' ' +test_expect_success 'GIT_EXTERNAL_DIFF trumps diff.external' ' + >.gitattributes && + test_config diff.external "echo ext-global" && + GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-env +' + +test_expect_success 'attributes trump GIT_EXTERNAL_DIFF and diff.external' ' + test_config diff.foo.command "echo ext-attribute" && + test_config diff.external "echo ext-global" && + echo "file diff=foo" >.gitattributes && + GIT_EXTERNAL_DIFF="echo ext-env" git diff | grep ext-attribute +' + test_expect_success 'no diff with -diff' ' echo >.gitattributes "file -diff" && git diff | grep Binary diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh index c00a94b9ba..2d030a4ec3 100755 --- a/t/t4022-diff-rewrite.sh +++ b/t/t4022-diff-rewrite.sh @@ -66,5 +66,35 @@ test_expect_success 'suppress deletion diff with -B -D' ' grep -v "Linus Torvalds" actual ' +test_expect_success 'prepare a file that ends with an incomplete line' ' + test_seq 1 99 >seq && + printf 100 >>seq && + git add seq && + git commit seq -m seq +' + +test_expect_success 'rewrite the middle 90% of sequence file and terminate with newline' ' + test_seq 1 5 >seq && + test_seq 9331 9420 >>seq && + test_seq 96 100 >>seq +' + +test_expect_success 'confirm that sequence file is considered a rewrite' ' + git diff -B seq >res && + grep "dissimilarity index" res +' + +test_expect_success 'no newline at eof is on its own line without -B' ' + git diff seq >res && + grep "^\\\\ " res && + ! grep "^..*\\\\ " res +' + +test_expect_success 'no newline at eof is on its own line with -B' ' + git diff -B seq >res && + grep "^\\\\ " res && + ! grep "^..*\\\\ " res +' + test_done diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh index 5d20acf436..55d549fcf4 100755 --- a/t/t4023-diff-rename-typechange.sh +++ b/t/t4023-diff-rename-typechange.sh @@ -4,44 +4,44 @@ test_description='typechange rename detection' . ./test-lib.sh -test_expect_success SYMLINKS setup ' +test_expect_success setup ' rm -f foo bar && cat "$TEST_DIRECTORY"/../COPYING >foo && - ln -s linklink bar && - git add foo bar && + test_ln_s_add linklink bar && + git add foo && git commit -a -m Initial && git tag one && - rm -f foo bar && + git rm -f foo bar && cat "$TEST_DIRECTORY"/../COPYING >bar && - ln -s linklink foo && - git add foo bar && + test_ln_s_add linklink foo && + git add bar && git commit -a -m Second && git tag two && - rm -f foo bar && + git rm -f foo bar && cat "$TEST_DIRECTORY"/../COPYING >foo && git add foo && git commit -a -m Third && git tag three && mv foo bar && - ln -s linklink foo && - git add foo bar && + test_ln_s_add linklink foo && + git add bar && git commit -a -m Fourth && git tag four && # This is purely for sanity check - rm -f foo bar && + git rm -f foo bar && cat "$TEST_DIRECTORY"/../COPYING >foo && cat "$TEST_DIRECTORY"/../Makefile >bar && git add foo bar && git commit -a -m Fifth && git tag five && - rm -f foo bar && + git rm -f foo bar && cat "$TEST_DIRECTORY"/../Makefile >foo && cat "$TEST_DIRECTORY"/../COPYING >bar && git add foo bar && @@ -50,7 +50,7 @@ test_expect_success SYMLINKS setup ' ' -test_expect_success SYMLINKS 'cross renames to be detected for regular files' ' +test_expect_success 'cross renames to be detected for regular files' ' git diff-tree five six -r --name-status -B -M | sort >actual && { @@ -61,7 +61,7 @@ test_expect_success SYMLINKS 'cross renames to be detected for regular files' ' ' -test_expect_success SYMLINKS 'cross renames to be detected for typechange' ' +test_expect_success 'cross renames to be detected for typechange' ' git diff-tree one two -r --name-status -B -M | sort >actual && { @@ -72,7 +72,7 @@ test_expect_success SYMLINKS 'cross renames to be detected for typechange' ' ' -test_expect_success SYMLINKS 'moves and renames' ' +test_expect_success 'moves and renames' ' git diff-tree three four -r --name-status -B -M | sort >actual && { diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh index 4ac162cfcf..aad6c7f78d 100755 --- a/t/t4030-diff-textconv.sh +++ b/t/t4030-diff-textconv.sh @@ -21,7 +21,7 @@ EOF cat >hexdump <<'EOF' #!/bin/sh -perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1" +"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1" EOF chmod +x hexdump @@ -58,6 +58,12 @@ test_expect_success 'diff produces text' ' test_cmp expect.text actual ' +test_expect_success 'show commit produces text' ' + git show HEAD >diff && + find_diff <diff >actual && + test_cmp expect.text actual +' + test_expect_success 'diff-tree produces binary' ' git diff-tree -p HEAD^ HEAD >diff && find_diff <diff >actual && @@ -84,14 +90,60 @@ test_expect_success 'status -v produces text' ' git reset --soft HEAD@{1} ' +test_expect_success 'show blob produces binary' ' + git show HEAD:file >actual && + printf "\\0\\n\\01\\n" >expect && + test_cmp expect actual +' + +test_expect_success 'show --textconv blob produces text' ' + git show --textconv HEAD:file >actual && + printf "0\\n1\\n" >expect && + test_cmp expect actual +' + +test_expect_success 'show --no-textconv blob produces binary' ' + git show --no-textconv HEAD:file >actual && + printf "\\0\\n\\01\\n" >expect && + test_cmp expect actual +' + +test_expect_success 'grep-diff (-G) operates on textconv data (add)' ' + echo one >expect && + git log --root --format=%s -G0 >actual && + test_cmp expect actual +' + +test_expect_success 'grep-diff (-G) operates on textconv data (modification)' ' + echo two >expect && + git log --root --format=%s -G1 >actual && + test_cmp expect actual +' + +test_expect_success 'pickaxe (-S) operates on textconv data (add)' ' + echo one >expect && + git log --root --format=%s -S0 >actual && + test_cmp expect actual +' + +test_expect_success 'pickaxe (-S) operates on textconv data (modification)' ' + echo two >expect && + git log --root --format=%s -S1 >actual && + test_cmp expect actual +' + cat >expect.stat <<'EOF' - file | Bin 2 -> 4 bytes + file | Bin 2 -> 4 bytes 1 file changed, 0 insertions(+), 0 deletions(-) EOF test_expect_success 'diffstat does not run textconv' ' echo file diff=fail >.gitattributes && git diff --stat HEAD^ HEAD >actual && - test_cmp expect.stat actual + test_i18ncmp expect.stat actual && + + head -n1 <expect.stat >expect.line1 && + head -n1 <actual >actual.line1 && + test_cmp expect.line1 actual.line1 ' # restore working setup echo file diff=foo >.gitattributes @@ -111,12 +163,10 @@ index 0000000..67be421 +frotz \ No newline at end of file EOF -# make a symlink the hard way that works on symlink-challenged file systems + test_expect_success 'textconv does not act on symlinks' ' - printf frotz > file && - git add file && - git ls-files -s | sed -e s/100644/120000/ | - git update-index --index-info && + rm -f file && + test_ln_s_add frotz file && git commit -m typechange && git show >diff && find_diff <diff >actual && diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh index 7d7470f21b..eacc6694f7 100755 --- a/t/t4031-diff-rewrite-binary.sh +++ b/t/t4031-diff-rewrite-binary.sh @@ -44,17 +44,23 @@ test_expect_success 'rewrite diff can show binary patch' ' grep "GIT binary patch" diff ' -test_expect_success 'rewrite diff --stat shows binary changes' ' +test_expect_success 'rewrite diff --numstat shows binary changes' ' + git diff -B --numstat --summary >diff && + grep -e "- - " diff && + grep " rewrite file" diff +' + +test_expect_success 'diff --stat counts binary rewrite as 0 lines' ' git diff -B --stat --summary >diff && grep "Bin" diff && - grep "0 insertions.*0 deletions" diff && + test_i18ngrep "0 insertions.*0 deletions" diff && grep " rewrite file" diff ' { echo "#!$SHELL_PATH" cat <<'EOF' -perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1" +"$PERL_PATH" -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1" EOF } >dump chmod +x dump diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh index 5c2012111c..f2f55fc51c 100755 --- a/t/t4034-diff-words.sh +++ b/t/t4034-diff-words.sh @@ -3,6 +3,7 @@ test_description='word diff colors' . ./test-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh cat >pre.simple <<-\EOF h(4) @@ -229,7 +230,7 @@ test_expect_success '.gitattributes override config' ' ' test_expect_success 'setup: remove diff driver regex' ' - test_might_fail git config --unset diff.testdriver.wordRegex + test_unconfig diff.testdriver.wordRegex ' test_expect_success 'use configured regex' ' @@ -293,6 +294,11 @@ test_expect_success '--word-diff=none' ' word_diff --word-diff=plain --word-diff=none ' +test_expect_success 'unset default driver' ' + test_unconfig diff.wordregex +' + +test_language_driver ada test_language_driver bibtex test_language_driver cpp test_language_driver csharp @@ -329,8 +335,7 @@ test_expect_success 'word-diff with diff.sbe' ' c EOF - test_when_finished "git config --unset diff.suppress-blank-empty" && - git config diff.suppress-blank-empty true && + test_config diff.suppress-blank-empty true && word_diff --word-diff=plain ' @@ -348,4 +353,35 @@ test_expect_success 'word-diff with no newline at EOF' ' word_diff --word-diff=plain ' +test_expect_success 'setup history with two files' ' + echo "a b; c" >a.tex && + echo "a b; c" >z.txt && + git add a.tex z.txt && + git commit -minitial && + + # modify both + echo "a bx; c" >a.tex && + echo "a bx; c" >z.txt && + git commit -mmodified -a +' + +test_expect_success 'wordRegex for the first file does not apply to the second' ' + echo "*.tex diff=tex" >.gitattributes && + test_config diff.tex.wordRegex "[a-z]+|." && + cat >expect <<-\EOF && + diff --git a/a.tex b/a.tex + --- a/a.tex + +++ b/a.tex + @@ -1 +1 @@ + a [-b-]{+bx+}; c + diff --git a/z.txt b/z.txt + --- a/z.txt + +++ b/z.txt + @@ -1 +1 @@ + a [-b;-]{+bx;+} c + EOF + git diff --word-diff HEAD~ >actual && + compare_diff_patch expect actual +' + test_done diff --git a/t/t4034/ada/expect b/t/t4034/ada/expect new file mode 100644 index 0000000000..be2376e904 --- /dev/null +++ b/t/t4034/ada/expect @@ -0,0 +1,27 @@ +<BOLD>diff --git a/pre b/post<RESET> +<BOLD>index d96fdd1..df21bb0 100644<RESET> +<BOLD>--- a/pre<RESET> +<BOLD>+++ b/post<RESET> +<CYAN>@@ -1,13 +1,13 @@<RESET> +Ada.Text_IO.Put_Line("Hello World<RED>!<RESET><GREEN>?<RESET>"); +1 1e<RED>-<RESET>10 16#FE12#E2 3.141_592 '<RED>x<RESET><GREEN>y<RESET>' +<RED>a<RESET><GREEN>x<RESET>+<RED>b a<RESET><GREEN>y x<RESET>-<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>*<RED>b a<RESET><GREEN>y x<RESET>/<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>**<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>(<RED>b<RESET><GREEN>y<RESET>) +<RED>a<RESET><GREEN>x<RESET>:=<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>=<RED>b a<RESET><GREEN>y x<RESET>/= <RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET><<RED>b a<RESET><GREEN>y x<RESET><=<RED>b a<RESET><GREEN>y x<RESET>><RED>b a<RESET><GREEN>y x<RESET>>=<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>,<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>=><RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET>..<RED>b<RESET> +<RED>a<RESET><GREEN>y<RESET> +<GREEN>x<RESET><><RED>b<RESET><GREEN>y<RESET> diff --git a/t/t4034/ada/post b/t/t4034/ada/post new file mode 100644 index 0000000000..df21bb044f --- /dev/null +++ b/t/t4034/ada/post @@ -0,0 +1,13 @@ +Ada.Text_IO.Put_Line("Hello World?"); +1 1e10 16#FE12#E2 3.141_592 'y' +x+y x-y +x*y x/y +x**y +x(y) +x:=y +x=y x/= y +x<y x<=y x>y x>=y +x,y +x=>y +x..y +x<>y diff --git a/t/t4034/ada/pre b/t/t4034/ada/pre new file mode 100644 index 0000000000..d96fdd1e8e --- /dev/null +++ b/t/t4034/ada/pre @@ -0,0 +1,13 @@ +Ada.Text_IO.Put_Line("Hello World!"); +1 1e-10 16#FE12#E2 3.141_592 'x' +a+b a-b +a*b a/b +a**b +a(b) +a:=b +a=b a/= b +a<b a<=b a>b a>=b +a,b +a=>b +a..b +a<>b diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh index e747e84227..231412d100 100755 --- a/t/t4035-diff-quiet.sh +++ b/t/t4035-diff-quiet.sh @@ -10,71 +10,142 @@ test_expect_success 'setup' ' git commit -m first && echo 2 >b && git add . && - git commit -a -m second + git commit -a -m second && + mkdir -p test-outside/repo && ( + cd test-outside/repo && + git init && + echo "1 1" >a && + git add . && + git commit -m 1 + ) && + mkdir -p test-outside/non/git && ( + cd test-outside/non/git && + echo "1 1" >a && + echo "1 1" >matching-file && + echo "1 1 " >trailing-space && + echo "1 1" >extra-space && + echo "2" >never-match + ) ' test_expect_success 'git diff-tree HEAD^ HEAD' ' git diff-tree --quiet HEAD^ HEAD >cnt - test $? = 1 && test $(wc -l <cnt) = 0 + test $? = 1 && test_line_count = 0 cnt ' test_expect_success 'git diff-tree HEAD^ HEAD -- a' ' git diff-tree --quiet HEAD^ HEAD -- a >cnt - test $? = 0 && test $(wc -l <cnt) = 0 + test $? = 0 && test_line_count = 0 cnt ' test_expect_success 'git diff-tree HEAD^ HEAD -- b' ' git diff-tree --quiet HEAD^ HEAD -- b >cnt - test $? = 1 && test $(wc -l <cnt) = 0 + test $? = 1 && test_line_count = 0 cnt ' # this diff outputs one line: sha1 of the given head test_expect_success 'echo HEAD | git diff-tree --stdin' ' echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt - test $? = 1 && test $(wc -l <cnt) = 1 + test $? = 1 && test_line_count = 1 cnt ' test_expect_success 'git diff-tree HEAD HEAD' ' git diff-tree --quiet HEAD HEAD >cnt - test $? = 0 && test $(wc -l <cnt) = 0 + test $? = 0 && test_line_count = 0 cnt ' test_expect_success 'git diff-files' ' git diff-files --quiet >cnt - test $? = 0 && test $(wc -l <cnt) = 0 + test $? = 0 && test_line_count = 0 cnt ' test_expect_success 'git diff-index --cached HEAD' ' git diff-index --quiet --cached HEAD >cnt - test $? = 0 && test $(wc -l <cnt) = 0 + test $? = 0 && test_line_count = 0 cnt ' test_expect_success 'git diff-index --cached HEAD^' ' git diff-index --quiet --cached HEAD^ >cnt - test $? = 1 && test $(wc -l <cnt) = 0 + test $? = 1 && test_line_count = 0 cnt ' test_expect_success 'git diff-index --cached HEAD^' ' echo text >>b && echo 3 >c && git add . && { git diff-index --quiet --cached HEAD^ >cnt - test $? = 1 && test $(wc -l <cnt) = 0 + test $? = 1 && test_line_count = 0 cnt } ' test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' ' git commit -m "text in b" && { git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt - test $? = 1 && test $(wc -l <cnt) = 0 + test $? = 1 && test_line_count = 0 cnt } ' test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' ' git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt - test $? = 0 && test $(wc -l <cnt) = 0 + test $? = 0 && test_line_count = 0 cnt ' test_expect_success 'git diff-files' ' echo 3 >>c && { git diff-files --quiet >cnt - test $? = 1 && test $(wc -l <cnt) = 0 + test $? = 1 && test_line_count = 0 cnt } ' test_expect_success 'git diff-index --cached HEAD' ' git update-index c && { git diff-index --quiet --cached HEAD >cnt - test $? = 1 && test $(wc -l <cnt) = 0 + test $? = 1 && test_line_count = 0 cnt } ' +test_expect_success 'git diff, one file outside repo' ' + ( + cd test-outside/repo && + test_expect_code 0 git diff --quiet a ../non/git/matching-file && + test_expect_code 1 git diff --quiet a ../non/git/extra-space + ) +' + +test_expect_success 'git diff, both files outside repo' ' + ( + GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" && + export GIT_CEILING_DIRECTORIES && + cd test-outside/non/git && + test_expect_code 0 git diff --quiet a matching-file && + test_expect_code 1 git diff --quiet a extra-space + ) +' + +test_expect_success 'git diff --ignore-space-at-eol, one file outside repo' ' + ( + cd test-outside/repo && + test_expect_code 0 git diff --quiet --ignore-space-at-eol a ../non/git/trailing-space && + test_expect_code 1 git diff --quiet --ignore-space-at-eol a ../non/git/extra-space + ) +' + +test_expect_success 'git diff --ignore-space-at-eol, both files outside repo' ' + ( + GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" && + export GIT_CEILING_DIRECTORIES && + cd test-outside/non/git && + test_expect_code 0 git diff --quiet --ignore-space-at-eol a trailing-space && + test_expect_code 1 git diff --quiet --ignore-space-at-eol a extra-space + ) +' + +test_expect_success 'git diff --ignore-all-space, one file outside repo' ' + ( + cd test-outside/repo && + test_expect_code 0 git diff --quiet --ignore-all-space a ../non/git/trailing-space && + test_expect_code 0 git diff --quiet --ignore-all-space a ../non/git/extra-space && + test_expect_code 1 git diff --quiet --ignore-all-space a ../non/git/never-match + ) +' + +test_expect_success 'git diff --ignore-all-space, both files outside repo' ' + ( + GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY/test-outside" && + export GIT_CEILING_DIRECTORIES && + cd test-outside/non/git && + test_expect_code 0 git diff --quiet --ignore-all-space a trailing-space && + test_expect_code 0 git diff --quiet --ignore-all-space a extra-space && + test_expect_code 1 git diff --quiet --ignore-all-space a never-match + ) +' + test_done diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh index 40277c77aa..1019d7b35f 100755 --- a/t/t4038-diff-combined.sh +++ b/t/t4038-diff-combined.sh @@ -3,6 +3,7 @@ test_description='combined diff' . ./test-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh setup_helper () { one=$1 branch=$2 side=$3 && @@ -89,4 +90,315 @@ test_expect_success 'diagnose truncated file' ' grep "diff --cc file" out ' +test_expect_success 'setup for --cc --raw' ' + blob=$(echo file | git hash-object --stdin -w) && + base_tree=$(echo "100644 blob $blob file" | git mktree) && + trees= && + for i in `test_seq 1 40` + do + blob=$(echo file$i | git hash-object --stdin -w) && + trees="$trees$(echo "100644 blob $blob file" | git mktree)$LF" + done +' + +test_expect_success 'check --cc --raw with four trees' ' + four_trees=$(echo "$trees" | sed -e 4q) && + git diff --cc --raw $four_trees $base_tree >out && + # Check for four leading colons in the output: + grep "^::::[^:]" out +' + +test_expect_success 'check --cc --raw with forty trees' ' + git diff --cc --raw $trees $base_tree >out && + # Check for forty leading colons in the output: + grep "^::::::::::::::::::::::::::::::::::::::::[^:]" out +' + +test_expect_success 'setup combined ignore spaces' ' + git checkout master && + >test && + git add test && + git commit -m initial && + + tr -d Q <<-\EOF >test && + always coalesce + eol space coalesce Q + space change coalesce + all spa ces coalesce + eol spaces Q + space change + all spa ces + EOF + git commit -m "test space change" -a && + + git checkout -b side HEAD^ && + tr -d Q <<-\EOF >test && + always coalesce + eol space coalesce + space change coalesce + all spaces coalesce + eol spaces + space change + all spaces + EOF + git commit -m "test other space changes" -a && + + test_must_fail git merge master && + tr -d Q <<-\EOF >test && + eol spaces Q + space change + all spa ces + EOF + git commit -m merged -a +' + +test_expect_success 'check combined output (no ignore space)' ' + git show >actual.tmp && + sed -e "1,/^@@@/d" < actual.tmp >actual && + tr -d Q <<-\EOF >expected && + --always coalesce + - eol space coalesce + - space change coalesce + - all spaces coalesce + - eol spaces + - space change + - all spaces + -eol space coalesce Q + -space change coalesce + -all spa ces coalesce + + eol spaces Q + + space change + + all spa ces + EOF + compare_diff_patch expected actual +' + +test_expect_success 'check combined output (ignore space at eol)' ' + git show --ignore-space-at-eol >actual.tmp && + sed -e "1,/^@@@/d" < actual.tmp >actual && + tr -d Q <<-\EOF >expected && + --always coalesce + --eol space coalesce + - space change coalesce + - all spaces coalesce + -space change coalesce + -all spa ces coalesce + eol spaces Q + - space change + - all spaces + + space change + + all spa ces + EOF + compare_diff_patch expected actual +' + +test_expect_success 'check combined output (ignore space change)' ' + git show -b >actual.tmp && + sed -e "1,/^@@@/d" < actual.tmp >actual && + tr -d Q <<-\EOF >expected && + --always coalesce + --eol space coalesce + --space change coalesce + - all spaces coalesce + -all spa ces coalesce + eol spaces Q + space change + - all spaces + + all spa ces + EOF + compare_diff_patch expected actual +' + +test_expect_success 'check combined output (ignore all spaces)' ' + git show -w >actual.tmp && + sed -e "1,/^@@@/d" < actual.tmp >actual && + tr -d Q <<-\EOF >expected && + --always coalesce + --eol space coalesce + --space change coalesce + --all spaces coalesce + eol spaces Q + space change + all spa ces + EOF + compare_diff_patch expected actual +' + +test_expect_success 'combine diff coalesce simple' ' + >test && + git add test && + git commit -m initial && + test_seq 4 >test && + git commit -a -m empty1 && + git branch side1 && + git checkout HEAD^ && + test_seq 5 >test && + git commit -a -m empty2 && + test_must_fail git merge side1 && + >test && + git commit -a -m merge && + git show >actual.tmp && + sed -e "1,/^@@@/d" < actual.tmp >actual && + tr -d Q <<-\EOF >expected && + --1 + --2 + --3 + --4 + - 5 + EOF + compare_diff_patch expected actual +' + +test_expect_success 'combine diff coalesce tricky' ' + >test && + git add test && + git commit -m initial --allow-empty && + cat <<-\EOF >test && + 3 + 1 + 2 + 3 + 4 + EOF + git commit -a -m empty1 && + git branch -f side1 && + git checkout HEAD^ && + cat <<-\EOF >test && + 1 + 3 + 5 + 4 + EOF + git commit -a -m empty2 && + git branch -f side2 && + test_must_fail git merge side1 && + >test && + git commit -a -m merge && + git show >actual.tmp && + sed -e "1,/^@@@/d" < actual.tmp >actual && + tr -d Q <<-\EOF >expected && + -3 + --1 + -2 + --3 + - 5 + --4 + EOF + compare_diff_patch expected actual && + git checkout -f side1 && + test_must_fail git merge side2 && + >test && + git commit -a -m merge && + git show >actual.tmp && + sed -e "1,/^@@@/d" < actual.tmp >actual && + tr -d Q <<-\EOF >expected && + - 3 + --1 + - 2 + --3 + -5 + --4 + EOF + compare_diff_patch expected actual +' + +test_expect_failure 'combine diff coalesce three parents' ' + >test && + git add test && + git commit -m initial --allow-empty && + cat <<-\EOF >test && + 3 + 1 + 2 + 3 + 4 + EOF + git commit -a -m empty1 && + git checkout -B side1 && + git checkout HEAD^ && + cat <<-\EOF >test && + 1 + 3 + 7 + 5 + 4 + EOF + git commit -a -m empty2 && + git branch -f side2 && + git checkout HEAD^ && + cat <<-\EOF >test && + 3 + 1 + 6 + 5 + 4 + EOF + git commit -a -m empty3 && + >test && + git add test && + TREE=$(git write-tree) && + COMMIT=$(git commit-tree -p HEAD -p side1 -p side2 -m merge $TREE) && + git show $COMMIT >actual.tmp && + sed -e "1,/^@@@/d" < actual.tmp >actual && + tr -d Q <<-\EOF >expected && + -- 3 + ---1 + - 6 + - 2 + --3 + -7 + - -5 + ---4 + EOF + compare_diff_patch expected actual +' + +# Test for a bug reported at +# http://thread.gmane.org/gmane.comp.version-control.git/224410 +# where a delete lines were missing from combined diff output when they +# occurred exactly before the context lines of a later change. +test_expect_success 'combine diff missing delete bug' ' + git commit -m initial --allow-empty && + cat <<-\EOF >test && + 1 + 2 + 3 + 4 + EOF + git add test && + git commit -a -m side1 && + git checkout -B side1 && + git checkout HEAD^ && + cat <<-\EOF >test && + 0 + 1 + 2 + 3 + 4modified + EOF + git add test && + git commit -m side2 && + git branch -f side2 && + test_must_fail git merge --no-commit side1 && + cat <<-\EOF >test && + 1 + 2 + 3 + 4modified + EOF + git add test && + git commit -a -m merge && + git diff-tree -c -p HEAD >actual.tmp && + sed -e "1,/^@@@/d" < actual.tmp >actual && + tr -d Q <<-\EOF >expected && + - 0 + 1 + 2 + 3 + -4 + +4modified + EOF + compare_diff_patch expected actual +' + test_done diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh index bf9a7526bd..1751c83307 100755 --- a/t/t4041-diff-submodule-option.sh +++ b/t/t4041-diff-submodule-option.sh @@ -1,6 +1,7 @@ #!/bin/sh # # Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin +# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests) # test_description='Support for verbose submodule differences in git diff @@ -10,19 +11,23 @@ This test tries to verify the sanity of the --submodule option of git diff. . ./test-lib.sh +# String "added" in German (translated with Google Translate), encoded in UTF-8, +# used in sample commit log messages in add_file() function below. +added=$(printf "hinzugef\303\274gt") add_file () { - sm=$1 - shift - owd=$(pwd) - cd "$sm" - for name; do - echo "$name" > "$name" && - git add "$name" && - test_tick && - git commit -m "Add $name" - done >/dev/null - git rev-parse --verify HEAD | cut -c1-7 - cd "$owd" + ( + cd "$1" && + shift && + for name + do + echo "$name" >"$name" && + git add "$name" && + test_tick && + msg_added_iso88591=$(echo "Add $name ($added $name)" | iconv -f utf-8 -t iso8859-1) && + git -c 'i18n.commitEncoding=iso8859-1' commit -m "$msg_added_iso88591" + done >/dev/null && + git rev-parse --short --verify HEAD + ) } commit_file () { test_tick && @@ -33,92 +38,133 @@ test_create_repo sm1 && add_file . foo >/dev/null head1=$(add_file sm1 foo1 foo2) +fullhead1=$(cd sm1; git rev-parse --verify HEAD) -test_expect_success 'added submodule' " +test_expect_success 'added submodule' ' git add sm1 && git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 0000000...$head1 (new submodule) -EOF + Submodule sm1 0000000...$head1 (new submodule) + EOF test_cmp expected actual -" +' + +test_expect_success 'added submodule, set diff.submodule' ' + git config diff.submodule log && + git add sm1 && + git diff --cached >actual && + cat >expected <<-EOF && + Submodule sm1 0000000...$head1 (new submodule) + EOF + git config --unset diff.submodule && + test_cmp expected actual +' + +test_expect_success '--submodule=short overrides diff.submodule' ' + test_config diff.submodule log && + git add sm1 && + git diff --submodule=short --cached >actual && + cat >expected <<-EOF && + diff --git a/sm1 b/sm1 + new file mode 160000 + index 0000000..$head1 + --- /dev/null + +++ b/sm1 + @@ -0,0 +1 @@ + +Subproject commit $fullhead1 + EOF + test_cmp expected actual +' + +test_expect_success 'diff.submodule does not affect plumbing' ' + test_config diff.submodule log && + git diff-index -p HEAD >actual && + cat >expected <<-EOF && + diff --git a/sm1 b/sm1 + new file mode 160000 + index 0000000..$head1 + --- /dev/null + +++ b/sm1 + @@ -0,0 +1 @@ + +Subproject commit $fullhead1 + EOF + test_cmp expected actual +' commit_file sm1 && head2=$(add_file sm1 foo3) -test_expect_success 'modified submodule(forward)' " +test_expect_success 'modified submodule(forward)' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head1..$head2: - > Add foo3 -EOF + Submodule sm1 $head1..$head2: + > Add foo3 ($added foo3) + EOF test_cmp expected actual -" +' -test_expect_success 'modified submodule(forward)' " +test_expect_success 'modified submodule(forward)' ' git diff --submodule=log >actual && cat >expected <<-EOF && -Submodule sm1 $head1..$head2: - > Add foo3 -EOF + Submodule sm1 $head1..$head2: + > Add foo3 ($added foo3) + EOF test_cmp expected actual -" +' -test_expect_success 'modified submodule(forward) --submodule' " +test_expect_success 'modified submodule(forward) --submodule' ' git diff --submodule >actual && cat >expected <<-EOF && -Submodule sm1 $head1..$head2: - > Add foo3 -EOF + Submodule sm1 $head1..$head2: + > Add foo3 ($added foo3) + EOF test_cmp expected actual -" +' -fullhead1=$(cd sm1; git rev-list --max-count=1 $head1) -fullhead2=$(cd sm1; git rev-list --max-count=1 $head2) -test_expect_success 'modified submodule(forward) --submodule=short' " +fullhead2=$(cd sm1; git rev-parse --verify HEAD) +test_expect_success 'modified submodule(forward) --submodule=short' ' git diff --submodule=short >actual && cat >expected <<-EOF && -diff --git a/sm1 b/sm1 -index $head1..$head2 160000 ---- a/sm1 -+++ b/sm1 -@@ -1 +1 @@ --Subproject commit $fullhead1 -+Subproject commit $fullhead2 -EOF + diff --git a/sm1 b/sm1 + index $head1..$head2 160000 + --- a/sm1 + +++ b/sm1 + @@ -1 +1 @@ + -Subproject commit $fullhead1 + +Subproject commit $fullhead2 + EOF test_cmp expected actual -" +' commit_file sm1 && head3=$( cd sm1 && git reset --hard HEAD~2 >/dev/null && - git rev-parse --verify HEAD | cut -c1-7 + git rev-parse --short --verify HEAD ) -test_expect_success 'modified submodule(backward)' " +test_expect_success 'modified submodule(backward)' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head2..$head3 (rewind): - < Add foo3 - < Add foo2 -EOF + Submodule sm1 $head2..$head3 (rewind): + < Add foo3 ($added foo3) + < Add foo2 ($added foo2) + EOF test_cmp expected actual -" +' -head4=$(add_file sm1 foo4 foo5) && -head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD) -test_expect_success 'modified submodule(backward and forward)' " +head4=$(add_file sm1 foo4 foo5) +test_expect_success 'modified submodule(backward and forward)' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head2...$head4: - > Add foo5 - > Add foo4 - < Add foo3 - < Add foo2 -EOF + Submodule sm1 $head2...$head4: + > Add foo5 ($added foo5) + > Add foo4 ($added foo4) + < Add foo3 ($added foo3) + < Add foo2 ($added foo2) + EOF test_cmp expected actual -" +' commit_file sm1 && mv sm1 sm1-bak && @@ -128,319 +174,319 @@ git add sm1 && rm -f sm1 && mv sm1-bak sm1 -test_expect_success 'typechanged submodule(submodule->blob), --cached' " +test_expect_success 'typechanged submodule(submodule->blob), --cached' ' git diff --submodule=log --cached >actual && cat >expected <<-EOF && -Submodule sm1 41fbea9...0000000 (submodule deleted) -diff --git a/sm1 b/sm1 -new file mode 100644 -index 0000000..9da5fb8 ---- /dev/null -+++ b/sm1 -@@ -0,0 +1 @@ -+sm1 -EOF + Submodule sm1 $head4...0000000 (submodule deleted) + diff --git a/sm1 b/sm1 + new file mode 100644 + index 0000000..$head5 + --- /dev/null + +++ b/sm1 + @@ -0,0 +1 @@ + +sm1 + EOF test_cmp expected actual -" +' -test_expect_success 'typechanged submodule(submodule->blob)' " +test_expect_success 'typechanged submodule(submodule->blob)' ' git diff --submodule=log >actual && cat >expected <<-EOF && -diff --git a/sm1 b/sm1 -deleted file mode 100644 -index 9da5fb8..0000000 ---- a/sm1 -+++ /dev/null -@@ -1 +0,0 @@ --sm1 -Submodule sm1 0000000...$head4 (new submodule) -EOF + diff --git a/sm1 b/sm1 + deleted file mode 100644 + index $head5..0000000 + --- a/sm1 + +++ /dev/null + @@ -1 +0,0 @@ + -sm1 + Submodule sm1 0000000...$head4 (new submodule) + EOF test_cmp expected actual -" +' rm -rf sm1 && git checkout-index sm1 -test_expect_success 'typechanged submodule(submodule->blob)' " +test_expect_success 'typechanged submodule(submodule->blob)' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head4...0000000 (submodule deleted) -diff --git a/sm1 b/sm1 -new file mode 100644 -index 0000000..$head5 ---- /dev/null -+++ b/sm1 -@@ -0,0 +1 @@ -+sm1 -EOF + Submodule sm1 $head4...0000000 (submodule deleted) + diff --git a/sm1 b/sm1 + new file mode 100644 + index 0000000..$head5 + --- /dev/null + +++ b/sm1 + @@ -0,0 +1 @@ + +sm1 + EOF test_cmp expected actual -" +' rm -f sm1 && test_create_repo sm1 && head6=$(add_file sm1 foo6 foo7) -fullhead6=$(cd sm1; git rev-list --max-count=1 $head6) -test_expect_success 'nonexistent commit' " +fullhead6=$(cd sm1; git rev-parse --verify HEAD) +test_expect_success 'nonexistent commit' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head4...$head6 (commits not present) -EOF + Submodule sm1 $head4...$head6 (commits not present) + EOF test_cmp expected actual -" +' commit_file -test_expect_success 'typechanged submodule(blob->submodule)' " +test_expect_success 'typechanged submodule(blob->submodule)' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -diff --git a/sm1 b/sm1 -deleted file mode 100644 -index $head5..0000000 ---- a/sm1 -+++ /dev/null -@@ -1 +0,0 @@ --sm1 -Submodule sm1 0000000...$head6 (new submodule) -EOF + diff --git a/sm1 b/sm1 + deleted file mode 100644 + index $head5..0000000 + --- a/sm1 + +++ /dev/null + @@ -1 +0,0 @@ + -sm1 + Submodule sm1 0000000...$head6 (new submodule) + EOF test_cmp expected actual -" +' commit_file sm1 && -test_expect_success 'submodule is up to date' " +test_expect_success 'submodule is up to date' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -EOF + EOF test_cmp expected actual -" +' -test_expect_success 'submodule contains untracked content' " +test_expect_success 'submodule contains untracked content' ' echo new > sm1/new-file && git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 contains untracked content -EOF + Submodule sm1 contains untracked content + EOF test_cmp expected actual -" +' -test_expect_success 'submodule contains untracked content (untracked ignored)' " +test_expect_success 'submodule contains untracked content (untracked ignored)' ' git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual && ! test -s actual -" +' -test_expect_success 'submodule contains untracked content (dirty ignored)' " +test_expect_success 'submodule contains untracked content (dirty ignored)' ' git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual && ! test -s actual -" +' -test_expect_success 'submodule contains untracked content (all ignored)' " +test_expect_success 'submodule contains untracked content (all ignored)' ' git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual && ! test -s actual -" +' -test_expect_success 'submodule contains untracked and modifed content' " +test_expect_success 'submodule contains untracked and modifed content' ' echo new > sm1/foo6 && git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 contains untracked content -Submodule sm1 contains modified content -EOF + Submodule sm1 contains untracked content + Submodule sm1 contains modified content + EOF test_cmp expected actual -" +' -test_expect_success 'submodule contains untracked and modifed content (untracked ignored)' " +test_expect_success 'submodule contains untracked and modifed content (untracked ignored)' ' echo new > sm1/foo6 && git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 contains modified content -EOF + Submodule sm1 contains modified content + EOF test_cmp expected actual -" +' -test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' " +test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' ' echo new > sm1/foo6 && git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual && ! test -s actual -" +' -test_expect_success 'submodule contains untracked and modifed content (all ignored)' " +test_expect_success 'submodule contains untracked and modifed content (all ignored)' ' echo new > sm1/foo6 && git diff-index -p --ignore-submodules --submodule=log HEAD >actual && ! test -s actual -" +' -test_expect_success 'submodule contains modifed content' " +test_expect_success 'submodule contains modifed content' ' rm -f sm1/new-file && git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 contains modified content -EOF + Submodule sm1 contains modified content + EOF test_cmp expected actual -" +' (cd sm1; git commit -mchange foo6 >/dev/null) && -head8=$(cd sm1; git rev-parse --verify HEAD | cut -c1-7) && -test_expect_success 'submodule is modified' " +head8=$(cd sm1; git rev-parse --short --verify HEAD) && +test_expect_success 'submodule is modified' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head6..$head8: - > change -EOF + Submodule sm1 $head6..$head8: + > change + EOF test_cmp expected actual -" +' -test_expect_success 'modified submodule contains untracked content' " +test_expect_success 'modified submodule contains untracked content' ' echo new > sm1/new-file && git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 contains untracked content -Submodule sm1 $head6..$head8: - > change -EOF + Submodule sm1 contains untracked content + Submodule sm1 $head6..$head8: + > change + EOF test_cmp expected actual -" +' -test_expect_success 'modified submodule contains untracked content (untracked ignored)' " +test_expect_success 'modified submodule contains untracked content (untracked ignored)' ' git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head6..$head8: - > change -EOF + Submodule sm1 $head6..$head8: + > change + EOF test_cmp expected actual -" +' -test_expect_success 'modified submodule contains untracked content (dirty ignored)' " +test_expect_success 'modified submodule contains untracked content (dirty ignored)' ' git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head6..$head8: - > change -EOF + Submodule sm1 $head6..$head8: + > change + EOF test_cmp expected actual -" +' -test_expect_success 'modified submodule contains untracked content (all ignored)' " +test_expect_success 'modified submodule contains untracked content (all ignored)' ' git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual && ! test -s actual -" +' -test_expect_success 'modified submodule contains untracked and modifed content' " +test_expect_success 'modified submodule contains untracked and modifed content' ' echo modification >> sm1/foo6 && git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 contains untracked content -Submodule sm1 contains modified content -Submodule sm1 $head6..$head8: - > change -EOF + Submodule sm1 contains untracked content + Submodule sm1 contains modified content + Submodule sm1 $head6..$head8: + > change + EOF test_cmp expected actual -" +' -test_expect_success 'modified submodule contains untracked and modifed content (untracked ignored)' " +test_expect_success 'modified submodule contains untracked and modifed content (untracked ignored)' ' echo modification >> sm1/foo6 && git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 contains modified content -Submodule sm1 $head6..$head8: - > change -EOF + Submodule sm1 contains modified content + Submodule sm1 $head6..$head8: + > change + EOF test_cmp expected actual -" +' -test_expect_success 'modified submodule contains untracked and modifed content (dirty ignored)' " +test_expect_success 'modified submodule contains untracked and modifed content (dirty ignored)' ' echo modification >> sm1/foo6 && git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head6..$head8: - > change -EOF + Submodule sm1 $head6..$head8: + > change + EOF test_cmp expected actual -" +' -test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' " +test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' ' echo modification >> sm1/foo6 && git diff-index -p --ignore-submodules --submodule=log HEAD >actual && ! test -s actual -" +' -test_expect_success 'modified submodule contains modifed content' " +test_expect_success 'modified submodule contains modifed content' ' rm -f sm1/new-file && git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 contains modified content -Submodule sm1 $head6..$head8: - > change -EOF + Submodule sm1 contains modified content + Submodule sm1 $head6..$head8: + > change + EOF test_cmp expected actual -" +' rm -rf sm1 -test_expect_success 'deleted submodule' " +test_expect_success 'deleted submodule' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head6...0000000 (submodule deleted) -EOF + Submodule sm1 $head6...0000000 (submodule deleted) + EOF test_cmp expected actual -" +' test_create_repo sm2 && head7=$(add_file sm2 foo8 foo9) && git add sm2 -test_expect_success 'multiple submodules' " +test_expect_success 'multiple submodules' ' git diff-index -p --submodule=log HEAD >actual && cat >expected <<-EOF && -Submodule sm1 $head6...0000000 (submodule deleted) -Submodule sm2 0000000...$head7 (new submodule) -EOF + Submodule sm1 $head6...0000000 (submodule deleted) + Submodule sm2 0000000...$head7 (new submodule) + EOF test_cmp expected actual -" +' -test_expect_success 'path filter' " +test_expect_success 'path filter' ' git diff-index -p --submodule=log HEAD sm2 >actual && cat >expected <<-EOF && -Submodule sm2 0000000...$head7 (new submodule) -EOF + Submodule sm2 0000000...$head7 (new submodule) + EOF test_cmp expected actual -" +' commit_file sm2 -test_expect_success 'given commit' " +test_expect_success 'given commit' ' git diff-index -p --submodule=log HEAD^ >actual && cat >expected <<-EOF && -Submodule sm1 $head6...0000000 (submodule deleted) -Submodule sm2 0000000...$head7 (new submodule) -EOF + Submodule sm1 $head6...0000000 (submodule deleted) + Submodule sm2 0000000...$head7 (new submodule) + EOF test_cmp expected actual -" +' -test_expect_success 'given commit --submodule' " +test_expect_success 'given commit --submodule' ' git diff-index -p --submodule HEAD^ >actual && cat >expected <<-EOF && -Submodule sm1 $head6...0000000 (submodule deleted) -Submodule sm2 0000000...$head7 (new submodule) -EOF + Submodule sm1 $head6...0000000 (submodule deleted) + Submodule sm2 0000000...$head7 (new submodule) + EOF test_cmp expected actual -" +' -fullhead7=$(cd sm2; git rev-list --max-count=1 $head7) +fullhead7=$(cd sm2; git rev-parse --verify HEAD) -test_expect_success 'given commit --submodule=short' " +test_expect_success 'given commit --submodule=short' ' git diff-index -p --submodule=short HEAD^ >actual && cat >expected <<-EOF && -diff --git a/sm1 b/sm1 -deleted file mode 160000 -index $head6..0000000 ---- a/sm1 -+++ /dev/null -@@ -1 +0,0 @@ --Subproject commit $fullhead6 -diff --git a/sm2 b/sm2 -new file mode 160000 -index 0000000..$head7 ---- /dev/null -+++ b/sm2 -@@ -0,0 +1 @@ -+Subproject commit $fullhead7 -EOF - test_cmp expected actual -" + diff --git a/sm1 b/sm1 + deleted file mode 160000 + index $head6..0000000 + --- a/sm1 + +++ /dev/null + @@ -1 +0,0 @@ + -Subproject commit $fullhead6 + diff --git a/sm2 b/sm2 + new file mode 160000 + index 0000000..$head7 + --- /dev/null + +++ b/sm2 + @@ -0,0 +1 @@ + +Subproject commit $fullhead7 + EOF + test_cmp expected actual +' test_expect_success 'setup .git file for sm2' ' (cd sm2 && @@ -452,9 +498,43 @@ test_expect_success 'setup .git file for sm2' ' test_expect_success 'diff --submodule with .git file' ' git diff --submodule HEAD^ >actual && cat >expected <<-EOF && -Submodule sm1 $head6...0000000 (submodule deleted) -Submodule sm2 0000000...$head7 (new submodule) -EOF + Submodule sm1 $head6...0000000 (submodule deleted) + Submodule sm2 0000000...$head7 (new submodule) + EOF + test_cmp expected actual +' + +test_expect_success 'diff --submodule with objects referenced by alternates' ' + mkdir sub_alt && + (cd sub_alt && + git init && + echo a >a && + git add a && + git commit -m a + ) && + mkdir super && + (cd super && + git clone -s ../sub_alt sub && + git init && + git add sub && + git commit -m "sub a" + ) && + (cd sub_alt && + sha1_before=$(git rev-parse --short HEAD) + echo b >b && + git add b && + git commit -m b + sha1_after=$(git rev-parse --short HEAD) + echo "Submodule sub $sha1_before..$sha1_after: + > b" >../expected + ) && + (cd super && + (cd sub && + git fetch && + git checkout origin/master + ) && + git diff --submodule > ../actual + ) test_cmp expected actual ' diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh index 91f8198f05..04a44d5c61 100755 --- a/t/t4042-diff-textconv-caching.sh +++ b/t/t4042-diff-textconv-caching.sh @@ -106,4 +106,12 @@ test_expect_success 'switching diff driver produces correct results' ' test_cmp expect actual ' +# The point here is to test that we can log the notes cache and still use it to +# produce a diff later (older versions of git would segfault on this). It's +# much more likely to come up in the real world with "log --all -p", but using +# --no-walk lets us reliably reproduce the order of traversal. +test_expect_success 'log notes cache and still use cache for -p' ' + git log --no-walk -p refs/notes/textconv/magic HEAD +' + test_done diff --git a/t/t4043-diff-rename-binary.sh b/t/t4043-diff-rename-binary.sh index 06012811a1..2a2cf91352 100755 --- a/t/t4043-diff-rename-binary.sh +++ b/t/t4043-diff-rename-binary.sh @@ -23,9 +23,8 @@ test_expect_success 'move the files into a "sub" directory' ' ' cat > expected <<\EOF - bar => sub/bar | Bin 5 -> 5 bytes - foo => sub/foo | 0 - 2 files changed, 0 insertions(+), 0 deletions(-) +- - bar => sub/bar +0 0 foo => sub/foo diff --git a/bar b/sub/bar similarity index 100% @@ -38,7 +37,8 @@ rename to sub/foo EOF test_expect_success 'git show -C -C report renames' ' - git show -C -C --raw --binary --stat | tail -n 12 > current && + git show -C -C --raw --binary --numstat >patch-with-stat && + tail -n 11 patch-with-stat >current && test_cmp expected current ' diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh index bd119be106..3950f5034d 100755 --- a/t/t4045-diff-relative.sh +++ b/t/t4045-diff-relative.sh @@ -29,15 +29,27 @@ test_expect_success "-p $*" " " } +check_numstat() { +expect=$1; shift +cat >expected <<EOF +1 0 $expect +EOF +test_expect_success "--numstat $*" " + echo '1 0 $expect' >expected && + git diff --numstat $* HEAD^ >actual && + test_cmp expected actual +" +} + check_stat() { expect=$1; shift cat >expected <<EOF - $expect | 1 + + $expect | 1 + 1 file changed, 1 insertion(+) EOF test_expect_success "--stat $*" " git diff --stat $* HEAD^ >actual && - test_cmp expected actual + test_i18ncmp expected actual " } @@ -52,7 +64,7 @@ test_expect_success "--raw $*" " " } -for type in diff stat raw; do +for type in diff numstat stat raw; do check_$type file2 --relative=subdir/ check_$type file2 --relative=subdir check_$type dir/file2 --relative=sub diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh index 29e80a58cd..ed7e093366 100755 --- a/t/t4047-diff-dirstat.sh +++ b/t/t4047-diff-dirstat.sh @@ -252,50 +252,47 @@ EOF ' cat <<EOF >expect_diff_stat - changed/text | 2 +- - dst/copy/changed/text | 10 ++++++++++ - dst/copy/rearranged/text | 10 ++++++++++ - dst/copy/unchanged/text | 10 ++++++++++ - dst/move/changed/text | 10 ++++++++++ - dst/move/rearranged/text | 10 ++++++++++ - dst/move/unchanged/text | 10 ++++++++++ - rearranged/text | 2 +- - src/move/changed/text | 10 ---------- - src/move/rearranged/text | 10 ---------- - src/move/unchanged/text | 10 ---------- - 11 files changed, 62 insertions(+), 32 deletions(-) +1 1 changed/text +10 0 dst/copy/changed/text +10 0 dst/copy/rearranged/text +10 0 dst/copy/unchanged/text +10 0 dst/move/changed/text +10 0 dst/move/rearranged/text +10 0 dst/move/unchanged/text +1 1 rearranged/text +0 10 src/move/changed/text +0 10 src/move/rearranged/text +0 10 src/move/unchanged/text EOF cat <<EOF >expect_diff_stat_M - changed/text | 2 +- - dst/copy/changed/text | 10 ++++++++++ - dst/copy/rearranged/text | 10 ++++++++++ - dst/copy/unchanged/text | 10 ++++++++++ - {src => dst}/move/changed/text | 2 +- - {src => dst}/move/rearranged/text | 2 +- - {src => dst}/move/unchanged/text | 0 - rearranged/text | 2 +- - 8 files changed, 34 insertions(+), 4 deletions(-) +1 1 changed/text +10 0 dst/copy/changed/text +10 0 dst/copy/rearranged/text +10 0 dst/copy/unchanged/text +1 1 {src => dst}/move/changed/text +1 1 {src => dst}/move/rearranged/text +0 0 {src => dst}/move/unchanged/text +1 1 rearranged/text EOF cat <<EOF >expect_diff_stat_CC - changed/text | 2 +- - {src => dst}/copy/changed/text | 2 +- - {src => dst}/copy/rearranged/text | 2 +- - {src => dst}/copy/unchanged/text | 0 - {src => dst}/move/changed/text | 2 +- - {src => dst}/move/rearranged/text | 2 +- - {src => dst}/move/unchanged/text | 0 - rearranged/text | 2 +- - 8 files changed, 6 insertions(+), 6 deletions(-) -EOF - -test_expect_success 'sanity check setup (--stat)' ' - git diff --stat HEAD^..HEAD >actual_diff_stat && +1 1 changed/text +1 1 {src => dst}/copy/changed/text +1 1 {src => dst}/copy/rearranged/text +0 0 {src => dst}/copy/unchanged/text +1 1 {src => dst}/move/changed/text +1 1 {src => dst}/move/rearranged/text +0 0 {src => dst}/move/unchanged/text +1 1 rearranged/text +EOF + +test_expect_success 'sanity check setup (--numstat)' ' + git diff --numstat HEAD^..HEAD >actual_diff_stat && test_cmp expect_diff_stat actual_diff_stat && - git diff --stat -M HEAD^..HEAD >actual_diff_stat_M && + git diff --numstat -M HEAD^..HEAD >actual_diff_stat_M && test_cmp expect_diff_stat_M actual_diff_stat_M && - git diff --stat -C -C HEAD^..HEAD >actual_diff_stat_CC && + git diff --numstat -C -C HEAD^..HEAD >actual_diff_stat_CC && test_cmp expect_diff_stat_CC actual_diff_stat_CC ' diff --git a/t/t4049-diff-stat-count.sh b/t/t4049-diff-stat-count.sh index a6d1887536..5b594e878f 100755 --- a/t/t4049-diff-stat-count.sh +++ b/t/t4049-diff-stat-count.sh @@ -4,22 +4,65 @@ test_description='diff --stat-count' . ./test-lib.sh -test_expect_success setup ' +test_expect_success 'setup' ' >a && >b && >c && >d && git add a b c d && - chmod +x c d && + git commit -m initial +' + +test_expect_success 'mode-only change show as a 0-line change' ' + git reset --hard && + test_chmod +x b d && + echo a >a && + echo c >c && + cat >expect <<-\EOF + a | 1 + + b | 0 + ... + 4 files changed, 2 insertions(+) + EOF + git diff --stat --stat-count=2 HEAD >actual && + test_i18ncmp expect actual +' + +test_expect_success 'binary changes do not count in lines' ' + git reset --hard && + echo a >a && + echo c >c && + cat "$TEST_DIRECTORY"/test-binary-1.png >d && + cat >expect <<-\EOF + a | 1 + + c | 1 + + ... + 3 files changed, 2 insertions(+) + EOF + git diff --stat --stat-count=2 >actual && + test_i18ncmp expect actual +' + +test_expect_success 'exclude unmerged entries from total file count' ' + git reset --hard && echo a >a && echo b >b && + git ls-files -s a >x && + git rm -f d && + for stage in 1 2 3 + do + sed -e "s/ 0 a/ $stage d/" x + done | + git update-index --index-info && + echo d >d && cat >expect <<-\EOF - a | 1 + - b | 1 + - 2 files changed, 2 insertions(+) + a | 1 + + b | 1 + + ... + 3 files changed, 3 insertions(+) EOF git diff --stat --stat-count=2 >actual && - test_cmp expect actual + test_i18ncmp expect actual ' test_done diff --git a/t/t4052-stat-output.sh b/t/t4052-stat-output.sh new file mode 100755 index 0000000000..b68afefa3c --- /dev/null +++ b/t/t4052-stat-output.sh @@ -0,0 +1,336 @@ +#!/bin/sh +# +# Copyright (c) 2012 Zbigniew JÄ™drzejewski-Szmek +# + +test_description='test --stat output of various commands' + +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-terminal.sh + +# 120 character name +name=aaaaaaaaaa +name=$name$name$name$name$name$name$name$name$name$name$name$name +test_expect_success 'preparation' ' + >"$name" && + git add "$name" && + git commit -m message && + echo a >"$name" && + git commit -m message "$name" +' + +while read cmd args +do + cat >expect <<-'EOF' + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + + EOF + test_expect_success "$cmd: small change with long name gives more space to the name" ' + git $cmd $args >output && + grep " | " output >actual && + test_cmp expect actual + ' + + cat >expect <<-'EOF' + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + + EOF + test_expect_success "$cmd --stat=width: a long name is given more room when the bar is short" ' + git $cmd $args --stat=40 >output && + grep " | " output >actual && + test_cmp expect actual + ' + + test_expect_success "$cmd --stat-width=width with long name" ' + git $cmd $args --stat-width=40 >output && + grep " | " output >actual && + test_cmp expect actual + ' + + cat >expect <<-'EOF' + ...aaaaaaaaaaaaaaaaaaaaaaaaaaa | 1 + + EOF + test_expect_success "$cmd --stat=...,name-width with long name" ' + git $cmd $args --stat=60,30 >output && + grep " | " output >actual && + test_cmp expect actual + ' + + test_expect_success "$cmd --stat-name-width with long name" ' + git $cmd $args --stat-name-width=30 >output && + grep " | " output >actual && + test_cmp expect actual + ' +done <<\EOF +format-patch -1 --stdout +diff HEAD^ HEAD --stat +show --stat +log -1 --stat +EOF + + +test_expect_success 'preparation for big change tests' ' + >abcd && + git add abcd && + git commit -m message && + i=0 && + while test $i -lt 1000 + do + echo $i && i=$(($i + 1)) + done >abcd && + git commit -m message abcd +' + +cat >expect80 <<'EOF' + abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +EOF +cat >expect80-graph <<'EOF' +| abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +EOF +cat >expect200 <<'EOF' + abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +EOF +cat >expect200-graph <<'EOF' +| abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +EOF +while read verb expect cmd args +do + test_expect_success "$cmd $verb COLUMNS (big change)" ' + COLUMNS=200 git $cmd $args >output + grep " | " output >actual && + test_cmp "$expect" actual + ' + + test "$cmd" != diff || continue + + test_expect_success "$cmd --graph $verb COLUMNS (big change)" ' + COLUMNS=200 git $cmd $args --graph >output + grep " | " output >actual && + test_cmp "$expect-graph" actual + ' +done <<\EOF +ignores expect80 format-patch -1 --stdout +respects expect200 diff HEAD^ HEAD --stat +respects expect200 show --stat +respects expect200 log -1 --stat +EOF + +cat >expect40 <<'EOF' + abcd | 1000 ++++++++++++++++++++++++++ +EOF +cat >expect40-graph <<'EOF' +| abcd | 1000 ++++++++++++++++++++++++ +EOF +while read verb expect cmd args +do + test_expect_success "$cmd $verb not enough COLUMNS (big change)" ' + COLUMNS=40 git $cmd $args >output + grep " | " output >actual && + test_cmp "$expect" actual + ' + + test "$cmd" != diff || continue + + test_expect_success "$cmd --graph $verb not enough COLUMNS (big change)" ' + COLUMNS=40 git $cmd $args --graph >output + grep " | " output >actual && + test_cmp "$expect-graph" actual + ' +done <<\EOF +ignores expect80 format-patch -1 --stdout +respects expect40 diff HEAD^ HEAD --stat +respects expect40 show --stat +respects expect40 log -1 --stat +EOF + +cat >expect40 <<'EOF' + abcd | 1000 ++++++++++++++++++++++++++ +EOF +cat >expect40-graph <<'EOF' +| abcd | 1000 ++++++++++++++++++++++++++ +EOF +while read verb expect cmd args +do + test_expect_success "$cmd $verb statGraphWidth config" ' + git -c diff.statGraphWidth=26 $cmd $args >output + grep " | " output >actual && + test_cmp "$expect" actual + ' + + test "$cmd" != diff || continue + + test_expect_success "$cmd --graph $verb statGraphWidth config" ' + git -c diff.statGraphWidth=26 $cmd $args --graph >output + grep " | " output >actual && + test_cmp "$expect-graph" actual + ' +done <<\EOF +ignores expect80 format-patch -1 --stdout +respects expect40 diff HEAD^ HEAD --stat +respects expect40 show --stat +respects expect40 log -1 --stat +EOF + + +cat >expect <<'EOF' + abcd | 1000 ++++++++++++++++++++++++++ +EOF +cat >expect-graph <<'EOF' +| abcd | 1000 ++++++++++++++++++++++++++ +EOF +while read cmd args +do + test_expect_success "$cmd --stat=width with big change" ' + git $cmd $args --stat=40 >output + grep " | " output >actual && + test_cmp expect actual + ' + + test_expect_success "$cmd --stat-width=width with big change" ' + git $cmd $args --stat-width=40 >output + grep " | " output >actual && + test_cmp expect actual + ' + + test_expect_success "$cmd --stat-graph-width with big change" ' + git $cmd $args --stat-graph-width=26 >output + grep " | " output >actual && + test_cmp expect actual + ' + + test "$cmd" != diff || continue + + test_expect_success "$cmd --stat-width=width --graph with big change" ' + git $cmd $args --stat-width=40 --graph >output + grep " | " output >actual && + test_cmp expect-graph actual + ' + + test_expect_success "$cmd --stat-graph-width --graph with big change" ' + git $cmd $args --stat-graph-width=26 --graph >output + grep " | " output >actual && + test_cmp expect-graph actual + ' +done <<\EOF +format-patch -1 --stdout +diff HEAD^ HEAD --stat +show --stat +log -1 --stat +EOF + +test_expect_success 'preparation for long filename tests' ' + cp abcd aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && + git add aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && + git commit -m message +' + +cat >expect <<'EOF' + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++ +EOF +cat >expect-graph <<'EOF' +| ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++ +EOF +while read cmd args +do + test_expect_success "$cmd --stat=width with big change is more balanced" ' + git $cmd $args --stat-width=60 >output && + grep " | " output >actual && + test_cmp expect actual + ' + + test "$cmd" != diff || continue + + test_expect_success "$cmd --stat=width --graph with big change is balanced" ' + git $cmd $args --stat-width=60 --graph >output && + grep " | " output >actual && + test_cmp expect-graph actual + ' +done <<\EOF +format-patch -1 --stdout +diff HEAD^ HEAD --stat +show --stat +log -1 --stat +EOF + +cat >expect80 <<'EOF' + ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++ +EOF +cat >expect80-graph <<'EOF' +| ...aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 ++++++++++++++++++++ +EOF +cat >expect200 <<'EOF' + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +EOF +cat >expect200-graph <<'EOF' +| aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +EOF +while read verb expect cmd args +do + test_expect_success "$cmd $verb COLUMNS (long filename)" ' + COLUMNS=200 git $cmd $args >output + grep " | " output >actual && + test_cmp "$expect" actual + ' + + test "$cmd" != diff || continue + + test_expect_success "$cmd --graph $verb COLUMNS (long filename)" ' + COLUMNS=200 git $cmd $args --graph >output + grep " | " output >actual && + test_cmp "$expect-graph" actual + ' +done <<\EOF +ignores expect80 format-patch -1 --stdout +respects expect200 diff HEAD^ HEAD --stat +respects expect200 show --stat +respects expect200 log -1 --stat +EOF + +cat >expect1 <<'EOF' + ...aaaaaaa | 1000 ++++++ +EOF +cat >expect1-graph <<'EOF' +| ...aaaaaaa | 1000 ++++++ +EOF +while read verb expect cmd args +do + test_expect_success COLUMNS_CAN_BE_1 \ + "$cmd $verb prefix greater than COLUMNS (big change)" ' + COLUMNS=1 git $cmd $args >output + grep " | " output >actual && + test_cmp "$expect" actual + ' + + test "$cmd" != diff || continue + + test_expect_success COLUMNS_CAN_BE_1 \ + "$cmd --graph $verb prefix greater than COLUMNS (big change)" ' + COLUMNS=1 git $cmd $args --graph >output + grep " | " output >actual && + test_cmp "$expect-graph" actual + ' +done <<\EOF +ignores expect80 format-patch -1 --stdout +respects expect1 diff HEAD^ HEAD --stat +respects expect1 show --stat +respects expect1 log -1 --stat +EOF + +cat >expect <<'EOF' + abcd | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +EOF +test_expect_success 'merge --stat respects COLUMNS (big change)' ' + git checkout -b branch HEAD^^ && + COLUMNS=100 git merge --stat --no-ff master^ >output && + grep " | " output >actual + test_cmp expect actual +' + +cat >expect <<'EOF' + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | 1000 +++++++++++++++++++++++++++++++++++++++ +EOF +test_expect_success 'merge --stat respects COLUMNS (long filename)' ' + COLUMNS=100 git merge --stat --no-ff master >output && + grep " | " output >actual + test_cmp expect actual +' + +test_done diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh new file mode 100755 index 0000000000..979e98398b --- /dev/null +++ b/t/t4053-diff-no-index.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +test_description='diff --no-index' + +. ./test-lib.sh + +test_expect_success 'setup' ' + mkdir a && + mkdir b && + echo 1 >a/1 && + echo 2 >a/2 && + git init repo && + echo 1 >repo/a && + mkdir -p non/git && + echo 1 >non/git/a && + echo 1 >non/git/b +' + +test_expect_success 'git diff --no-index directories' ' + git diff --no-index a b >cnt + test $? = 1 && test_line_count = 14 cnt +' + +test_expect_success 'git diff --no-index relative path outside repo' ' + ( + cd repo && + test_expect_code 0 git diff --no-index a ../non/git/a && + test_expect_code 0 git diff --no-index ../non/git/a ../non/git/b + ) +' + +test_done diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh new file mode 100755 index 0000000000..cd0454356a --- /dev/null +++ b/t/t4055-diff-context.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# +# Copyright (c) 2012 Mozilla Foundation +# + +test_description='diff.context configuration' + +. ./test-lib.sh + +test_expect_success 'setup' ' + cat >template <<-\EOF && + firstline + b + c + d + e + f + preline + TARGET + postline + i + j + k + l + m + n + EOF + sed "/TARGET/d" >x <template && + git update-index --add x && + git commit -m initial && + + sed "s/TARGET/ADDED/" >x <template && + git update-index --add x && + git commit -m next && + + sed "s/TARGET/MODIFIED/" >x <template +' + +test_expect_success 'the default number of context lines is 3' ' + git diff >output && + ! grep "^ d" output && + grep "^ e" output && + grep "^ j" output && + ! grep "^ k" output +' + +test_expect_success 'diff.context honored by "log"' ' + git log -1 -p >output && + ! grep firstline output && + git config diff.context 8 && + git log -1 -p >output && + grep "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context' ' + git config diff.context 8 && + git log -U4 -1 >output && + ! grep "^ firstline" output +' + +test_expect_success 'diff.context honored by "diff"' ' + git config diff.context 8 && + git diff >output && + grep "^ firstline" output +' + +test_expect_success 'plumbing not affected' ' + git config diff.context 8 && + git diff-files -p >output && + ! grep "^ firstline" output +' + +test_expect_success 'non-integer config parsing' ' + git config diff.context no && + test_must_fail git diff 2>output && + test_i18ngrep "bad numeric config value" output +' + +test_expect_success 'negative integer config parsing' ' + git config diff.context -1 && + test_must_fail git diff 2>output && + test_i18ngrep "bad config file" output +' + +test_expect_success '-U0 is valid, so is diff.context=0' ' + git config diff.context 0 && + git diff >output && + grep "^-ADDED" output && + grep "^+MODIFIED" output +' + +test_done diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh index 9b433de836..744b8e51be 100755 --- a/t/t4100-apply-stat.sh +++ b/t/t4100-apply-stat.sh @@ -17,13 +17,13 @@ do test_expect_success "$title" ' git apply --stat --summary \ <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current && - test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current + test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current ' test_expect_success "$title with recount" ' sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" | git apply --recount --stat --summary >current && - test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current + test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current ' done <<\EOF rename diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh index dbbf56cba9..1b420e3b5f 100755 --- a/t/t4103-apply-binary.sh +++ b/t/t4103-apply-binary.sh @@ -8,30 +8,28 @@ 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 - -test_expect_success 'setup' " +test_expect_success '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 commit -m "Initial Version" 2>/dev/null && git checkout -b binary && - perl -pe 'y/x/\000/' <file1 >file3 && + perl -pe "y/x/\000/" <file1 >file3 && cat file3 >file4 && git add file2 && - perl -pe 'y/\000/v/' <file3 >file1 && + perl -pe "y/\000/v/" <file3 >file1 && rm -f file2 && git update-index --add --remove file1 file2 file3 file4 && - git commit -m 'Second Version' && + git commit -m "Second Version" && git diff-tree -p master binary >B.diff && git diff-tree -p -C master binary >C.diff && @@ -42,17 +40,25 @@ test_expect_success 'setup' " git diff-tree -p --full-index master binary >B-index.diff && git diff-tree -p -C --full-index master binary >C-index.diff && + git diff-tree -p --binary --no-prefix master binary -- file3 >B0.diff && + git init other-repo && - (cd other-repo && - git fetch .. master && - git reset --hard FETCH_HEAD + ( + cd other-repo && + git fetch .. master && + git reset --hard FETCH_HEAD ) -" +' test_expect_success 'stat binary diff -- should not fail.' \ 'git checkout master && git apply --stat --summary B.diff' +test_expect_success 'stat binary -p0 diff -- should not fail.' ' + git checkout master && + git apply --stat -p0 B0.diff +' + test_expect_success 'stat binary diff (copy) -- should not fail.' \ 'git checkout master && git apply --stat --summary C.diff' @@ -143,4 +149,10 @@ test_expect_success 'apply binary diff (copy).' \ git apply --allow-binary-replacement --index CF.diff && test -z "$(git diff --name-status binary)"' +test_expect_success 'apply binary -p0 diff' ' + do_reset && + git apply -p0 --index B0.diff && + test -z "$(git diff --name-status binary -- file3)" +' + test_done diff --git a/t/t4108-apply-threeway.sh b/t/t4108-apply-threeway.sh new file mode 100755 index 0000000000..fa5d4efb89 --- /dev/null +++ b/t/t4108-apply-threeway.sh @@ -0,0 +1,157 @@ +#!/bin/sh + +test_description='git apply --3way' + +. ./test-lib.sh + +create_file () { + for i + do + echo "$i" + done +} + +sanitize_conflicted_diff () { + sed -e ' + /^index /d + s/^\(+[<>][<>][<>][<>]*\) .*/\1/ + ' +} + +test_expect_success setup ' + test_tick && + create_file >one 1 2 3 4 5 6 7 && + cat one >two && + git add one two && + git commit -m initial && + + git branch side && + + test_tick && + create_file >one 1 two 3 4 5 six 7 && + create_file >two 1 two 3 4 5 6 7 && + git commit -a -m master && + + git checkout side && + create_file >one 1 2 3 4 five 6 7 && + create_file >two 1 2 3 4 five 6 7 && + git commit -a -m side && + + git checkout master +' + +test_expect_success 'apply without --3way' ' + git diff side^ side >P.diff && + + # should fail to apply + git reset --hard && + git checkout master^0 && + test_must_fail git apply --index P.diff && + # should leave things intact + git diff-files --exit-code && + git diff-index --exit-code --cached HEAD +' + +test_expect_success 'apply with --3way' ' + # Merging side should be similar to applying this patch + git diff ...side >P.diff && + + # The corresponding conflicted merge + git reset --hard && + git checkout master^0 && + test_must_fail git merge --no-commit side && + git ls-files -s >expect.ls && + git diff HEAD | sanitize_conflicted_diff >expect.diff && + + # should fail to apply + git reset --hard && + git checkout master^0 && + test_must_fail git apply --index --3way P.diff && + git ls-files -s >actual.ls && + git diff HEAD | sanitize_conflicted_diff >actual.diff && + + # The result should resemble the corresponding merge + test_cmp expect.ls actual.ls && + test_cmp expect.diff actual.diff +' + +test_expect_success 'apply with --3way with rerere enabled' ' + git config rerere.enabled true && + + # Merging side should be similar to applying this patch + git diff ...side >P.diff && + + # The corresponding conflicted merge + git reset --hard && + git checkout master^0 && + test_must_fail git merge --no-commit side && + + # Manually resolve and record the resolution + create_file 1 two 3 4 five six 7 >one && + git rerere && + cat one >expect && + + # should fail to apply + git reset --hard && + git checkout master^0 && + test_must_fail git apply --index --3way P.diff && + + # but rerere should have replayed the recorded resolution + test_cmp expect one +' + +test_expect_success 'apply -3 with add/add conflict setup' ' + git reset --hard && + + git checkout -b adder && + create_file 1 2 3 4 5 6 7 >three && + create_file 1 2 3 4 5 6 7 >four && + git add three four && + git commit -m "add three and four" && + + git checkout -b another adder^ && + create_file 1 2 3 4 5 6 7 >three && + create_file 1 2 3 four 5 6 7 >four && + git add three four && + git commit -m "add three and four" && + + # Merging another should be similar to applying this patch + git diff adder...another >P.diff && + + git checkout adder^0 && + test_must_fail git merge --no-commit another && + git ls-files -s >expect.ls && + git diff HEAD | sanitize_conflicted_diff >expect.diff +' + +test_expect_success 'apply -3 with add/add conflict' ' + # should fail to apply ... + git reset --hard && + git checkout adder^0 && + test_must_fail git apply --index --3way P.diff && + # ... and leave conflicts in the index and in the working tree + git ls-files -s >actual.ls && + git diff HEAD | sanitize_conflicted_diff >actual.diff && + + # The result should resemble the corresponding merge + test_cmp expect.ls actual.ls && + test_cmp expect.diff actual.diff +' + +test_expect_success 'apply -3 with add/add conflict (dirty working tree)' ' + # should fail to apply ... + git reset --hard && + git checkout adder^0 && + echo >>four && + cat four >four.save && + cat three >three.save && + git ls-files -s >expect.ls && + test_must_fail git apply --index --3way P.diff && + # ... and should not touch anything + git ls-files -s >actual.ls && + test_cmp expect.ls actual.ls && + test_cmp four.save four && + test_cmp three.save three +' + +test_done diff --git a/t/t4111-apply-subdir.sh b/t/t4111-apply-subdir.sh index 7c398432ba..1618a6dbc7 100755 --- a/t/t4111-apply-subdir.sh +++ b/t/t4111-apply-subdir.sh @@ -86,6 +86,20 @@ test_expect_success 'apply --index from subdir of toplevel' ' test_cmp expected sub/dir/file ' +test_expect_success 'apply half-broken patch from subdir of toplevel' ' + ( + cd sub/dir && + test_must_fail git apply <<-EOF + --- sub/dir/file + +++ sub/dir/file + @@ -1,0 +1,0 @@ + --- file_in_root + +++ file_in_root + @@ -1,0 +1,0 @@ + EOF + ) +' + test_expect_success 'apply from .git dir' ' cp postimage expected && cp preimage .git/file && diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh index f12826fb09..ebadbc347f 100755 --- a/t/t4114-apply-typechange.sh +++ b/t/t4114-apply-typechange.sh @@ -9,20 +9,19 @@ test_description='git apply should not get confused with type changes. . ./test-lib.sh -test_expect_success SYMLINKS 'setup repository and commits' ' +test_expect_success 'setup repository and commits' ' echo "hello world" > foo && echo "hi planet" > bar && git update-index --add foo bar && git commit -m initial && git branch initial && rm -f foo && - ln -s bar foo && - git update-index foo && + test_ln_s_add bar foo && git commit -m "foo symlinked to bar" && git branch foo-symlinked-to-bar && - rm -f foo && + git rm -f foo && echo "how far is the sun?" > foo && - git update-index foo && + git update-index --add foo && git commit -m "foo back to file" && git branch foo-back-to-file && printf "\0" > foo && @@ -42,7 +41,7 @@ test_expect_success SYMLINKS 'setup repository and commits' ' git branch foo-baz-renamed-from-foo ' -test_expect_success SYMLINKS 'file renamed from foo to foo/baz' ' +test_expect_success 'file renamed from foo to foo/baz' ' git checkout -f initial && git diff-tree -M -p HEAD foo-baz-renamed-from-foo > patch && git apply --index < patch @@ -50,7 +49,7 @@ test_expect_success SYMLINKS 'file renamed from foo to foo/baz' ' test_debug 'cat patch' -test_expect_success SYMLINKS 'file renamed from foo/baz to foo' ' +test_expect_success 'file renamed from foo/baz to foo' ' git checkout -f foo-baz-renamed-from-foo && git diff-tree -M -p HEAD initial > patch && git apply --index < patch @@ -58,7 +57,7 @@ test_expect_success SYMLINKS 'file renamed from foo/baz to foo' ' test_debug 'cat patch' -test_expect_success SYMLINKS 'directory becomes file' ' +test_expect_success 'directory becomes file' ' git checkout -f foo-becomes-a-directory && git diff-tree -p HEAD initial > patch && git apply --index < patch @@ -66,7 +65,7 @@ test_expect_success SYMLINKS 'directory becomes file' ' test_debug 'cat patch' -test_expect_success SYMLINKS 'file becomes directory' ' +test_expect_success 'file becomes directory' ' git checkout -f initial && git diff-tree -p HEAD foo-becomes-a-directory > patch && git apply --index < patch @@ -74,7 +73,7 @@ test_expect_success SYMLINKS 'file becomes directory' ' test_debug 'cat patch' -test_expect_success SYMLINKS 'file becomes symlink' ' +test_expect_success 'file becomes symlink' ' git checkout -f initial && git diff-tree -p HEAD foo-symlinked-to-bar > patch && git apply --index < patch @@ -82,21 +81,21 @@ test_expect_success SYMLINKS 'file becomes symlink' ' test_debug 'cat patch' -test_expect_success SYMLINKS 'symlink becomes file' ' +test_expect_success 'symlink becomes file' ' git checkout -f foo-symlinked-to-bar && git diff-tree -p HEAD foo-back-to-file > patch && git apply --index < patch ' test_debug 'cat patch' -test_expect_success SYMLINKS 'binary file becomes symlink' ' +test_expect_success 'binary file becomes symlink' ' git checkout -f foo-becomes-binary && git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch && git apply --index < patch ' test_debug 'cat patch' -test_expect_success SYMLINKS 'symlink becomes binary file' ' +test_expect_success 'symlink becomes binary file' ' git checkout -f foo-symlinked-to-bar && git diff-tree -p --binary HEAD foo-becomes-binary > patch && git apply --index < patch @@ -104,7 +103,7 @@ test_expect_success SYMLINKS 'symlink becomes binary file' ' test_debug 'cat patch' -test_expect_success SYMLINKS 'symlink becomes directory' ' +test_expect_success 'symlink becomes directory' ' git checkout -f foo-symlinked-to-bar && git diff-tree -p HEAD foo-becomes-a-directory > patch && git apply --index < patch @@ -112,7 +111,7 @@ test_expect_success SYMLINKS 'symlink becomes directory' ' test_debug 'cat patch' -test_expect_success SYMLINKS 'directory becomes symlink' ' +test_expect_success 'directory becomes symlink' ' git checkout -f foo-becomes-a-directory && git diff-tree -p HEAD foo-symlinked-to-bar > patch && git apply --index < patch diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh index 7674dd2ec9..872fcda6cb 100755 --- a/t/t4115-apply-symlink.sh +++ b/t/t4115-apply-symlink.sh @@ -9,18 +9,16 @@ test_description='git apply symlinks and partial files . ./test-lib.sh -test_expect_success SYMLINKS setup ' +test_expect_success setup ' - ln -s path1/path2/path3/path4/path5 link1 && - git add link? && + test_ln_s_add path1/path2/path3/path4/path5 link1 && git commit -m initial && git branch side && rm -f link? && - ln -s htap6 link1 && - git update-index link? && + test_ln_s_add htap6 link1 && git commit -m second && git diff-tree -p HEAD^ HEAD >patch && @@ -37,7 +35,7 @@ test_expect_success SYMLINKS 'apply symlink patch' ' ' -test_expect_success SYMLINKS 'apply --index symlink patch' ' +test_expect_success 'apply --index symlink patch' ' git checkout -f side && git apply --index patch && diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh index e9ccd161ee..8e15ecbdfd 100755 --- a/t/t4117-apply-reject.sh +++ b/t/t4117-apply-reject.sh @@ -46,6 +46,14 @@ test_expect_success setup ' cat file1 >saved.file1 ' +test_expect_success 'apply --reject is incompatible with --3way' ' + test_when_finished "cat saved.file1 >file1" && + git diff >patch.0 && + git checkout file1 && + test_must_fail git apply --reject --3way patch.0 && + git diff --exit-code +' + test_expect_success 'apply without --reject should fail' ' if git apply patch.1 diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh index a33d510bf6..c5fecdfed4 100755 --- a/t/t4120-apply-popt.sh +++ b/t/t4120-apply-popt.sh @@ -32,7 +32,7 @@ test_expect_success 'apply git diff with -p2' ' test_expect_success 'apply with too large -p' ' cp file1.saved file1 && test_must_fail git apply --stat -p3 patch.file 2>err && - grep "removing 3 leading" err + test_i18ngrep "removing 3 leading" err ' test_expect_success 'apply (-p2) traditional diff with funny filenames' ' @@ -54,7 +54,7 @@ test_expect_success 'apply (-p2) traditional diff with funny filenames' ' test_expect_success 'apply with too large -p and fancy filename' ' cp file1.saved file1 && test_must_fail git apply --stat -p3 patch.escaped 2>err && - grep "removing 3 leading" err + test_i18ngrep "removing 3 leading" err ' test_expect_success 'apply (-p2) diff, mode change only' ' diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh index 39407376ba..70b3a06e1d 100755 --- a/t/t4122-apply-symlink-inside.sh +++ b/t/t4122-apply-symlink-inside.sh @@ -10,11 +10,11 @@ lecho () { done } -test_expect_success SYMLINKS setup ' +test_expect_success setup ' mkdir -p arch/i386/boot arch/x86_64 && lecho 1 2 3 4 5 >arch/i386/boot/Makefile && - ln -s ../i386/boot arch/x86_64/boot && + test_ln_s_add ../i386/boot arch/x86_64/boot && git add . && test_tick && git commit -m initial && @@ -31,7 +31,7 @@ test_expect_success SYMLINKS setup ' ' -test_expect_success SYMLINKS apply ' +test_expect_success apply ' git checkout test && git diff --exit-code test && @@ -40,7 +40,7 @@ test_expect_success SYMLINKS apply ' ' -test_expect_success SYMLINKS 'check result' ' +test_expect_success 'check result' ' git diff --exit-code master && git diff --exit-code --cached master && diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh index 6f6ee88b28..5d0c598338 100755 --- a/t/t4124-apply-ws-rule.sh +++ b/t/t4124-apply-ws-rule.sh @@ -47,7 +47,7 @@ test_fix () { # find touched lines $DIFF file target | sed -n -e "s/^> //p" >fixed - # the changed lines are all expeced to change + # the changed lines are all expected to change fixed_cnt=$(wc -l <fixed) case "$1" in '') expect_cnt=$fixed_cnt ;; @@ -486,4 +486,30 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' ' test_cmp one expect ' +test_expect_success 'whitespace=fix to expand' ' + qz_to_tab_space >preimage <<-\EOF && + QQa + QQb + QQc + ZZZZZZZZZZZZZZZZd + QQe + QQf + QQg + EOF + qz_to_tab_space >patch <<-\EOF && + diff --git a/preimage b/preimage + --- a/preimage + +++ b/preimage + @@ -1,7 +1,6 @@ + QQa + QQb + QQc + -QQd + QQe + QQf + QQg + EOF + git -c core.whitespace=tab-in-indent apply --whitespace=fix patch +' + test_done diff --git a/t/t4133-apply-filenames.sh b/t/t4133-apply-filenames.sh index 94da99075c..2ecb4216b7 100755 --- a/t/t4133-apply-filenames.sh +++ b/t/t4133-apply-filenames.sh @@ -30,9 +30,9 @@ EOF test_expect_success 'apply diff with inconsistent filenames in headers' ' test_must_fail git apply bad1.patch 2>err && - grep "inconsistent new filename" err && + test_i18ngrep "inconsistent new filename" err && test_must_fail git apply bad2.patch 2>err && - grep "inconsistent old filename" err + test_i18ngrep "inconsistent old filename" err ' test_done diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 658354a6ce..5edb79a058 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -17,7 +17,7 @@ test_expect_success 'setup: messages' ' vero eos et accusam et justo duo dolores et ea rebum. EOF - q_to_tab <<-\EOF >>msg && + qz_to_tab_space <<-\EOF >>msg && QDuis autem vel eum iriure dolor in hendrerit in vulputate velit Qesse molestie consequat, vel illum dolore eu feugiat nulla facilisis Qat vero eros et accumsan et iusto odio dignissim qui blandit @@ -147,7 +147,7 @@ test_expect_success 'am applies patch correctly' ' git checkout first && test_tick && git am <patch1 && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git diff --exit-code second && test "$(git rev-parse second)" = "$(git rev-parse HEAD)" && test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)" @@ -158,7 +158,7 @@ test_expect_success 'am applies patch e-mail not in a mbox' ' git reset --hard && git checkout first && git am patch1.eml && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git diff --exit-code second && test "$(git rev-parse second)" = "$(git rev-parse HEAD)" && test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)" @@ -169,7 +169,7 @@ test_expect_success 'am applies patch e-mail not in a mbox with CRLF' ' git reset --hard && git checkout first && git am patch1-crlf.eml && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git diff --exit-code second && test "$(git rev-parse second)" = "$(git rev-parse HEAD)" && test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)" @@ -180,7 +180,7 @@ test_expect_success 'am applies patch e-mail with preceding whitespace' ' git reset --hard && git checkout first && git am patch1-ws.eml && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git diff --exit-code second && test "$(git rev-parse second)" = "$(git rev-parse HEAD)" && test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)" @@ -206,7 +206,7 @@ test_expect_success 'am changes committer and keeps author' ' git reset --hard && git checkout first && git am patch2 && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" && git diff --exit-code master..HEAD && git diff --exit-code master^..HEAD^ && @@ -238,7 +238,7 @@ test_expect_success 'am stays in branch' ' test_expect_success 'am --signoff does not add Signed-off-by: line if already there' ' git format-patch --stdout HEAD^ >patch3 && - sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4 && + sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2] [foo," patch3 >patch4 && rm -fr .git/rebase-apply && git reset --hard && git checkout HEAD^ && @@ -258,9 +258,19 @@ test_expect_success 'am --keep really keeps the subject' ' git reset --hard && git checkout HEAD^ && git am --keep patch4 && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git cat-file commit HEAD >actual && - grep "Re: Re: Re: \[PATCH 1/5 v2\] third" actual + grep "Re: Re: Re: \[PATCH 1/5 v2\] \[foo\] third" actual +' + +test_expect_success 'am --keep-non-patch really keeps the non-patch part' ' + rm -fr .git/rebase-apply && + git reset --hard && + git checkout HEAD^ && + git am --keep-non-patch patch4 && + test_path_is_missing .git/rebase-apply && + git cat-file commit HEAD >actual && + grep "^\[foo\] third" actual ' test_expect_success 'am -3 falls back to 3-way merge' ' @@ -273,7 +283,7 @@ test_expect_success 'am -3 falls back to 3-way merge' ' test_tick && git commit -m "copied stuff" && git am -3 lorem-move.patch && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git diff --exit-code lorem ' @@ -287,7 +297,7 @@ test_expect_success 'am -3 -p0 can read --no-prefix patch' ' test_tick && git commit -m "copied stuff" && git am -3 -p0 lorem-zero.patch && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git diff --exit-code lorem ' @@ -297,7 +307,7 @@ test_expect_success 'am can rename a file' ' git reset --hard && git checkout lorem^0 && git am rename.patch && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git update-index --refresh && git diff --exit-code rename ' @@ -308,7 +318,7 @@ test_expect_success 'am -3 can rename a file' ' git reset --hard && git checkout lorem^0 && git am -3 rename.patch && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git update-index --refresh && git diff --exit-code rename ' @@ -319,7 +329,7 @@ test_expect_success 'am -3 can rename a file after falling back to 3-way merge' git reset --hard && git checkout lorem^0 && git am -3 rename-add.patch && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git update-index --refresh && git diff --exit-code rename ' @@ -348,11 +358,17 @@ test_expect_success 'am pauses on conflict' ' test_expect_success 'am --skip works' ' echo goodbye >expected && git am --skip && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git diff --exit-code lorem2^^ -- file && test_cmp expected another ' +test_expect_success 'am --abort removes a stray directory' ' + mkdir .git/rebase-apply && + git am --abort && + test_path_is_missing .git/rebase-apply +' + test_expect_success 'am --resolved works' ' echo goodbye >expected && rm -fr .git/rebase-apply && @@ -363,7 +379,7 @@ test_expect_success 'am --resolved works' ' echo resolved >>file && git add file && git am --resolved && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && test_cmp expected another ' @@ -372,7 +388,7 @@ test_expect_success 'am takes patches from a Pine mailbox' ' git reset --hard && git checkout first && cat pine patch1 | git am && - ! test -d .git/rebase-apply && + test_path_is_missing .git/rebase-apply && git diff --exit-code master^..HEAD ' @@ -381,7 +397,7 @@ test_expect_success 'am fails on mail without patch' ' git reset --hard && test_must_fail git am <failmail && git am --abort && - ! test -d .git/rebase-apply + test_path_is_missing .git/rebase-apply ' test_expect_success 'am fails on empty patch' ' @@ -390,7 +406,7 @@ test_expect_success 'am fails on empty patch' ' echo "---" >>failmail && test_must_fail git am <failmail && git am --skip && - ! test -d .git/rebase-apply + test_path_is_missing .git/rebase-apply ' test_expect_success 'am works from stdin in subdirectory' ' @@ -515,9 +531,9 @@ test_expect_success 'am empty-file does not infloop' ' git reset --hard && touch empty-file && test_tick && - { git am empty-file > actual 2>&1 && false || :; } && + test_must_fail git am empty-file 2>actual && echo Patch format detection failed. >expected && - test_cmp expected actual + test_i18ncmp expected actual ' test_done diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index 36255d608a..ed9c91e25b 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -172,7 +172,7 @@ test_expect_success 'first postimage wins' ' git show second^:a1 | sed "s/To die: t/To die! T/" >a1 && git commit -q -a -m third && - test_must_fail git pull . first && + test_must_fail git merge first && # rerere kicked in ! grep "^=======\$" a1 && test_cmp expect a1 @@ -382,13 +382,13 @@ test_expect_success 'rerere --no-no-rerere-autoupdate' ' git update-index --index-info <failedmerge && cp file3.conflict file3 && test_must_fail git rerere --no-no-rerere-autoupdate 2>err && - grep [Uu]sage err && + test_i18ngrep [Uu]sage err && test_must_fail git update-index --refresh ' test_expect_success 'rerere -h' ' test_must_fail git rerere -h >help && - grep [Uu]sage help + test_i18ngrep [Uu]sage help ' test_done diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index 6872ba1a42..42866992cf 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -120,6 +120,30 @@ test_expect_success 'shortlog from non-git directory' ' test_cmp expect out ' +test_expect_success 'shortlog should add newline when input line matches wraplen' ' + cat >expect <<\EOF && +A U Thor (2): + bbbbbbbbbbbbbbbbbb: bbbbbbbb bbb bbbb bbbbbbb bb bbbb bbb bbbbb bbbbbb + aaaaaaaaaaaaaaaaaaaaaa: aaaaaa aaaaaaaaaa aaaa aaaaaaaa aa aaaa aa aaa + +EOF + git shortlog -w >out <<\EOF && +commit 0000000000000000000000000000000000000001 +Author: A U Thor <author@example.com> +Date: Thu Apr 7 15:14:13 2005 -0700 + + aaaaaaaaaaaaaaaaaaaaaa: aaaaaa aaaaaaaaaa aaaa aaaaaaaa aa aaaa aa aaa + +commit 0000000000000000000000000000000000000002 +Author: A U Thor <author@example.com> +Date: Thu Apr 7 15:14:13 2005 -0700 + + bbbbbbbbbbbbbbbbbb: bbbbbbbb bbb bbbb bbbbbbb bb bbbb bbb bbbbb bbbbbb + +EOF + test_cmp expect out +' + iconvfromutf8toiso88591() { printf "%s" "$*" | iconv -f UTF-8 -t ISO8859-1 } @@ -148,4 +172,20 @@ test_expect_success 'shortlog encoding' ' git shortlog HEAD~2.. > out && test_cmp expect out' +test_expect_success 'shortlog ignores commits with missing authors' ' + git commit --allow-empty -m normal && + git commit --allow-empty -m soon-to-be-broken && + git cat-file commit HEAD >commit.tmp && + sed "/^author/d" commit.tmp >broken.tmp && + commit=$(git hash-object -w -t commit --stdin <broken.tmp) && + git update-ref HEAD $commit && + cat >expect <<-\EOF && + A U Thor (1): + normal + + EOF + git shortlog HEAD~2.. >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 222f7559e9..cb03d28769 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -72,9 +72,9 @@ cat > expect << EOF commit. EOF -test_expect_success 'format %w(12,1,2)' ' +test_expect_success 'format %w(11,1,2)' ' - git log -2 --format="%w(12,1,2)This is the %s commit." > actual && + git log -2 --format="%w(11,1,2)This is the %s commit." > actual && test_cmp expect actual ' @@ -178,11 +178,21 @@ test_expect_success 'git log --no-walk <commits> sorts by commit time' ' test_cmp expect actual ' +test_expect_success 'git log --no-walk=sorted <commits> sorts by commit time' ' + git log --no-walk=sorted --oneline 5d31159 804a787 394ef78 > actual && + test_cmp expect actual +' + cat > expect << EOF 5d31159 fourth 804a787 sixth 394ef78 fifth EOF +test_expect_success 'git log --no-walk=unsorted <commits> leaves list of commits as given' ' + git log --no-walk=unsorted --oneline 5d31159 804a787 394ef78 > actual && + test_cmp expect actual +' + test_expect_success 'git show <commits> leaves list of commits as given' ' git show --oneline -s 5d31159 804a787 394ef78 > actual && test_cmp expect actual @@ -220,6 +230,12 @@ test_expect_success 'log --grep -i' ' test_cmp expect actual ' +test_expect_success 'log -F -E --grep=<ere> uses ere' ' + echo second >expect && + git log -1 --pretty="tformat:%s" -F -E --grep=s.c.nd >actual && + test_cmp expect actual +' + cat > expect <<EOF * Second * sixth @@ -264,6 +280,16 @@ test_expect_success 'log --graph with merge' ' test_cmp expect actual ' +test_expect_success 'log --raw --graph -m with merge' ' + git log --raw --graph --oneline -m master | head -n 500 >actual && + grep "initial" actual +' + +test_expect_success 'diff-tree --graph' ' + git diff-tree --graph master^ | head -n 500 >actual && + grep "one" actual +' + cat > expect <<\EOF * commit master |\ Merge: A B @@ -393,8 +419,6 @@ test_expect_success 'log --graph with merge' ' ' test_expect_success 'log.decorate configuration' ' - test_might_fail git config --unset-all log.decorate && - git log --oneline >expect.none && git log --oneline --decorate >expect.short && git log --oneline --decorate=full >expect.full && @@ -403,8 +427,7 @@ test_expect_success 'log.decorate configuration' ' git log --oneline >actual && test_cmp expect.short actual && - git config --unset-all log.decorate && - git config log.decorate true && + test_config log.decorate true && git log --oneline >actual && test_cmp expect.short actual && git log --oneline --decorate=full >actual && @@ -412,8 +435,7 @@ test_expect_success 'log.decorate configuration' ' git log --oneline --decorate=no >actual && test_cmp expect.none actual && - git config --unset-all log.decorate && - git config log.decorate no && + test_config log.decorate no && git log --oneline >actual && test_cmp expect.none actual && git log --oneline --decorate >actual && @@ -421,8 +443,7 @@ test_expect_success 'log.decorate configuration' ' git log --oneline --decorate=full >actual && test_cmp expect.full actual && - git config --unset-all log.decorate && - git config log.decorate 1 && + test_config log.decorate 1 && git log --oneline >actual && test_cmp expect.short actual && git log --oneline --decorate=full >actual && @@ -430,8 +451,7 @@ test_expect_success 'log.decorate configuration' ' git log --oneline --decorate=no >actual && test_cmp expect.none actual && - git config --unset-all log.decorate && - git config log.decorate short && + test_config log.decorate short && git log --oneline >actual && test_cmp expect.short actual && git log --oneline --no-decorate >actual && @@ -439,8 +459,7 @@ test_expect_success 'log.decorate configuration' ' git log --oneline --decorate=full >actual && test_cmp expect.full actual && - git config --unset-all log.decorate && - git config log.decorate full && + test_config log.decorate full && git log --oneline >actual && test_cmp expect.full actual && git log --oneline --no-decorate >actual && @@ -448,16 +467,15 @@ test_expect_success 'log.decorate configuration' ' git log --oneline --decorate >actual && test_cmp expect.short actual - git config --unset-all log.decorate && + test_unconfig log.decorate && git log --pretty=raw >expect.raw && - git config log.decorate full && + test_config log.decorate full && git log --pretty=raw >actual && test_cmp expect.raw actual ' test_expect_success 'reflog is expected format' ' - test_might_fail git config --remove-section log && git log -g --abbrev-commit --pretty=oneline >expect && git reflog >actual && test_cmp expect actual @@ -470,10 +488,6 @@ test_expect_success 'whatchanged is expected format' ' ' test_expect_success 'log.abbrevCommit configuration' ' - test_when_finished "git config --unset log.abbrevCommit" && - - test_might_fail git config --unset log.abbrevCommit && - git log --abbrev-commit >expect.log.abbrev && git log --no-abbrev-commit >expect.log.full && git log --pretty=raw >expect.log.raw && @@ -482,7 +496,7 @@ test_expect_success 'log.abbrevCommit configuration' ' git whatchanged --abbrev-commit >expect.whatchanged.abbrev && git whatchanged --no-abbrev-commit >expect.whatchanged.full && - git config log.abbrevCommit true && + test_config log.abbrevCommit true && git log >actual && test_cmp expect.log.abbrev actual && @@ -516,4 +530,315 @@ test_expect_success 'show added path under "--follow -M"' ' ) ' +test_expect_success 'git log -c --follow' ' + test_create_repo follow-c && + ( + cd follow-c && + test_commit initial file original && + git rm file && + test_commit rename file2 original && + git reset --hard initial && + test_commit modify file foo && + git merge -m merge rename && + git log -c --follow file2 + ) +' + +cat >expect <<\EOF +* commit COMMIT_OBJECT_NAME +|\ Merge: MERGE_PARENTS +| | Author: A U Thor <author@example.com> +| | +| | Merge HEADS DESCRIPTION +| | +| * commit COMMIT_OBJECT_NAME +| | Author: A U Thor <author@example.com> +| | +| | reach +| | --- +| | reach.t | 1 + +| | 1 file changed, 1 insertion(+) +| | +| | diff --git a/reach.t b/reach.t +| | new file mode 100644 +| | index 0000000..10c9591 +| | --- /dev/null +| | +++ b/reach.t +| | @@ -0,0 +1 @@ +| | +reach +| | +| \ +*-. \ commit COMMIT_OBJECT_NAME +|\ \ \ Merge: MERGE_PARENTS +| | | | Author: A U Thor <author@example.com> +| | | | +| | | | Merge HEADS DESCRIPTION +| | | | +| | * | commit COMMIT_OBJECT_NAME +| | |/ Author: A U Thor <author@example.com> +| | | +| | | octopus-b +| | | --- +| | | octopus-b.t | 1 + +| | | 1 file changed, 1 insertion(+) +| | | +| | | diff --git a/octopus-b.t b/octopus-b.t +| | | new file mode 100644 +| | | index 0000000..d5fcad0 +| | | --- /dev/null +| | | +++ b/octopus-b.t +| | | @@ -0,0 +1 @@ +| | | +octopus-b +| | | +| * | commit COMMIT_OBJECT_NAME +| |/ Author: A U Thor <author@example.com> +| | +| | octopus-a +| | --- +| | octopus-a.t | 1 + +| | 1 file changed, 1 insertion(+) +| | +| | diff --git a/octopus-a.t b/octopus-a.t +| | new file mode 100644 +| | index 0000000..11ee015 +| | --- /dev/null +| | +++ b/octopus-a.t +| | @@ -0,0 +1 @@ +| | +octopus-a +| | +* | commit COMMIT_OBJECT_NAME +|/ Author: A U Thor <author@example.com> +| +| seventh +| --- +| seventh.t | 1 + +| 1 file changed, 1 insertion(+) +| +| diff --git a/seventh.t b/seventh.t +| new file mode 100644 +| index 0000000..9744ffc +| --- /dev/null +| +++ b/seventh.t +| @@ -0,0 +1 @@ +| +seventh +| +* commit COMMIT_OBJECT_NAME +|\ Merge: MERGE_PARENTS +| | Author: A U Thor <author@example.com> +| | +| | Merge branch 'tangle' +| | +| * commit COMMIT_OBJECT_NAME +| |\ Merge: MERGE_PARENTS +| | | Author: A U Thor <author@example.com> +| | | +| | | Merge branch 'side' (early part) into tangle +| | | +| * | commit COMMIT_OBJECT_NAME +| |\ \ Merge: MERGE_PARENTS +| | | | Author: A U Thor <author@example.com> +| | | | +| | | | Merge branch 'master' (early part) into tangle +| | | | +| * | | commit COMMIT_OBJECT_NAME +| | | | Author: A U Thor <author@example.com> +| | | | +| | | | tangle-a +| | | | --- +| | | | tangle-a | 1 + +| | | | 1 file changed, 1 insertion(+) +| | | | +| | | | diff --git a/tangle-a b/tangle-a +| | | | new file mode 100644 +| | | | index 0000000..7898192 +| | | | --- /dev/null +| | | | +++ b/tangle-a +| | | | @@ -0,0 +1 @@ +| | | | +a +| | | | +* | | | commit COMMIT_OBJECT_NAME +|\ \ \ \ Merge: MERGE_PARENTS +| | | | | Author: A U Thor <author@example.com> +| | | | | +| | | | | Merge branch 'side' +| | | | | +| * | | | commit COMMIT_OBJECT_NAME +| | |_|/ Author: A U Thor <author@example.com> +| |/| | +| | | | side-2 +| | | | --- +| | | | 2 | 1 + +| | | | 1 file changed, 1 insertion(+) +| | | | +| | | | diff --git a/2 b/2 +| | | | new file mode 100644 +| | | | index 0000000..0cfbf08 +| | | | --- /dev/null +| | | | +++ b/2 +| | | | @@ -0,0 +1 @@ +| | | | +2 +| | | | +| * | | commit COMMIT_OBJECT_NAME +| | | | Author: A U Thor <author@example.com> +| | | | +| | | | side-1 +| | | | --- +| | | | 1 | 1 + +| | | | 1 file changed, 1 insertion(+) +| | | | +| | | | diff --git a/1 b/1 +| | | | new file mode 100644 +| | | | index 0000000..d00491f +| | | | --- /dev/null +| | | | +++ b/1 +| | | | @@ -0,0 +1 @@ +| | | | +1 +| | | | +* | | | commit COMMIT_OBJECT_NAME +| | | | Author: A U Thor <author@example.com> +| | | | +| | | | Second +| | | | --- +| | | | one | 1 + +| | | | 1 file changed, 1 insertion(+) +| | | | +| | | | diff --git a/one b/one +| | | | new file mode 100644 +| | | | index 0000000..9a33383 +| | | | --- /dev/null +| | | | +++ b/one +| | | | @@ -0,0 +1 @@ +| | | | +case +| | | | +* | | | commit COMMIT_OBJECT_NAME +| |_|/ Author: A U Thor <author@example.com> +|/| | +| | | sixth +| | | --- +| | | a/two | 1 - +| | | 1 file changed, 1 deletion(-) +| | | +| | | diff --git a/a/two b/a/two +| | | deleted file mode 100644 +| | | index 9245af5..0000000 +| | | --- a/a/two +| | | +++ /dev/null +| | | @@ -1 +0,0 @@ +| | | -ni +| | | +* | | commit COMMIT_OBJECT_NAME +| | | Author: A U Thor <author@example.com> +| | | +| | | fifth +| | | --- +| | | a/two | 1 + +| | | 1 file changed, 1 insertion(+) +| | | +| | | diff --git a/a/two b/a/two +| | | new file mode 100644 +| | | index 0000000..9245af5 +| | | --- /dev/null +| | | +++ b/a/two +| | | @@ -0,0 +1 @@ +| | | +ni +| | | +* | | commit COMMIT_OBJECT_NAME +|/ / Author: A U Thor <author@example.com> +| | +| | fourth +| | --- +| | ein | 1 + +| | 1 file changed, 1 insertion(+) +| | +| | diff --git a/ein b/ein +| | new file mode 100644 +| | index 0000000..9d7e69f +| | --- /dev/null +| | +++ b/ein +| | @@ -0,0 +1 @@ +| | +ichi +| | +* | commit COMMIT_OBJECT_NAME +|/ Author: A U Thor <author@example.com> +| +| third +| --- +| ichi | 1 + +| one | 1 - +| 2 files changed, 1 insertion(+), 1 deletion(-) +| +| diff --git a/ichi b/ichi +| new file mode 100644 +| index 0000000..9d7e69f +| --- /dev/null +| +++ b/ichi +| @@ -0,0 +1 @@ +| +ichi +| diff --git a/one b/one +| deleted file mode 100644 +| index 9d7e69f..0000000 +| --- a/one +| +++ /dev/null +| @@ -1 +0,0 @@ +| -ichi +| +* commit COMMIT_OBJECT_NAME +| Author: A U Thor <author@example.com> +| +| second +| --- +| one | 2 +- +| 1 file changed, 1 insertion(+), 1 deletion(-) +| +| diff --git a/one b/one +| index 5626abf..9d7e69f 100644 +| --- a/one +| +++ b/one +| @@ -1 +1 @@ +| -one +| +ichi +| +* commit COMMIT_OBJECT_NAME + Author: A U Thor <author@example.com> + + initial + --- + one | 1 + + 1 file changed, 1 insertion(+) + + diff --git a/one b/one + new file mode 100644 + index 0000000..5626abf + --- /dev/null + +++ b/one + @@ -0,0 +1 @@ + +one +EOF + +sanitize_output () { + sed -e 's/ *$//' \ + -e 's/commit [0-9a-f]*$/commit COMMIT_OBJECT_NAME/' \ + -e 's/Merge: [ 0-9a-f]*$/Merge: MERGE_PARENTS/' \ + -e 's/Merge tag.*/Merge HEADS DESCRIPTION/' \ + -e 's/Merge commit.*/Merge HEADS DESCRIPTION/' \ + -e 's/, 0 deletions(-)//' \ + -e 's/, 0 insertions(+)//' \ + -e 's/ 1 files changed, / 1 file changed, /' \ + -e 's/, 1 deletions(-)/, 1 deletion(-)/' \ + -e 's/, 1 insertions(+)/, 1 insertion(+)/' +} + +test_expect_success 'log --graph with diff and stats' ' + git log --graph --pretty=short --stat -p >actual && + sanitize_output >actual.sanitized <actual && + test_i18ncmp expect actual.sanitized +' + +test_expect_success 'dotdot is a parent directory' ' + mkdir -p a/b && + ( echo sixth && echo fifth ) >expect && + ( cd a/b && git log --format=%s .. ) >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh index 1f182f612c..0dd8b65d7c 100755 --- a/t/t4203-mailmap.sh +++ b/t/t4203-mailmap.sh @@ -13,6 +13,11 @@ fuzz_blame () { } test_expect_success setup ' + cat >contacts <<-\EOF && + A U Thor <author@example.com> + nick1 <bugs@company.xx> + EOF + echo one >one && git add one && test_tick && @@ -23,6 +28,44 @@ test_expect_success setup ' git commit --author "nick1 <bugs@company.xx>" -m second ' +test_expect_success 'check-mailmap no arguments' ' + test_must_fail git check-mailmap +' + +test_expect_success 'check-mailmap arguments' ' + cat >expect <<-\EOF && + A U Thor <author@example.com> + nick1 <bugs@company.xx> + EOF + git check-mailmap \ + "A U Thor <author@example.com>" \ + "nick1 <bugs@company.xx>" >actual && + test_cmp expect actual +' + +test_expect_success 'check-mailmap --stdin' ' + cat >expect <<-\EOF && + A U Thor <author@example.com> + nick1 <bugs@company.xx> + EOF + git check-mailmap --stdin <contacts >actual && + test_cmp expect actual +' + +test_expect_success 'check-mailmap --stdin arguments' ' + cat >expect <<-\EOF && + Internal Guy <bugs@company.xy> + EOF + cat <contacts >>expect && + git check-mailmap --stdin "Internal Guy <bugs@company.xy>" \ + <contacts >actual && + test_cmp expect actual +' + +test_expect_success 'check-mailmap bogus contact' ' + test_must_fail git check-mailmap bogus +' + cat >expect <<\EOF A U Thor (1): initial @@ -149,6 +192,136 @@ test_expect_success 'No mailmap files, but configured' ' test_cmp expect actual ' +test_expect_success 'setup mailmap blob tests' ' + git checkout -b map && + test_when_finished "git checkout master" && + cat >just-bugs <<-\EOF && + Blob Guy <bugs@company.xx> + EOF + cat >both <<-\EOF && + Blob Guy <author@example.com> + Blob Guy <bugs@company.xx> + EOF + printf "Tricky Guy <author@example.com>" >no-newline && + git add just-bugs both no-newline && + git commit -m "my mailmaps" && + echo "Repo Guy <author@example.com>" >.mailmap && + echo "Internal Guy <author@example.com>" >internal.map +' + +test_expect_success 'mailmap.blob set' ' + cat >expect <<-\EOF && + Blob Guy (1): + second + + Repo Guy (1): + initial + + EOF + git -c mailmap.blob=map:just-bugs shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob overrides .mailmap' ' + cat >expect <<-\EOF && + Blob Guy (2): + initial + second + + EOF + git -c mailmap.blob=map:both shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.file overrides mailmap.blob' ' + cat >expect <<-\EOF && + Blob Guy (1): + second + + Internal Guy (1): + initial + + EOF + git \ + -c mailmap.blob=map:both \ + -c mailmap.file=internal.map \ + shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob can be missing' ' + cat >expect <<-\EOF && + Repo Guy (1): + initial + + nick1 (1): + second + + EOF + git -c mailmap.blob=map:nonexistent shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'mailmap.blob defaults to off in non-bare repo' ' + git init non-bare && + ( + cd non-bare && + test_commit one .mailmap "Fake Name <author@example.com>" && + echo " 1 Fake Name" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual && + rm .mailmap && + echo " 1 A U Thor" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual + ) +' + +test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' ' + git clone --bare non-bare bare && + ( + cd bare && + echo " 1 Fake Name" >expect && + git shortlog -ns HEAD >actual && + test_cmp expect actual + ) +' + +test_expect_success 'mailmap.blob can handle blobs without trailing newline' ' + cat >expect <<-\EOF && + Tricky Guy (1): + initial + + nick1 (1): + second + + EOF + git -c mailmap.blob=map:no-newline shortlog HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'cleanup after mailmap.blob tests' ' + rm -f .mailmap +' + +test_expect_success 'single-character name' ' + echo " 1 A <author@example.com>" >expect && + echo " 1 nick1 <bugs@company.xx>" >>expect && + echo "A <author@example.com>" >.mailmap && + test_when_finished "rm .mailmap" && + git shortlog -es HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'preserve canonical email case' ' + echo " 1 A U Thor <AUTHOR@example.com>" >expect && + echo " 1 nick1 <bugs@company.xx>" >>expect && + echo "<AUTHOR@example.com> <author@example.com>" >.mailmap && + test_when_finished "rm .mailmap" && + git shortlog -es HEAD >actual && + test_cmp expect actual +' + # Extended mailmap configurations should give us the following output for shortlog cat >expect <<\EOF A U Thor <author@example.com> (1): @@ -239,6 +412,62 @@ test_expect_success 'Log output (complex mapping)' ' test_cmp expect actual ' +cat >expect <<\EOF +Author: CTO <cto@company.xx> +Author: Santa Claus <santa.claus@northpole.xx> +Author: Santa Claus <santa.claus@northpole.xx> +Author: Other Author <other@author.xx> +Author: Other Author <other@author.xx> +Author: Some Dude <some@dude.xx> +Author: A U Thor <author@example.com> +EOF + +test_expect_success 'Log output with --use-mailmap' ' + git log --use-mailmap | grep Author >actual && + test_cmp expect actual +' + +cat >expect <<\EOF +Author: CTO <cto@company.xx> +Author: Santa Claus <santa.claus@northpole.xx> +Author: Santa Claus <santa.claus@northpole.xx> +Author: Other Author <other@author.xx> +Author: Other Author <other@author.xx> +Author: Some Dude <some@dude.xx> +Author: A U Thor <author@example.com> +EOF + +test_expect_success 'Log output with log.mailmap' ' + git -c log.mailmap=True log | grep Author >actual && + test_cmp expect actual +' + +cat >expect <<\EOF +Author: Santa Claus <santa.claus@northpole.xx> +Author: Santa Claus <santa.claus@northpole.xx> +EOF + +test_expect_success 'Grep author with --use-mailmap' ' + git log --use-mailmap --author Santa | grep Author >actual && + test_cmp expect actual +' +cat >expect <<\EOF +Author: Santa Claus <santa.claus@northpole.xx> +Author: Santa Claus <santa.claus@northpole.xx> +EOF + +test_expect_success 'Grep author with log.mailmap' ' + git -c log.mailmap=True log --author Santa | grep Author >actual && + test_cmp expect actual +' + +>expect + +test_expect_success 'Only grep replaced author with --use-mailmap' ' + git log --use-mailmap --author "<cto@coompany.xx>" >actual && + test_cmp expect actual +' + # git blame cat >expect <<\EOF ^OBJI (A U Thor DATE 1) one @@ -255,4 +484,15 @@ test_expect_success 'Blame output (complex mapping)' ' test_cmp expect actual.fuzz ' +cat >expect <<\EOF +Some Dude <some@dude.xx> +EOF + +test_expect_success 'commit --author honors mailmap' ' + test_must_fail git commit --author "nick" --allow-empty -meight && + git commit --author "Some Dude" --allow-empty -meight && + git show --pretty=format:"%an <%ae>%n" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index 2ae9faa8b3..fb00041139 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -1,20 +1,38 @@ #!/bin/sh # # Copyright (c) 2010, Will Palmer +# Copyright (c) 2011, Alexey Shumkin (+ non-UTF-8 commit encoding tests) # test_description='Test pretty formats' . ./test-lib.sh +sample_utf8_part=$(printf "f\303\244ng") + +commit_msg () { + # String "initial. initial" partly in German + # (translated with Google Translate), + # encoded in UTF-8, used as a commit log message below. + msg="initial. an${sample_utf8_part}lich\n" + if test -n "$1" + then + printf "$msg" | iconv -f utf-8 -t "$1" + else + printf "$msg" + fi +} + test_expect_success 'set up basic repos' ' >foo && >bar && git add foo && test_tick && - git commit -m initial && + git config i18n.commitEncoding iso8859-1 && + git commit -m "$(commit_msg iso8859-1)" && git add bar && test_tick && - git commit -m "add bar" + git commit -m "add bar" && + git config --unset i18n.commitEncoding ' test_expect_success 'alias builtin format' ' @@ -38,6 +56,20 @@ test_expect_success 'alias user-defined format' ' test_cmp expected actual ' +test_expect_success 'alias user-defined tformat with %s (iso8859-1 encoding)' ' + git config i18n.logOutputEncoding iso8859-1 && + git log --oneline >expected-s && + git log --pretty="tformat:%h %s" >actual-s && + git config --unset i18n.logOutputEncoding && + test_cmp expected-s actual-s +' + +test_expect_success 'alias user-defined tformat with %s (utf-8 encoding)' ' + git log --oneline >expected-s && + git log --pretty="tformat:%h %s" >actual-s && + test_cmp expected-s actual-s +' + test_expect_success 'alias user-defined tformat' ' git log --pretty="tformat:%h" >expected && git config pretty.test-alias "tformat:%h" && @@ -71,4 +103,211 @@ test_expect_success 'alias loop' ' test_must_fail git log --pretty=test-foo ' +test_expect_success 'NUL separation' ' + printf "add bar\0$(commit_msg)" >expected && + git log -z --pretty="format:%s" >actual && + test_cmp expected actual +' + +test_expect_success 'NUL termination' ' + printf "add bar\0$(commit_msg)\0" >expected && + git log -z --pretty="tformat:%s" >actual && + test_cmp expected actual +' + +test_expect_success 'NUL separation with --stat' ' + stat0_part=$(git diff --stat HEAD^ HEAD) && + stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) && + printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n" >expected && + git log -z --stat --pretty="format:%s" >actual && + test_i18ncmp expected actual +' + +test_expect_failure 'NUL termination with --stat' ' + stat0_part=$(git diff --stat HEAD^ HEAD) && + stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) && + printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n0" >expected && + git log -z --stat --pretty="tformat:%s" >actual && + test_i18ncmp expected actual +' + +test_expect_success 'setup more commits' ' + test_commit "message one" one one message-one && + test_commit "message two" two two message-two && + head1=$(git rev-parse --verify --short HEAD~0) && + head2=$(git rev-parse --verify --short HEAD~1) && + head3=$(git rev-parse --verify --short HEAD~2) && + head4=$(git rev-parse --verify --short HEAD~3) +' + +test_expect_success 'left alignment formatting' ' + git log --pretty="format:%<(40)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + qz_to_tab_space <<EOF >expected && +message two Z +message one Z +add bar Z +$(commit_msg) Z +EOF + test_cmp expected actual +' + +test_expect_success 'left alignment formatting at the nth column' ' + git log --pretty="format:%h %<|(40)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + qz_to_tab_space <<EOF >expected && +$head1 message two Z +$head2 message one Z +$head3 add bar Z +$head4 $(commit_msg) Z +EOF + test_cmp expected actual +' + +test_expect_success 'left alignment formatting with no padding' ' + git log --pretty="format:%<(1)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + cat <<EOF >expected && +message two +message one +add bar +$(commit_msg) +EOF + test_cmp expected actual +' + +test_expect_success 'left alignment formatting with trunc' ' + git log --pretty="format:%<(10,trunc)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + qz_to_tab_space <<EOF >expected && +message .. +message .. +add bar Z +initial... +EOF + test_cmp expected actual +' + +test_expect_success 'left alignment formatting with ltrunc' ' + git log --pretty="format:%<(10,ltrunc)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + qz_to_tab_space <<EOF >expected && +..sage two +..sage one +add bar Z +..${sample_utf8_part}lich +EOF + test_cmp expected actual +' + +test_expect_success 'left alignment formatting with mtrunc' ' + git log --pretty="format:%<(10,mtrunc)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + qz_to_tab_space <<EOF >expected && +mess.. two +mess.. one +add bar Z +init..lich +EOF + test_cmp expected actual +' + +test_expect_success 'right alignment formatting' ' + git log --pretty="format:%>(40)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + qz_to_tab_space <<EOF >expected && +Z message two +Z message one +Z add bar +Z $(commit_msg) +EOF + test_cmp expected actual +' + +test_expect_success 'right alignment formatting at the nth column' ' + git log --pretty="format:%h %>|(40)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + qz_to_tab_space <<EOF >expected && +$head1 message two +$head2 message one +$head3 add bar +$head4 $(commit_msg) +EOF + test_cmp expected actual +' + +test_expect_success 'right alignment formatting with no padding' ' + git log --pretty="format:%>(1)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + cat <<EOF >expected && +message two +message one +add bar +$(commit_msg) +EOF + test_cmp expected actual +' + +test_expect_success 'center alignment formatting' ' + git log --pretty="format:%><(40)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + qz_to_tab_space <<EOF >expected && +Z message two Z +Z message one Z +Z add bar Z +Z $(commit_msg) Z +EOF + test_cmp expected actual +' + +test_expect_success 'center alignment formatting at the nth column' ' + git log --pretty="format:%h %><|(40)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + qz_to_tab_space <<EOF >expected && +$head1 message two Z +$head2 message one Z +$head3 add bar Z +$head4 $(commit_msg) Z +EOF + test_cmp expected actual +' + +test_expect_success 'center alignment formatting with no padding' ' + git log --pretty="format:%><(1)%s" >actual && + # complete the incomplete line at the end + echo >>actual && + cat <<EOF >expected && +message two +message one +add bar +$(commit_msg) +EOF + test_cmp expected actual +' + +test_expect_success 'left/right alignment formatting with stealing' ' + git commit --amend -m short --author "long long long <long@me.com>" && + git log --pretty="format:%<(10,trunc)%s%>>(10,ltrunc)% an" >actual && + # complete the incomplete line at the end + echo >>actual && + cat <<EOF >expected && +short long long long +message .. A U Thor +add bar A U Thor +initial... A U Thor +EOF + test_cmp expected actual +' + test_done diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh index bbde31b019..925f577a3c 100755 --- a/t/t4207-log-decoration-colors.sh +++ b/t/t4207-log-decoration-colors.sh @@ -44,15 +44,15 @@ test_expect_success setup ' ' cat >expected <<EOF -${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\ +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_HEAD}HEAD${c_reset}${c_commit},\ ${c_tag}tag: v1.0${c_reset}${c_commit},\ ${c_tag}tag: B${c_reset}${c_commit},\ ${c_branch}master${c_reset}${c_commit})${c_reset} B -${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\ +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A1${c_reset}${c_commit},\ ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1 -${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\ +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\ On master: Changes to A.t -${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A +${c_commit}COMMIT_ID${c_reset}${c_commit} (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A EOF # We want log to show all, but the second parent to refs/stash is irrelevant diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh index 2c482b622b..d8f23f488e 100755 --- a/t/t4208-log-magic-pathspec.sh +++ b/t/t4208-log-magic-pathspec.sh @@ -11,11 +11,24 @@ test_expect_success 'setup' ' mkdir sub ' -test_expect_success '"git log :/" should be ambiguous' ' - test_must_fail git log :/ 2>error && +test_expect_success '"git log :/" should not be ambiguous' ' + git log :/ +' + +test_expect_success '"git log :/a" should be ambiguous (applied both rev and worktree)' ' + : >a && + test_must_fail git log :/a 2>error && grep ambiguous error ' +test_expect_success '"git log :/a -- " should not be ambiguous' ' + git log :/a -- +' + +test_expect_success '"git log -- :/a" should not be ambiguous' ' + git log -- :/a +' + test_expect_success '"git log :" should be ambiguous' ' test_must_fail git log : 2>error && grep ambiguous error @@ -33,4 +46,19 @@ test_expect_success 'git log HEAD -- :/' ' test_cmp expected actual ' +test_expect_success 'command line pathspec parsing for "git log"' ' + git reset --hard && + >a && + git add a && + git commit -m "add an empty a" --allow-empty && + echo 1 >a && + git commit -a -m "update a to 1" && + git checkout HEAD^ && + echo 2 >a && + git commit -a -m "update a to 2" && + test_must_fail git merge master && + git add a && + git log --merge -- a +' + test_done diff --git a/t/t4209-log-pickaxe.sh b/t/t4209-log-pickaxe.sh new file mode 100755 index 0000000000..38fb80f643 --- /dev/null +++ b/t/t4209-log-pickaxe.sh @@ -0,0 +1,147 @@ +#!/bin/sh + +test_description='log --grep/--author/--regexp-ignore-case/-S/-G' +. ./test-lib.sh + +test_expect_success setup ' + >file && + git add file && + test_tick && + git commit -m initial && + + echo Picked >file && + test_tick && + git commit -a --author="Another Person <another@example.com>" -m second +' + +test_expect_success 'log --grep' ' + git log --grep=initial --format=%H >actual && + git rev-parse --verify HEAD^ >expect && + test_cmp expect actual +' + +test_expect_success 'log --grep --regexp-ignore-case' ' + git log --regexp-ignore-case --grep=InItial --format=%H >actual && + git rev-parse --verify HEAD^ >expect && + test_cmp expect actual +' + +test_expect_success 'log --grep -i' ' + git log -i --grep=InItial --format=%H >actual && + git rev-parse --verify HEAD^ >expect && + test_cmp expect actual +' + +test_expect_success 'log --author --regexp-ignore-case' ' + git log --regexp-ignore-case --author=person --format=%H >actual && + git rev-parse --verify HEAD >expect && + test_cmp expect actual +' + +test_expect_success 'log --author -i' ' + git log -i --author=person --format=%H >actual && + git rev-parse --verify HEAD >expect && + test_cmp expect actual +' + +test_expect_success 'log -G (nomatch)' ' + git log -Gpicked --format=%H >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'log -G (match)' ' + git log -GPicked --format=%H >actual && + git rev-parse --verify HEAD >expect && + test_cmp expect actual +' + +test_expect_success 'log -G --regexp-ignore-case (nomatch)' ' + git log --regexp-ignore-case -Gpickle --format=%H >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'log -G -i (nomatch)' ' + git log -i -Gpickle --format=%H >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'log -G --regexp-ignore-case (match)' ' + git log --regexp-ignore-case -Gpicked --format=%H >actual && + git rev-parse --verify HEAD >expect && + test_cmp expect actual +' + +test_expect_success 'log -G -i (match)' ' + git log -i -Gpicked --format=%H >actual && + git rev-parse --verify HEAD >expect && + test_cmp expect actual +' + +test_expect_success 'log -G --textconv (missing textconv tool)' ' + echo "* diff=test" >.gitattributes && + test_must_fail git -c diff.test.textconv=missing log -Gfoo && + rm .gitattributes +' + +test_expect_success 'log -G --no-textconv (missing textconv tool)' ' + echo "* diff=test" >.gitattributes && + git -c diff.test.textconv=missing log -Gfoo --no-textconv >actual && + >expect && + test_cmp expect actual && + rm .gitattributes +' + +test_expect_success 'log -S (nomatch)' ' + git log -Spicked --format=%H >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'log -S (match)' ' + git log -SPicked --format=%H >actual && + git rev-parse --verify HEAD >expect && + test_cmp expect actual +' + +test_expect_success 'log -S --regexp-ignore-case (match)' ' + git log --regexp-ignore-case -Spicked --format=%H >actual && + git rev-parse --verify HEAD >expect && + test_cmp expect actual +' + +test_expect_success 'log -S -i (match)' ' + git log -i -Spicked --format=%H >actual && + git rev-parse --verify HEAD >expect && + test_cmp expect actual +' + +test_expect_success 'log -S --regexp-ignore-case (nomatch)' ' + git log --regexp-ignore-case -Spickle --format=%H >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'log -S -i (nomatch)' ' + git log -i -Spickle --format=%H >actual && + >expect && + test_cmp expect actual +' + +test_expect_success 'log -S --textconv (missing textconv tool)' ' + echo "* diff=test" >.gitattributes && + test_must_fail git -c diff.test.textconv=missing log -Sfoo && + rm .gitattributes +' + +test_expect_success 'log -S --no-textconv (missing textconv tool)' ' + echo "* diff=test" >.gitattributes && + git -c diff.test.textconv=missing log -Sfoo --no-textconv >actual && + >expect && + test_cmp expect actual && + rm .gitattributes +' + +test_done diff --git a/t/t4210-log-i18n.sh b/t/t4210-log-i18n.sh new file mode 100755 index 0000000000..52a74729ba --- /dev/null +++ b/t/t4210-log-i18n.sh @@ -0,0 +1,58 @@ +#!/bin/sh + +test_description='test log with i18n features' +. ./test-lib.sh + +# two forms of é +utf8_e=$(printf '\303\251') +latin1_e=$(printf '\351') + +test_expect_success 'create commits in different encodings' ' + test_tick && + cat >msg <<-EOF && + utf8 + + t${utf8_e}st + EOF + git add msg && + git -c i18n.commitencoding=utf8 commit -F msg && + cat >msg <<-EOF && + latin1 + + t${latin1_e}st + EOF + git add msg && + git -c i18n.commitencoding=ISO-8859-1 commit -F msg +' + +test_expect_success 'log --grep searches in log output encoding (utf8)' ' + cat >expect <<-\EOF && + latin1 + utf8 + EOF + git log --encoding=utf8 --format=%s --grep=$utf8_e >actual && + test_cmp expect actual +' + +test_expect_success 'log --grep searches in log output encoding (latin1)' ' + cat >expect <<-\EOF && + latin1 + utf8 + EOF + git log --encoding=ISO-8859-1 --format=%s --grep=$latin1_e >actual && + test_cmp expect actual +' + +test_expect_success 'log --grep does not find non-reencoded values (utf8)' ' + >expect && + git log --encoding=utf8 --format=%s --grep=$latin1_e >actual && + test_cmp expect actual +' + +test_expect_success 'log --grep does not find non-reencoded values (latin1)' ' + >expect && + git log --encoding=ISO-8859-1 --format=%s --grep=$utf8_e >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh new file mode 100755 index 0000000000..7369d3c517 --- /dev/null +++ b/t/t4211-line-log.sh @@ -0,0 +1,97 @@ +#!/bin/sh + +test_description='test log -L' +. ./test-lib.sh + +test_expect_success 'setup (import history)' ' + git fast-import < "$TEST_DIRECTORY"/t4211/history.export && + git reset --hard +' + +canned_test_1 () { + test_expect_$1 "$2" " + git log $2 >actual && + test_cmp \"\$TEST_DIRECTORY\"/t4211/expect.$3 actual + " +} + +canned_test () { + canned_test_1 success "$@" +} +canned_test_failure () { + canned_test_1 failure "$@" +} + +test_bad_opts () { + test_expect_success "invalid args: $1" " + test_must_fail git log $1 2>errors && + grep '$2' errors + " +} + +canned_test "-L 4,12:a.c simple" simple-f +canned_test "-L 4,+9:a.c simple" simple-f +canned_test "-L '/long f/,/^}/:a.c' simple" simple-f +canned_test "-L :f:a.c simple" simple-f-to-main + +canned_test "-L '/main/,/^}/:a.c' simple" simple-main +canned_test "-L :main:a.c simple" simple-main-to-end + +canned_test "-L 1,+4:a.c simple" beginning-of-file + +canned_test "-L 20:a.c simple" end-of-file + +canned_test "-L '/long f/',/^}/:a.c -L /main/,/^}/:a.c simple" two-ranges +canned_test "-L 24,+1:a.c simple" vanishes-early + +canned_test "-M -L '/long f/,/^}/:b.c' move-support" move-support-f +canned_test "-M -L ':f:b.c' parallel-change" parallel-change-f-to-main + +canned_test "-L 4,12:a.c -L :main:a.c simple" multiple +canned_test "-L 4,18:a.c -L ^:main:a.c simple" multiple-overlapping +canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping +canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset +canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset + +test_bad_opts "-L" "switch.*requires a value" +test_bad_opts "-L b.c" "argument.*not of the form" +test_bad_opts "-L 1:" "argument.*not of the form" +test_bad_opts "-L 1:nonexistent" "There is no path" +test_bad_opts "-L 1:simple" "There is no path" +test_bad_opts "-L '/foo:b.c'" "argument.*not of the form" +test_bad_opts "-L 1000:b.c" "has only.*lines" +test_bad_opts "-L 1,1000:b.c" "has only.*lines" +test_bad_opts "-L :b.c" "argument.*not of the form" +test_bad_opts "-L :foo:b.c" "no match" + +test_expect_success '-L X (X == nlines)' ' + n=$(wc -l <b.c) && + git log -L $n:b.c +' + +test_expect_success '-L X (X == nlines + 1)' ' + n=$(expr $(wc -l <b.c) + 1) && + test_must_fail git log -L $n:b.c +' + +test_expect_success '-L X (X == nlines + 2)' ' + n=$(expr $(wc -l <b.c) + 2) && + test_must_fail git log -L $n:b.c +' + +test_expect_success '-L ,Y (Y == nlines)' ' + n=$(printf "%d" $(wc -l <b.c)) && + git log -L ,$n:b.c +' + +test_expect_success '-L ,Y (Y == nlines + 1)' ' + n=$(expr $(wc -l <b.c) + 1) && + test_must_fail git log -L ,$n:b.c +' + +test_expect_success '-L ,Y (Y == nlines + 2)' ' + n=$(expr $(wc -l <b.c) + 2) && + test_must_fail git log -L ,$n:b.c +' + +test_done diff --git a/t/t4211/expect.beginning-of-file b/t/t4211/expect.beginning-of-file new file mode 100644 index 0000000000..91b4054898 --- /dev/null +++ b/t/t4211/expect.beginning-of-file @@ -0,0 +1,43 @@ +commit 4a23ae5c98d59a58c6da036156959f2dc9f472ad +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:47:40 2013 +0100 + + change at very beginning + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -1,3 +1,4 @@ ++#include <unistd.h> + #include <stdio.h> + + long f(long x) + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -1,3 +1,3 @@ + #include <stdio.h> + +-int f(int x) ++long f(long x) + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +1,3 @@ ++#include <stdio.h> ++ ++int f(int x) diff --git a/t/t4211/expect.end-of-file b/t/t4211/expect.end-of-file new file mode 100644 index 0000000000..bd25bb2f59 --- /dev/null +++ b/t/t4211/expect.end-of-file @@ -0,0 +1,62 @@ +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -20,3 +20,5 @@ + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} ++ ++/* incomplete lines are bad! */ + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -20,3 +20,3 @@ + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -19,3 +19,3 @@ +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +18,3 @@ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/expect.move-support-f b/t/t4211/expect.move-support-f new file mode 100644 index 0000000000..c905e01bc2 --- /dev/null +++ b/t/t4211/expect.move-support-f @@ -0,0 +1,80 @@ +commit 6ce3c4ff690136099bb17e1a8766b75764726ea7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:49:50 2013 +0100 + + another simple change + +diff --git a/b.c b/b.c +--- a/b.c ++++ b/b.c +@@ -4,9 +4,9 @@ + long f(long x) + { + int s = 0; + while (x) { +- x >>= 1; ++ x /= 2; + s++; + } + return s; + } + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,9 +3,9 @@ +-int f(int x) ++long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + +commit f04fb20f2c77850996cba739709acc6faecc58f7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:55 2013 +0100 + + change f() + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,8 +3,9 @@ + int f(int x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } ++ return s; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +3,8 @@ ++int f(int x) ++{ ++ int s = 0; ++ while (x) { ++ x >>= 1; ++ s++; ++ } ++} diff --git a/t/t4211/expect.multiple b/t/t4211/expect.multiple new file mode 100644 index 0000000000..76ad5b598c --- /dev/null +++ b/t/t4211/expect.multiple @@ -0,0 +1,104 @@ +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,7 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} ++ ++/* incomplete lines are bad! */ + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,5 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,9 +3,9 @@ +-int f(int x) ++long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } +@@ -17,5 +17,5 @@ + int main () + { +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } + +commit f04fb20f2c77850996cba739709acc6faecc58f7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:55 2013 +0100 + + change f() + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,8 +3,9 @@ + int f(int x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } ++ return s; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +3,8 @@ ++int f(int x) ++{ ++ int s = 0; ++ while (x) { ++ x >>= 1; ++ s++; ++ } ++} diff --git a/t/t4211/expect.multiple-overlapping b/t/t4211/expect.multiple-overlapping new file mode 100644 index 0000000000..d930b6eec4 --- /dev/null +++ b/t/t4211/expect.multiple-overlapping @@ -0,0 +1,187 @@ +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -4,19 +4,21 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * This is only an example! + */ + + int main () + { + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} ++ ++/* incomplete lines are bad! */ + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -4,19 +4,19 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * This is only an example! + */ + + int main () + { + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:41 2013 +0100 + + touch comment + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,19 +3,19 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* +- * A comment. ++ * This is only an example! + */ + + int main () + { + printf("%ld\n", f(15)); + return 0; + } + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,19 +3,19 @@ +-int f(int x) ++long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * A comment. + */ + + int main () + { +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } + +commit f04fb20f2c77850996cba739709acc6faecc58f7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:55 2013 +0100 + + change f() + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,18 +3,19 @@ + int f(int x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } ++ return s; + } + + /* + * A comment. + */ + + int main () + { + printf("%d\n", f(15)); + return 0; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +3,18 @@ ++int f(int x) ++{ ++ int s = 0; ++ while (x) { ++ x >>= 1; ++ s++; ++ } ++} ++ ++/* ++ * A comment. ++ */ ++ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/expect.multiple-superset b/t/t4211/expect.multiple-superset new file mode 100644 index 0000000000..d930b6eec4 --- /dev/null +++ b/t/t4211/expect.multiple-superset @@ -0,0 +1,187 @@ +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -4,19 +4,21 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * This is only an example! + */ + + int main () + { + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} ++ ++/* incomplete lines are bad! */ + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -4,19 +4,19 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * This is only an example! + */ + + int main () + { + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:41 2013 +0100 + + touch comment + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,19 +3,19 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* +- * A comment. ++ * This is only an example! + */ + + int main () + { + printf("%ld\n", f(15)); + return 0; + } + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,19 +3,19 @@ +-int f(int x) ++long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * A comment. + */ + + int main () + { +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } + +commit f04fb20f2c77850996cba739709acc6faecc58f7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:55 2013 +0100 + + change f() + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,18 +3,19 @@ + int f(int x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } ++ return s; + } + + /* + * A comment. + */ + + int main () + { + printf("%d\n", f(15)); + return 0; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +3,18 @@ ++int f(int x) ++{ ++ int s = 0; ++ while (x) { ++ x >>= 1; ++ s++; ++ } ++} ++ ++/* ++ * A comment. ++ */ ++ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/expect.parallel-change-f-to-main b/t/t4211/expect.parallel-change-f-to-main new file mode 100644 index 0000000000..052def8074 --- /dev/null +++ b/t/t4211/expect.parallel-change-f-to-main @@ -0,0 +1,160 @@ +commit 0469c60bc4837d52d97b1f081dec5f98dea20fed +Merge: ba227c6 6ce3c4f +Author: Thomas Rast <trast@inf.ethz.ch> +Date: Fri Apr 12 16:16:24 2013 +0200 + + Merge across the rename + + +commit 6ce3c4ff690136099bb17e1a8766b75764726ea7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:49:50 2013 +0100 + + another simple change + +diff --git a/b.c b/b.c +--- a/b.c ++++ b/b.c +@@ -4,14 +4,14 @@ + long f(long x) + { + int s = 0; + while (x) { +- x >>= 1; ++ x /= 2; + s++; + } + return s; + } + + /* + * This is only an example! + */ + + +commit ba227c6632349700fbb957dec2b50f5e2358be3f +Author: Thomas Rast <trast@inf.ethz.ch> +Date: Fri Apr 12 16:15:57 2013 +0200 + + change on another line of history while rename happens + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -4,14 +4,14 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* +- * This is only an example! ++ * This is only a short example! + */ + + +commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:41 2013 +0100 + + touch comment + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,14 +3,14 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* +- * A comment. ++ * This is only an example! + */ + + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,14 +3,14 @@ +-int f(int x) ++long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * A comment. + */ + + +commit f04fb20f2c77850996cba739709acc6faecc58f7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:55 2013 +0100 + + change f() + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,13 +3,14 @@ + int f(int x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } ++ return s; + } + + /* + * A comment. + */ + + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +3,13 @@ ++int f(int x) ++{ ++ int s = 0; ++ while (x) { ++ x >>= 1; ++ s++; ++ } ++} ++ ++/* ++ * A comment. ++ */ ++ diff --git a/t/t4211/expect.simple-f b/t/t4211/expect.simple-f new file mode 100644 index 0000000000..a1f5bc49c8 --- /dev/null +++ b/t/t4211/expect.simple-f @@ -0,0 +1,59 @@ +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,9 +3,9 @@ +-int f(int x) ++long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + +commit f04fb20f2c77850996cba739709acc6faecc58f7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:55 2013 +0100 + + change f() + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,8 +3,9 @@ + int f(int x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } ++ return s; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +3,8 @@ ++int f(int x) ++{ ++ int s = 0; ++ while (x) { ++ x >>= 1; ++ s++; ++ } ++} diff --git a/t/t4211/expect.simple-f-to-main b/t/t4211/expect.simple-f-to-main new file mode 100644 index 0000000000..a475768710 --- /dev/null +++ b/t/t4211/expect.simple-f-to-main @@ -0,0 +1,100 @@ +commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:41 2013 +0100 + + touch comment + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,14 +3,14 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* +- * A comment. ++ * This is only an example! + */ + + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,14 +3,14 @@ +-int f(int x) ++long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * A comment. + */ + + +commit f04fb20f2c77850996cba739709acc6faecc58f7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:55 2013 +0100 + + change f() + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,13 +3,14 @@ + int f(int x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } ++ return s; + } + + /* + * A comment. + */ + + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +3,13 @@ ++int f(int x) ++{ ++ int s = 0; ++ while (x) { ++ x >>= 1; ++ s++; ++ } ++} ++ ++/* ++ * A comment. ++ */ ++ diff --git a/t/t4211/expect.simple-main b/t/t4211/expect.simple-main new file mode 100644 index 0000000000..39ce39bebe --- /dev/null +++ b/t/t4211/expect.simple-main @@ -0,0 +1,68 @@ +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,5 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,5 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -17,5 +17,5 @@ + int main () + { +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +16,5 @@ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/expect.simple-main-to-end b/t/t4211/expect.simple-main-to-end new file mode 100644 index 0000000000..8480bd9cc4 --- /dev/null +++ b/t/t4211/expect.simple-main-to-end @@ -0,0 +1,70 @@ +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,7 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} ++ ++/* incomplete lines are bad! */ + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,5 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -17,5 +17,5 @@ + int main () + { +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +16,5 @@ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t4211/expect.two-ranges b/t/t4211/expect.two-ranges new file mode 100644 index 0000000000..6109aa0dce --- /dev/null +++ b/t/t4211/expect.two-ranges @@ -0,0 +1,102 @@ +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,5 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -18,5 +18,5 @@ + int main () + { + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:16 2013 +0100 + + touch both functions + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,9 +3,9 @@ +-int f(int x) ++long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } +@@ -17,5 +17,5 @@ + int main () + { +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } + +commit f04fb20f2c77850996cba739709acc6faecc58f7 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:55 2013 +0100 + + change f() + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,8 +3,9 @@ + int f(int x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } ++ return s; + } + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +3,8 @@ ++int f(int x) ++{ ++ int s = 0; ++ while (x) { ++ x >>= 1; ++ s++; ++ } ++} diff --git a/t/t4211/expect.vanishes-early b/t/t4211/expect.vanishes-early new file mode 100644 index 0000000000..1f7cd06941 --- /dev/null +++ b/t/t4211/expect.vanishes-early @@ -0,0 +1,39 @@ +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -22,1 +24,1 @@ +-} +\ No newline at end of file ++/* incomplete lines are bad! */ + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -22,1 +22,1 @@ +-} ++} +\ No newline at end of file + +commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:44:48 2013 +0100 + + initial + +diff --git a/a.c b/a.c +--- /dev/null ++++ b/a.c +@@ -0,0 +20,1 @@ ++} diff --git a/t/t4211/history.export b/t/t4211/history.export new file mode 100644 index 0000000000..f9f41e211e --- /dev/null +++ b/t/t4211/history.export @@ -0,0 +1,406 @@ +blob +mark :1 +data 157 +#include <stdio.h> + +int f(int x) +{ + int s = 0; + while (x) { + x >>= 1; + s++; + } +} + +/* + * A comment. + */ + +int main () +{ + printf("%d\n", f(15)); + return 0; +} + +reset refs/tags/simple +commit refs/tags/simple +mark :2 +author Thomas Rast <trast@student.ethz.ch> 1362044688 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362044688 +0100 +data 8 +initial +M 100644 :1 a.c + +blob +mark :3 +data 168 +#include <stdio.h> + +int f(int x) +{ + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; +} + +/* + * A comment. + */ + +int main () +{ + printf("%d\n", f(15)); + return 0; +} + +commit refs/tags/simple +mark :4 +author Thomas Rast <trast@student.ethz.ch> 1362044695 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362044695 +0100 +data 11 +change f() +from :2 +M 100644 :3 a.c + +blob +mark :5 +data 171 +#include <stdio.h> + +long f(long x) +{ + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; +} + +/* + * A comment. + */ + +int main () +{ + printf("%ld\n", f(15)); + return 0; +} + +commit refs/tags/simple +mark :6 +author Thomas Rast <trast@student.ethz.ch> 1362044716 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362044716 +0100 +data 21 +touch both functions +from :4 +M 100644 :5 a.c + +blob +mark :7 +data 185 +#include <stdio.h> + +long f(long x) +{ + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; +} + +/* + * This is only an example! + */ + +int main () +{ + printf("%ld\n", f(15)); + return 0; +} + +commit refs/tags/simple +mark :8 +author Thomas Rast <trast@student.ethz.ch> 1362044741 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362044741 +0100 +data 14 +touch comment +from :6 +M 100644 :7 a.c + +blob +mark :9 +data 205 +#include <unistd.h> +#include <stdio.h> + +long f(long x) +{ + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; +} + +/* + * This is only an example! + */ + +int main () +{ + printf("%ld\n", f(15)); + return 0; +} + +commit refs/tags/simple +mark :10 +author Thomas Rast <trast@student.ethz.ch> 1362044860 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362044860 +0100 +data 25 +change at very beginning +from :8 +M 100644 :9 a.c + +blob +mark :11 +data 204 +#include <unistd.h> +#include <stdio.h> + +long f(long x) +{ + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; +} + +/* + * This is only an example! + */ + +int main () +{ + printf("%ld\n", f(15)); + return 0; +} +commit refs/tags/simple +mark :12 +author Thomas Rast <trast@student.ethz.ch> 1362044890 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362044890 +0100 +data 36 +change to an incomplete line at end +from :10 +M 100644 :11 a.c + +blob +mark :13 +data 238 +#include <unistd.h> +#include <stdio.h> + +long f(long x) +{ + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; +} + +/* + * This is only an example! + */ + +int main () +{ + printf("%ld\n", f(15)); + return 0; +} + +/* incomplete lines are bad! */ + +commit refs/tags/simple +mark :14 +author Thomas Rast <trast@student.ethz.ch> 1362044923 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362044923 +0100 +data 29 +change back to complete line +from :12 +M 100644 :13 a.c + +commit refs/tags/move-support +mark :15 +author Thomas Rast <trast@student.ethz.ch> 1362044968 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362044968 +0100 +data 10 +move file +from :14 +D a.c +M 100644 :13 b.c + +blob +mark :16 +data 237 +#include <unistd.h> +#include <stdio.h> + +long f(long x) +{ + int s = 0; + while (x) { + x /= 2; + s++; + } + return s; +} + +/* + * This is only an example! + */ + +int main () +{ + printf("%ld\n", f(15)); + return 0; +} + +/* incomplete lines are bad! */ + +commit refs/tags/move-support +mark :17 +author Thomas Rast <trast@student.ethz.ch> 1362044990 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362044990 +0100 +data 22 +another simple change +from :15 +M 100644 :16 b.c + +blob +mark :18 +data 254 +#include <unistd.h> +#include <stdio.h> + +long f(long x); + +/* + * This is only an example! + */ + +int main () +{ + printf("%ld\n", f(15)); + return 0; +} + +/* incomplete lines are bad! */ + +long f(long x) +{ + int s = 0; + while (x) { + x /= 2; + s++; + } + return s; +} + +commit refs/heads/master +mark :19 +author Thomas Rast <trast@student.ethz.ch> 1362045024 +0100 +committer Thomas Rast <trast@student.ethz.ch> 1362045024 +0100 +data 21 +move within the file +from :17 +M 100644 :18 b.c + +blob +mark :20 +data 243 +#include <unistd.h> +#include <stdio.h> + +long f(long x) +{ + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; +} + +/* + * This is only a short example! + */ + +int main () +{ + printf("%ld\n", f(15)); + return 0; +} + +/* incomplete lines are bad! */ + +commit refs/heads/parallel-change +mark :21 +author Thomas Rast <trast@inf.ethz.ch> 1365776157 +0200 +committer Thomas Rast <trast@inf.ethz.ch> 1365776157 +0200 +data 55 +change on another line of history while rename happens +from :14 +M 100644 :20 a.c + +blob +mark :22 +data 242 +#include <unistd.h> +#include <stdio.h> + +long f(long x) +{ + int s = 0; + while (x) { + x /= 2; + s++; + } + return s; +} + +/* + * This is only a short example! + */ + +int main () +{ + printf("%ld\n", f(15)); + return 0; +} + +/* incomplete lines are bad! */ + +commit refs/heads/parallel-change +mark :23 +author Thomas Rast <trast@inf.ethz.ch> 1365776184 +0200 +committer Thomas Rast <trast@inf.ethz.ch> 1365776191 +0200 +data 24 +Merge across the rename +from :21 +merge :17 +D a.c +M 100644 :22 b.c + +reset refs/heads/parallel-change +from :23 + diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh new file mode 100755 index 0000000000..93c7c366cf --- /dev/null +++ b/t/t4212-log-corrupt.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +test_description='git log with invalid commit headers' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit foo && + + git cat-file commit HEAD | + sed "/^author /s/>/>-<>/" >broken_email.commit && + git hash-object -w -t commit broken_email.commit >broken_email.hash && + git update-ref refs/heads/broken_email $(cat broken_email.hash) +' + +test_expect_success 'fsck notices broken commit' ' + git fsck 2>actual && + test_i18ngrep invalid.author actual +' + +test_expect_success 'git log with broken author email' ' + { + echo commit $(cat broken_email.hash) + echo "Author: A U Thor <author@example.com>" + echo "Date: Thu Apr 7 15:13:13 2005 -0700" + echo + echo " foo" + } >expect.out && + : >expect.err && + + git log broken_email >actual.out 2>actual.err && + + test_cmp expect.out actual.out && + test_cmp expect.err actual.err +' + +test_expect_success 'git log --format with broken author email' ' + echo "A U Thor+author@example.com+Thu Apr 7 15:13:13 2005 -0700" >expect.out && + : >expect.err && + + git log --format="%an+%ae+%ad" broken_email >actual.out 2>actual.err && + + test_cmp expect.out actual.out && + test_cmp expect.err actual.err +' + +test_done diff --git a/t/t4253-am-keep-cr-dos.sh b/t/t4253-am-keep-cr-dos.sh index 735e55d77c..553fe3e88e 100755 --- a/t/t4253-am-keep-cr-dos.sh +++ b/t/t4253-am-keep-cr-dos.sh @@ -62,7 +62,7 @@ test_expect_success 'am with dos files config am.keepcr' ' git diff --exit-code master ' -test_expect_success 'am with dos files config am.keepcr overriden by --no-keep-cr' ' +test_expect_success 'am with dos files config am.keepcr overridden by --no-keep-cr' ' git config am.keepcr 1 && git checkout -b dosfiles-conf-keepcr-override initial && git format-patch -k initial..master && @@ -83,7 +83,7 @@ test_expect_success 'am with dos files with --keep-cr continue' ' git diff --exit-code master ' -test_expect_success 'am with unix files config am.keepcr overriden by --no-keep-cr' ' +test_expect_success 'am with unix files config am.keepcr overridden by --no-keep-cr' ' git config am.keepcr 1 && git checkout -b unixfiles-conf-keepcr-override initial && cp -f file1 file && diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh index b7da95fac5..85716dd6ec 100755 --- a/t/t4254-am-corrupt.sh +++ b/t/t4254-am-corrupt.sh @@ -3,20 +3,19 @@ test_description='git am with corrupt input' . ./test-lib.sh -# Note the missing "+++" line: -cat > bad-patch.diff <<'EOF' -From: A U Thor <au.thor@example.com> -diff --git a/f b/f -index 7898192..6178079 100644 ---- a/f -@@ -1 +1 @@ --a -+b -EOF - test_expect_success setup ' - test $? = 0 && - echo a > f && + # Note the missing "+++" line: + cat >bad-patch.diff <<-\EOF && + From: A U Thor <au.thor@example.com> + diff --git a/f b/f + index 7898192..6178079 100644 + --- a/f + @@ -1 +1 @@ + -a + +b + EOF + + echo a >f && git add f && test_tick && git commit -m initial @@ -26,17 +25,12 @@ test_expect_success setup ' # fatal: unable to write file '(null)' mode 100644: Bad address # Also, it had the unwanted side-effect of deleting f. test_expect_success 'try to apply corrupted patch' ' - git am bad-patch.diff 2> actual - test $? = 1 + test_must_fail git am bad-patch.diff 2>actual ' -cat > expected <<EOF -fatal: git diff header lacks filename information (line 4) -EOF - test_expect_success 'compare diagnostic; ensure file is still here' ' - test $? = 0 && - test -f f && + echo "fatal: git diff header lacks filename information (line 4)" >expected && + test_path_is_file f && test_cmp expected actual ' diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh index 46c3fe76d3..9015e47654 100755 --- a/t/t4300-merge-tree.sh +++ b/t/t4300-merge-tree.sh @@ -26,8 +26,6 @@ EXPECTED test_expect_success 'file add !A, B' ' cat >expected <<\EXPECTED && -added in local - our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE EXPECTED git reset --hard initial && @@ -38,9 +36,6 @@ EXPECTED test_expect_success 'file add A, B (same)' ' cat >expected <<\EXPECTED && -added in both - our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE - their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE EXPECTED git reset --hard initial && @@ -181,9 +176,6 @@ AAA" && test_expect_success 'file remove A, !B' ' cat >expected <<\EXPECTED && -removed in local - base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE - their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE EXPECTED git reset --hard initial && @@ -213,6 +205,19 @@ EXPECTED test_cmp expected actual ' +test_expect_success 'file remove A, B (same)' ' + cat >expected <<\EXPECTED && +EXPECTED + + git reset --hard initial && + test_commit "rm-a-b-base" "ONE" "AAA" && + git rm ONE && + git commit -m "rm-a-b" && + git tag "rm-a-b" && + git merge-tree rm-a-b-base rm-a-b rm-a-b >actual && + test_cmp expected actual +' + test_expect_success 'file change A, remove B' ' cat >expected <<\EXPECTED && removed in remote @@ -254,4 +259,97 @@ EXPECTED test_cmp expected actual ' +test_expect_success 'tree add A, B (same)' ' + cat >expect <<-\EOF && + EOF + git reset --hard initial && + mkdir sub && + test_commit "add sub/file" "sub/file" "file" add-tree-A && + git merge-tree initial add-tree-A add-tree-A >actual && + test_cmp expect actual +' + +test_expect_success 'tree add A, B (different)' ' + cat >expect <<-\EOF && + added in both + our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file + their 100644 ba629238ca89489f2b350e196ca445e09d8bb834 sub/file + @@ -1 +1,5 @@ + +<<<<<<< .our + AAA + +======= + +BBB + +>>>>>>> .their + EOF + git reset --hard initial && + mkdir sub && + test_commit "add sub/file" "sub/file" "AAA" add-tree-a-b-A && + git reset --hard initial && + mkdir sub && + test_commit "add sub/file" "sub/file" "BBB" add-tree-a-b-B && + git merge-tree initial add-tree-a-b-A add-tree-a-b-B >actual && + test_cmp expect actual +' + +test_expect_success 'tree unchanged A, removed B' ' + cat >expect <<-\EOF && + removed in remote + base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file + our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file + @@ -1 +0,0 @@ + -AAA + EOF + git reset --hard initial && + mkdir sub && + test_commit "add sub/file" "sub/file" "AAA" tree-remove-b-initial && + git rm sub/file && + test_tick && + git commit -m "remove sub/file" && + git tag tree-remove-b-B && + git merge-tree tree-remove-b-initial tree-remove-b-initial tree-remove-b-B >actual && + test_cmp expect actual +' + +test_expect_success 'turn file to tree' ' + git reset --hard initial && + rm initial-file && + mkdir initial-file && + test_commit "turn-file-to-tree" "initial-file/ONE" "CCC" && + git merge-tree initial initial turn-file-to-tree >actual && + cat >expect <<-\EOF && + added in remote + their 100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 initial-file/ONE + @@ -0,0 +1 @@ + +CCC + removed in remote + base 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file + our 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file + @@ -1 +0,0 @@ + -initial + EOF + test_cmp expect actual +' + +test_expect_success 'turn tree to file' ' + git reset --hard initial && + mkdir dir && + test_commit "add-tree" "dir/path" "AAA" && + test_commit "add-another-tree" "dir/another" "BBB" && + rm -fr dir && + test_commit "make-file" "dir" "CCC" && + git merge-tree add-tree add-another-tree make-file >actual && + cat >expect <<-\EOF && + removed in remote + base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path + our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path + @@ -1 +0,0 @@ + -AAA + added in remote + their 100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 dir + @@ -0,0 +1 @@ + +CCC + EOF + test_cmp expect actual +' + test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 527c9e7548..c2023b1a3d 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -25,16 +25,81 @@ commit id embedding: ' . ./test-lib.sh -UNZIP=${UNZIP:-unzip} GZIP=${GZIP:-gzip} GUNZIP=${GUNZIP:-gzip -d} SUBSTFORMAT=%H%n +test_lazy_prereq TAR_NEEDS_PAX_FALLBACK ' + ( + mkdir pax && + cd pax && + "$TAR" xf "$TEST_DIRECTORY"/t5000/pax.tar && + test -f PaxHeaders.1791/file + ) +' + +get_pax_header() { + file=$1 + header=$2= + + while read len rest + do + if test "$len" = $(echo "$len $rest" | wc -c) + then + case "$rest" in + $header*) + echo "${rest#$header}" + ;; + esac + fi + done <"$file" +} + +check_tar() { + tarfile=$1.tar + listfile=$1.lst + dir=$1 + dir_with_prefix=$dir/$2 + + test_expect_success ' extract tar archive' ' + (mkdir $dir && cd $dir && "$TAR" xf -) <$tarfile + ' + + test_expect_success TAR_NEEDS_PAX_FALLBACK ' interpret pax headers' ' + ( + cd $dir && + for header in *.paxheader + do + data=${header%.paxheader}.data && + if test -h $data -o -e $data + then + path=$(get_pax_header $header path) && + if test -n "$path" + then + mv "$data" "$path" + fi + fi + done + ) + ' + + test_expect_success ' validate filenames' ' + (cd ${dir_with_prefix}a && find .) | sort >$listfile && + test_cmp a.lst $listfile + ' + + test_expect_success ' validate file contents' ' + diff -r a ${dir_with_prefix}a + ' +} + test_expect_success \ 'populate workdir' \ - 'mkdir a b c && + 'mkdir a && echo simple textfile >a/a && + ten=0123456789 && hundred=$ten$ten$ten$ten$ten$ten$ten$ten$ten$ten && + echo long filename >a/four$hundred && mkdir a/bin && cp /bin/sh a/bin && printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 && @@ -63,6 +128,12 @@ test_expect_success \ git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \ git commit-tree $treeid </dev/null)' +test_expect_success 'setup export-subst' ' + echo "substfile?" export-subst >>.git/info/attributes && + git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ + >a/substfile1 +' + test_expect_success \ 'create bare clone' \ 'git clone --bare . bare.git && @@ -76,13 +147,25 @@ test_expect_success \ 'git archive' \ 'git archive HEAD >b.tar' -test_expect_success \ - 'git tar-tree' \ - 'git tar-tree HEAD >b2.tar' +check_tar b -test_expect_success \ - 'git archive vs. git tar-tree' \ - 'test_cmp b.tar b2.tar' +test_expect_success 'git archive --prefix=prefix/' ' + git archive --prefix=prefix/ HEAD >with_prefix.tar +' + +check_tar with_prefix prefix/ + +test_expect_success 'git-archive --prefix=olde-' ' + git archive --prefix=olde- HEAD >with_olde-prefix.tar +' + +check_tar with_olde-prefix olde- + +test_expect_success 'git archive on large files' ' + test_config core.bigfilethreshold 1 && + git archive HEAD >b3.tar && + test_cmp b.tar b3.tar +' test_expect_success \ 'git archive in a bare repo' \ @@ -113,87 +196,14 @@ test_expect_success \ 'git get-tar-commit-id <b.tar >b.commitid && test_cmp .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 && - test_cmp 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 && - test_cmp a.lst c.lst' - -test_expect_success \ - 'validate file contents with prefix' \ - 'diff -r a c/prefix/a' - -test_expect_success \ - 'create archives with substfiles' \ - 'cp .git/info/attributes .git/info/attributes.before && - echo "substfile?" export-subst >>.git/info/attributes && - git archive HEAD >f.tar && - git archive --prefix=prefix/ HEAD >g.tar && - mv .git/info/attributes.before .git/info/attributes' - -test_expect_success \ - 'extract substfiles' \ - '(mkdir f && cd f && "$TAR" xf -) <f.tar' - -test_expect_success \ - 'validate substfile contents' \ - 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ - >f/a/substfile1.expected && - test_cmp f/a/substfile1.expected f/a/substfile1 && - test_cmp a/substfile2 f/a/substfile2 -' - -test_expect_success \ - 'extract substfiles from archive with prefix' \ - '(mkdir g && cd g && "$TAR" xf -) <g.tar' - -test_expect_success \ - 'validate substfile contents from archive with prefix' \ - 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ - >g/prefix/a/substfile1.expected && - test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 && - test_cmp a/substfile2 g/prefix/a/substfile2 +test_expect_success 'git tar-tree' ' + git tar-tree HEAD >tar-tree.tar && + test_cmp b.tar tar-tree.tar ' -test_expect_success \ - 'git archive --format=zip' \ - 'git archive --format=zip HEAD >d.zip' - -test_expect_success \ - 'git archive --format=zip in a bare repo' \ - '(cd bare.git && git archive --format=zip HEAD) >d1.zip' - -test_expect_success \ - 'git archive --format=zip vs. the same in a bare repo' \ - 'test_cmp d.zip d1.zip' - -test_expect_success 'git archive --format=zip with --output' \ - 'git archive --format=zip --output=d2.zip HEAD && - test_cmp d.zip d2.zip' - -test_expect_success 'git archive with --output, inferring format' ' - git archive --output=d3.zip HEAD && - test_cmp d.zip d3.zip +test_expect_success 'git tar-tree with prefix' ' + git tar-tree HEAD prefix >tar-tree_with_prefix.tar && + test_cmp with_prefix.tar tar-tree_with_prefix.tar ' test_expect_success 'git archive with --output, override inferred format' ' @@ -201,43 +211,6 @@ test_expect_success 'git archive with --output, override inferred format' ' test_cmp b.tar d4.zip ' -$UNZIP -v >/dev/null 2>&1 -if [ $? -eq 127 ]; then - say "Skipping ZIP tests, because unzip was not found" -else - test_set_prereq UNZIP -fi - -test_expect_success UNZIP \ - 'extract ZIP archive' \ - '(mkdir d && cd d && $UNZIP ../d.zip)' - -test_expect_success UNZIP \ - 'validate filenames' \ - '(cd d/a && find .) | sort >d.lst && - test_cmp a.lst d.lst' - -test_expect_success UNZIP \ - 'validate file contents' \ - 'diff -r a d/a' - -test_expect_success \ - 'git archive --format=zip with prefix' \ - 'git archive --format=zip --prefix=prefix/ HEAD >e.zip' - -test_expect_success UNZIP \ - 'extract ZIP archive with prefix' \ - '(mkdir e && cd e && $UNZIP ../e.zip)' - -test_expect_success UNZIP \ - 'validate filenames with prefix' \ - '(cd e/prefix/a && find .) | sort >e.lst && - test_cmp a.lst e.lst' - -test_expect_success UNZIP \ - 'validate file contents with prefix' \ - 'diff -r a e/prefix/a' - test_expect_success \ 'git archive --list outside of a git repo' \ 'GIT_DIR=some/non-existing/directory git archive --list' @@ -250,22 +223,11 @@ test_expect_success 'clients cannot access unreachable commits' ' test_must_fail git archive --remote=. $sha1 >remote.tar ' -test_expect_success 'git-archive --prefix=olde-' ' - git archive --prefix=olde- >h.tar HEAD && - ( - mkdir h && - cd h && - "$TAR" xf - <../h.tar - ) && - test -d h/olde-a && - test -d h/olde-a/bin && - test -f h/olde-a/bin/sh -' - test_expect_success 'setup tar filters' ' git config tar.tar.foo.command "tr ab ba" && git config tar.bar.command "tr ab ba" && - git config tar.bar.remote true + git config tar.bar.remote true && + git config tar.invalid baz ' test_expect_success 'archive --list mentions user filter' ' diff --git a/t/t5000/pax.tar b/t/t5000/pax.tar Binary files differnew file mode 100644 index 0000000000..d911737149 --- /dev/null +++ b/t/t5000/pax.tar diff --git a/t/t5002-archive-attr-pattern.sh b/t/t5002-archive-attr-pattern.sh new file mode 100755 index 0000000000..6667d159ab --- /dev/null +++ b/t/t5002-archive-attr-pattern.sh @@ -0,0 +1,84 @@ +#!/bin/sh + +test_description='git archive attribute pattern tests' + +. ./test-lib.sh + +test_expect_exists() { + test_expect_success " $1 exists" "test -e $1" +} + +test_expect_missing() { + test_expect_success " $1 does not exist" "test ! -e $1" +} + +test_expect_success 'setup' ' + echo ignored >ignored && + echo ignored export-ignore >>.git/info/attributes && + git add ignored && + + mkdir not-ignored-dir && + echo ignored-in-tree >not-ignored-dir/ignored && + echo not-ignored-in-tree >not-ignored-dir/ignored-only-if-dir && + git add not-ignored-dir && + + mkdir ignored-only-if-dir && + echo ignored by ignored dir >ignored-only-if-dir/ignored-by-ignored-dir && + echo ignored-only-if-dir/ export-ignore >>.git/info/attributes && + git add ignored-only-if-dir && + + mkdir -p ignored-without-slash && + echo "ignored without slash" >ignored-without-slash/foo && + git add ignored-without-slash/foo && + echo "ignored-without-slash export-ignore" >>.git/info/attributes && + + mkdir -p wildcard-without-slash && + echo "ignored without slash" >wildcard-without-slash/foo && + git add wildcard-without-slash/foo && + echo "wild*-without-slash export-ignore" >>.git/info/attributes && + + mkdir -p deep/and/slashless && + echo "ignored without slash" >deep/and/slashless/foo && + git add deep/and/slashless/foo && + echo "deep/and/slashless export-ignore" >>.git/info/attributes && + + mkdir -p deep/with/wildcard && + echo "ignored without slash" >deep/with/wildcard/foo && + git add deep/with/wildcard/foo && + echo "deep/*t*/wildcard export-ignore" >>.git/info/attributes && + + mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir && + echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir && + git add one-level-lower && + + git commit -m. && + + git clone --bare . bare && + cp .git/info/attributes bare/info/attributes +' + +test_expect_success 'git archive' ' + git archive HEAD >archive.tar && + (mkdir archive && cd archive && "$TAR" xf -) <archive.tar +' + +test_expect_missing archive/ignored +test_expect_missing archive/not-ignored-dir/ignored +test_expect_exists archive/not-ignored-dir/ignored-only-if-dir +test_expect_exists archive/not-ignored-dir/ +test_expect_missing archive/ignored-only-if-dir/ +test_expect_missing archive/ignored-ony-if-dir/ignored-by-ignored-dir +test_expect_missing archive/ignored-without-slash/ && +test_expect_missing archive/ignored-without-slash/foo && +test_expect_missing archive/wildcard-without-slash/ +test_expect_missing archive/wildcard-without-slash/foo && +test_expect_missing archive/deep/and/slashless/ && +test_expect_missing archive/deep/and/slashless/foo && +test_expect_missing archive/deep/with/wildcard/ && +test_expect_missing archive/deep/with/wildcard/foo && +test_expect_exists archive/one-level-lower/ +test_expect_missing archive/one-level-lower/two-levels-lower/ignored-only-if-dir/ +test_expect_missing archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir + + +test_done diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh new file mode 100755 index 0000000000..c72f71eb18 --- /dev/null +++ b/t/t5003-archive-zip.sh @@ -0,0 +1,131 @@ +#!/bin/sh + +test_description='git archive --format=zip test' + +. ./test-lib.sh + +SUBSTFORMAT=%H%n + +test_lazy_prereq UNZIP_SYMLINKS ' + ( + mkdir unzip-symlinks && + cd unzip-symlinks && + "$GIT_UNZIP" "$TEST_DIRECTORY"/t5003/infozip-symlinks.zip && + test -h symlink + ) +' + +check_zip() { + zipfile=$1.zip + listfile=$1.lst + dir=$1 + dir_with_prefix=$dir/$2 + + test_expect_success UNZIP " extract ZIP archive" ' + (mkdir $dir && cd $dir && "$GIT_UNZIP" ../$zipfile) + ' + + test_expect_success UNZIP " validate filenames" " + (cd ${dir_with_prefix}a && find .) | sort >$listfile && + test_cmp a.lst $listfile + " + + test_expect_success UNZIP " validate file contents" " + diff -r a ${dir_with_prefix}a + " +} + +test_expect_success \ + 'populate workdir' \ + 'mkdir a && + echo simple textfile >a/a && + mkdir a/bin && + cp /bin/sh a/bin && + printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 && + printf "A not substituted O" >a/substfile2 && + (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) +' + +test_expect_success SYMLINKS,UNZIP_SYMLINKS 'add symlink' ' + ln -s a a/symlink_to_a +' + +test_expect_success 'prepare file list' ' + (cd a && find .) | sort >a.lst +' + +test_expect_success \ + 'add ignored file' \ + 'echo ignore me >a/ignored && + echo ignored export-ignore >.git/info/attributes' + +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 'setup export-subst' ' + echo "substfile?" export-subst >>.git/info/attributes && + git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \ + >a/substfile1 +' + +test_expect_success \ + 'create bare clone' \ + 'git clone --bare . bare.git && + cp .git/info/attributes bare.git/info/attributes' + +test_expect_success \ + 'remove ignored file' \ + 'rm a/ignored' + +test_expect_success \ + 'git archive --format=zip' \ + 'git archive --format=zip HEAD >d.zip' + +check_zip d + +test_expect_success \ + 'git archive --format=zip in a bare repo' \ + '(cd bare.git && git archive --format=zip HEAD) >d1.zip' + +test_expect_success \ + 'git archive --format=zip vs. the same in a bare repo' \ + 'test_cmp d.zip d1.zip' + +test_expect_success 'git archive --format=zip with --output' \ + 'git archive --format=zip --output=d2.zip HEAD && + test_cmp d.zip d2.zip' + +test_expect_success 'git archive with --output, inferring format' ' + git archive --output=d3.zip HEAD && + test_cmp d.zip d3.zip +' + +test_expect_success \ + 'git archive --format=zip with prefix' \ + 'git archive --format=zip --prefix=prefix/ HEAD >e.zip' + +check_zip e prefix/ + +test_expect_success 'git archive -0 --format=zip on large files' ' + test_config core.bigfilethreshold 1 && + git archive -0 --format=zip HEAD >large.zip +' + +check_zip large + +test_expect_success 'git archive --format=zip on large files' ' + test_config core.bigfilethreshold 1 && + git archive --format=zip HEAD >large-compressed.zip +' + +check_zip large-compressed + +test_done diff --git a/t/t5003/infozip-symlinks.zip b/t/t5003/infozip-symlinks.zip Binary files differnew file mode 100644 index 0000000000..065728c631 --- /dev/null +++ b/t/t5003/infozip-symlinks.zip diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh new file mode 100755 index 0000000000..67f3b54bed --- /dev/null +++ b/t/t5004-archive-corner-cases.sh @@ -0,0 +1,116 @@ +#!/bin/sh + +test_description='test corner cases of git-archive' +. ./test-lib.sh + +test_expect_success 'create commit with empty tree' ' + git commit --allow-empty -m foo +' + +# Make a dir and clean it up afterwards +make_dir() { + mkdir "$1" && + test_when_finished "rm -rf '$1'" +} + +# Check that the dir given in "$1" contains exactly the +# set of paths given as arguments. +check_dir() { + dir=$1; shift + { + echo "$dir" && + for i in "$@"; do + echo "$dir/$i" + done + } | sort >expect && + find "$dir" ! -name pax_global_header -print | sort >actual && + test_cmp expect actual +} + + +# bsdtar/libarchive versions before 3.1.3 consider a tar file with a +# global pax header that is not followed by a file record as corrupt. +if "$TAR" tf "$TEST_DIRECTORY"/t5004/empty-with-pax-header.tar >/dev/null 2>&1 +then + test_set_prereq HEADER_ONLY_TAR_OK +fi + +test_expect_success HEADER_ONLY_TAR_OK 'tar archive of commit with empty tree' ' + git archive --format=tar HEAD >empty-with-pax-header.tar && + make_dir extract && + "$TAR" xf empty-with-pax-header.tar -C extract && + check_dir extract +' + +test_expect_success 'tar archive of empty tree is empty' ' + git archive --format=tar HEAD: >empty.tar && + perl -e "print \"\\0\" x 10240" >10knuls.tar && + test_cmp 10knuls.tar empty.tar +' + +test_expect_success 'tar archive of empty tree with prefix' ' + git archive --format=tar --prefix=foo/ HEAD >prefix.tar && + make_dir extract && + "$TAR" xf prefix.tar -C extract && + check_dir extract foo +' + +test_expect_success UNZIP 'zip archive of empty tree is empty' ' + # Detect the exit code produced when our particular flavor of unzip + # sees an empty archive. Infozip will generate a warning and exit with + # code 1. But in the name of sanity, we do not expect other unzip + # implementations to do the same thing (it would be perfectly + # reasonable to exit 0, for example). + # + # This makes our test less rigorous on some platforms (unzip may not + # handle the empty repo at all, making our later check of its exit code + # a no-op). But we cannot do anything reasonable except skip the test + # on such platforms anyway, and this is the moral equivalent. + "$GIT_UNZIP" "$TEST_DIRECTORY"/t5004/empty.zip + expect_code=$? + + git archive --format=zip HEAD >empty.zip && + make_dir extract && + ( + cd extract && + test_expect_code $expect_code "$GIT_UNZIP" ../empty.zip + ) && + check_dir extract +' + +test_expect_success UNZIP 'zip archive of empty tree with prefix' ' + # We do not have to play exit-code tricks here, because our + # result should not be empty; it has a directory in it. + git archive --format=zip --prefix=foo/ HEAD >prefix.zip && + make_dir extract && + ( + cd extract && + "$GIT_UNZIP" ../prefix.zip + ) && + check_dir extract foo +' + +test_expect_success 'archive complains about pathspec on empty tree' ' + test_must_fail git archive --format=tar HEAD -- foo >/dev/null +' + +test_expect_success 'create a commit with an empty subtree' ' + empty_tree=$(git hash-object -t tree /dev/null) && + root_tree=$(printf "040000 tree $empty_tree\tsub\n" | git mktree) +' + +test_expect_success 'archive empty subtree with no pathspec' ' + git archive --format=tar $root_tree >subtree-all.tar && + make_dir extract && + "$TAR" xf subtree-all.tar -C extract && + check_dir extract sub +' + +test_expect_success 'archive empty subtree by direct pathspec' ' + git archive --format=tar $root_tree -- sub >subtree-path.tar && + make_dir extract && + "$TAR" xf subtree-path.tar -C extract && + check_dir extract sub +' + +test_done diff --git a/t/t5004/empty-with-pax-header.tar b/t/t5004/empty-with-pax-header.tar Binary files differnew file mode 100644 index 0000000000..da9e39e6cf --- /dev/null +++ b/t/t5004/empty-with-pax-header.tar diff --git a/t/t5004/empty.zip b/t/t5004/empty.zip Binary files differnew file mode 100644 index 0000000000..1a76bb6005 --- /dev/null +++ b/t/t5004/empty.zip diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index ebc36c1758..3e64a7a65d 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -11,7 +11,7 @@ test_expect_success 'split sample box' \ 'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last && last=`cat last` && echo total is $last && - test `cat last` = 16' + test `cat last` = 17' check_mailinfo () { mail=$1 opt=$2 @@ -65,7 +65,7 @@ test_expect_success 'respect NULs' ' git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain && test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 && (cat 001 | git mailinfo msg patch) && - test 4 = $(wc -l < patch) + test_line_count = 4 patch ' diff --git a/t/t5100/info0017 b/t/t5100/info0017 new file mode 100644 index 0000000000..d2bc89ffe9 --- /dev/null +++ b/t/t5100/info0017 @@ -0,0 +1,5 @@ +Author: A U Thor +Email: a.u.thor@example.com +Subject: A E I O U +Date: Mon, 17 Sep 2012 14:23:44 -0700 + diff --git a/t/t5100/msg0017 b/t/t5100/msg0017 new file mode 100644 index 0000000000..2ee0900850 --- /dev/null +++ b/t/t5100/msg0017 @@ -0,0 +1,2 @@ +New content here + diff --git a/t/t5100/patch0001 b/t/t5100/patch0001 index 8ce155167d..02c97746d6 100644 --- a/t/t5100/patch0001 +++ b/t/t5100/patch0001 @@ -1,5 +1,5 @@ --- - foo | 2 +- + foo | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/foo b/foo diff --git a/t/t5100/patch0002 b/t/t5100/patch0002 index 8ce155167d..02c97746d6 100644 --- a/t/t5100/patch0002 +++ b/t/t5100/patch0002 @@ -1,5 +1,5 @@ --- - foo | 2 +- + foo | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/foo b/foo diff --git a/t/t5100/patch0003 b/t/t5100/patch0003 index 8ce155167d..02c97746d6 100644 --- a/t/t5100/patch0003 +++ b/t/t5100/patch0003 @@ -1,5 +1,5 @@ --- - foo | 2 +- + foo | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/foo b/foo diff --git a/t/t5100/patch0005 b/t/t5100/patch0005 index 7d24b24af8..ab7a38373b 100644 --- a/t/t5100/patch0005 +++ b/t/t5100/patch0005 @@ -1,7 +1,7 @@ --- - Documentation/git-cvsimport-script.txt | 9 ++++++++- - git-cvsimport-script | 4 ++-- + Documentation/git-cvsimport-script.txt | 9 ++++++++- + git-cvsimport-script | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) 50452f9c0c2df1f04d83a26266ba704b13861632 diff --git a/t/t5100/patch0006 b/t/t5100/patch0006 index 8ce155167d..02c97746d6 100644 --- a/t/t5100/patch0006 +++ b/t/t5100/patch0006 @@ -1,5 +1,5 @@ --- - foo | 2 +- + foo | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/foo b/foo diff --git a/t/t5100/patch0010 b/t/t5100/patch0010 index f055481d56..436821c97a 100644 --- a/t/t5100/patch0010 +++ b/t/t5100/patch0010 @@ -1,5 +1,5 @@ --- - builtin-mailinfo.c | 2 +- + builtin-mailinfo.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c diff --git a/t/t5100/patch0011 b/t/t5100/patch0011 index 8841d3c139..0988713761 100644 --- a/t/t5100/patch0011 +++ b/t/t5100/patch0011 @@ -1,5 +1,5 @@ --- - builtin-mailinfo.c | 4 ++-- + builtin-mailinfo.c | 4 ++-- diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 3e5fe51..aabfe5c 100644 diff --git a/t/t5100/patch0014 b/t/t5100/patch0014 index 124efd234f..3f3825f9f2 100644 --- a/t/t5100/patch0014 +++ b/t/t5100/patch0014 @@ -1,5 +1,5 @@ --- - builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++- + builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++- 1 files changed, 36 insertions(+), 1 deletions(-) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c diff --git a/t/t5100/patch0014--scissors b/t/t5100/patch0014--scissors index 124efd234f..3f3825f9f2 100644 --- a/t/t5100/patch0014--scissors +++ b/t/t5100/patch0014--scissors @@ -1,5 +1,5 @@ --- - builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++- + builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++- 1 files changed, 36 insertions(+), 1 deletions(-) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c diff --git a/t/t5100/patch0017 b/t/t5100/patch0017 new file mode 100644 index 0000000000..35cf84c9a1 --- /dev/null +++ b/t/t5100/patch0017 @@ -0,0 +1,6 @@ +diff --git a/foo b/foo +index e69de29..d95f3ad 100644 +--- a/foo ++++ b/foo +@@ -0,0 +1 @@ ++New content diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index de1031241d..8b2ae064c3 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -12,7 +12,7 @@ Subject: [PATCH] a commit. Here is a patch from A U Thor. --- - foo | 2 +- + foo | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/foo b/foo @@ -52,7 +52,7 @@ two truly blank and another full of spaces in between. Hope this helps. --- - foo | 2 +- + foo | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/foo b/foo @@ -83,7 +83,7 @@ Message-Id: <nitpicker.12121212@example.net> Hopefully this would fix the problem stated there. --- - foo | 2 +- + foo | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/foo b/foo @@ -249,8 +249,8 @@ actual flags. Signed-off-by: David K=E5gedal <davidk@lysator.liu.se> --- - Documentation/git-cvsimport-script.txt | 9 ++++++++- - git-cvsimport-script | 4 ++-- + Documentation/git-cvsimport-script.txt | 9 ++++++++- + git-cvsimport-script | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) 50452f9c0c2df1f04d83a26266ba704b13861632 @@ -379,7 +379,7 @@ Subject: [PATCH] a commit. Here is a patch from A U Thor. --- - foo | 2 +- + foo | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/foo b/foo @@ -449,7 +449,7 @@ memcmp("Subject: ", header[i], 7) will never match. Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se> Signed-off-by: Junio C Hamano <gitster@pobox.com> --- - builtin-mailinfo.c | 2 +- + builtin-mailinfo.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c @@ -482,7 +482,7 @@ Content-Transfer-Encoding: quoted-printable Here comes a commit log message, and its second line is here. --- - builtin-mailinfo.c | 4 ++-- + builtin-mailinfo.c | 4 ++-- diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index 3e5fe51..aabfe5c 100644 @@ -587,7 +587,7 @@ everything before it in the message body. Signed-off-by: Junio C Hamano <gitster@pobox.com> --- - builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++- + builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++- 1 files changed, 36 insertions(+), 1 deletions(-) diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c @@ -683,3 +683,19 @@ index e69de29..d95f3ad 100644 @@ -0,0 +1 @@ +content +From nobody Mon Sep 17 00:00:00 2001 +From: A U Thor <a.u.thor@example.com> +Subject: A E I O U +Date: Mon, 17 Sep 2012 14:23:44 -0700 +MIME-Version: 1.0 +Content-Type: text/plain; charset="iso-2022-jp" +Content-type: text/plain; charset="UTF-8" + +New content here + +diff --git a/foo b/foo +index e69de29..d95f3ad 100644 +--- a/foo ++++ b/foo +@@ -0,0 +1 @@ ++New content diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh index 2af8947eeb..1afa0d5c44 100755 --- a/t/t5150-request-pull.sh +++ b/t/t5150-request-pull.sh @@ -80,12 +80,12 @@ test_expect_success 'setup: two scripts for reading pull requests' ' cat <<-EOT >fuzz.sed #!/bin/sed -nf + s/$downstream_url_for_sed/URL/g s/$_x40/OBJECT_NAME/g s/A U Thor/AUTHOR/g s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g s/ [^ ].*/ SUBJECT/g s/ [^ ].* (DATE)/ SUBJECT (DATE)/g - s/$downstream_url_for_sed/URL/g s/for-upstream/BRANCH/g s/mnemonic.txt/FILENAME/g s/^version [0-9]/VERSION/ @@ -216,7 +216,7 @@ test_expect_success 'pull request format' ' git request-pull initial "$downstream_url" >../request ) && <request sed -nf fuzz.sed >request.fuzzy && - test_cmp expect request.fuzzy + test_i18ncmp expect request.fuzzy ' diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 602806d09c..20c1961515 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -38,6 +38,10 @@ test_expect_success \ 'pack without delta' \ 'packname_1=$(git pack-objects --window=0 test-1 <obj-list)' +test_expect_success \ + 'pack-objects with bogus arguments' \ + 'test_must_fail git pack-objects --window=0 test-1 blah blah <obj-list' + rm -fr .git2 mkdir .git2 @@ -147,7 +151,7 @@ test_expect_success \ git cat-file $t $object || return 1 done <obj-list } >current && - test_cmp expect current' + cmp expect current' test_expect_success \ 'use packed deltified (REF_DELTA) objects' \ @@ -162,7 +166,7 @@ test_expect_success \ git cat-file $t $object || return 1 done <obj-list } >current && - test_cmp expect current' + cmp expect current' test_expect_success \ 'use packed deltified (OFS_DELTA) objects' \ @@ -177,7 +181,7 @@ test_expect_success \ git cat-file $t $object || return 1 done <obj-list } >current && - test_cmp expect current' + cmp expect current' unset GIT_OBJECT_DIRECTORY @@ -191,9 +195,9 @@ test_expect_success 'survive missing objects/pack directory' ' rm -fr $GOP && git index-pack --stdin --keep=test <../test-3-${packname_3}.pack && test -f $GOP/pack-${packname_3}.pack && - test_cmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack && + cmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack && test -f $GOP/pack-${packname_3}.idx && - test_cmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx && + cmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx && test -f $GOP/pack-${packname_3}.keep ) ' @@ -412,6 +416,11 @@ test_expect_success \ test_expect_success \ 'make sure index-pack detects the SHA1 collision' \ 'test_must_fail git index-pack -o bad.idx test-3.pack 2>msg && - grep "SHA1 COLLISION FOUND" msg' + test_i18ngrep "SHA1 COLLISION FOUND" msg' + +test_expect_success \ + 'make sure index-pack detects the SHA1 collision (large blobs)' \ + 'test_must_fail git -c core.bigfilethreshold=1 index-pack -o bad.idx test-3.pack 2>msg && + test_i18ngrep "SHA1 COLLISION FOUND" msg' test_done diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh index f8fa92446c..fe82025d4a 100755 --- a/t/t5302-pack-index.sh +++ b/t/t5302-pack-index.sh @@ -74,6 +74,10 @@ test_expect_success 'index-pack --verify on index version 2' ' ' test_expect_success \ + 'pack-objects --index-version=2, is not accepted' \ + 'test_must_fail git pack-objects --index-version=2, test-3 <obj-list' + +test_expect_success \ 'index v2: force some 64-bit offsets with pack-objects' \ 'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)' diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh index 5f6cd4f333..663b02bbb1 100755 --- a/t/t5303-pack-corruption-resilience.sh +++ b/t/t5303-pack-corruption-resilience.sh @@ -51,7 +51,7 @@ do_corrupt_object() { ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` && ofs=$(($ofs + $2)) && chmod +w ${pack}.pack && - dd of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs && + dd of=${pack}.pack bs=1 conv=notrunc seek=$ofs && test_must_fail git verify-pack ${pack}.pack } @@ -276,6 +276,33 @@ test_expect_success \ git cat-file blob $blob_3 > /dev/null' test_expect_success \ + 'corruption of delta base reference pointing to wrong object' \ + 'create_new_pack --delta-base-offset && + git prune-packed && + printf "\220\033" | do_corrupt_object $blob_3 2 && + git cat-file blob $blob_1 >/dev/null && + git cat-file blob $blob_2 >/dev/null && + test_must_fail git cat-file blob $blob_3 >/dev/null' + +test_expect_success \ + '... but having a loose copy allows for full recovery' \ + 'mv ${pack}.idx tmp && + git hash-object -t blob -w file_3 && + mv tmp ${pack}.idx && + git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ + '... and then a repack "clears" the corruption' \ + 'do_repack --delta-base-offset --no-reuse-delta && + git prune-packed && + git verify-pack ${pack}.pack && + git cat-file blob $blob_1 > /dev/null && + git cat-file blob $blob_2 > /dev/null && + git cat-file blob $blob_3 > /dev/null' + +test_expect_success \ 'corrupting header to have too small output buffer fails unpack' \ 'create_new_pack && git prune-packed && diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh index d645328609..e4bb3a1457 100755 --- a/t/t5304-prune.sh +++ b/t/t5304-prune.sh @@ -195,4 +195,30 @@ test_expect_success 'gc: prune old objects after local clone' ' ) ' +test_expect_success 'garbage report in count-objects -v' ' + : >.git/objects/pack/foo && + : >.git/objects/pack/foo.bar && + : >.git/objects/pack/foo.keep && + : >.git/objects/pack/foo.pack && + : >.git/objects/pack/fake.bar && + : >.git/objects/pack/fake.keep && + : >.git/objects/pack/fake.pack && + : >.git/objects/pack/fake.idx && + : >.git/objects/pack/fake2.keep && + : >.git/objects/pack/fake3.idx && + git count-objects -v 2>stderr && + grep "index file .git/objects/pack/fake.idx is too small" stderr && + grep "^warning:" stderr | sort >actual && + cat >expected <<\EOF && +warning: garbage found: .git/objects/pack/fake.bar +warning: garbage found: .git/objects/pack/foo +warning: garbage found: .git/objects/pack/foo.bar +warning: no corresponding .idx nor .pack: .git/objects/pack/fake2.keep +warning: no corresponding .idx: .git/objects/pack/foo.keep +warning: no corresponding .idx: .git/objects/pack/foo.pack +warning: no corresponding .pack: .git/objects/pack/fake3.idx +EOF + test_cmp expected actual +' + test_done diff --git a/t/t5308-pack-detect-duplicates.sh b/t/t5308-pack-detect-duplicates.sh new file mode 100755 index 0000000000..9c5a8766ab --- /dev/null +++ b/t/t5308-pack-detect-duplicates.sh @@ -0,0 +1,80 @@ +#!/bin/sh + +test_description='handling of duplicate objects in incoming packfiles' +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-pack.sh + +# The sha1s we have in our pack. It's important that these have the same +# starting byte, so that they end up in the same fanout section of the index. +# That lets us make sure we are exercising the binary search with both sets. +LO_SHA1=e68fe8129b546b101aee9510c5328e7f21ca1d18 +HI_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 + +# And here's a "missing sha1" which will produce failed lookups. It must also +# be in the same fanout section, and should be between the two (so that during +# our binary search, we are sure to end up looking at one or the other of the +# duplicate runs). +MISSING_SHA1='e69d000000000000000000000000000000000000' + +# git will never intentionally create packfiles with +# duplicate objects, so we have to construct them by hand. +# +# $1 is the name of the packfile to create +# +# $2 is the number of times to duplicate each object +create_pack () { + pack_header "$((2 * $2))" >"$1" && + for i in $(test_seq 1 "$2"); do + pack_obj $LO_SHA1 && + pack_obj $HI_SHA1 + done >>"$1" && + pack_trailer "$1" +} + +# double-check that create_pack actually works +test_expect_success 'pack with no duplicates' ' + create_pack no-dups.pack 1 && + git index-pack --stdin <no-dups.pack +' + +test_expect_success 'index-pack will allow duplicate objects by default' ' + clear_packs && + create_pack dups.pack 100 && + git index-pack --stdin <dups.pack +' + +test_expect_success 'create batch-check test vectors' ' + cat >input <<-EOF && + $LO_SHA1 + $HI_SHA1 + $MISSING_SHA1 + EOF + cat >expect <<-EOF + $LO_SHA1 blob 2 + $HI_SHA1 blob 0 + $MISSING_SHA1 missing + EOF +' + +test_expect_success 'lookup in duplicated pack (binary search)' ' + git cat-file --batch-check <input >actual && + test_cmp expect actual +' + +test_expect_success 'lookup in duplicated pack (GIT_USE_LOOKUP)' ' + ( + GIT_USE_LOOKUP=1 && + export GIT_USE_LOOKUP && + git cat-file --batch-check <input >actual + ) && + test_cmp expect actual +' + +test_expect_success 'index-pack can reject packs with duplicates' ' + clear_packs && + create_pack dups.pack 2 && + test_must_fail git index-pack --strict --stdin <dups.pack && + test_expect_code 1 git cat-file -e $LO_SHA1 +' + +test_done diff --git a/t/t5309-pack-delta-cycles.sh b/t/t5309-pack-delta-cycles.sh new file mode 100755 index 0000000000..3e7861b075 --- /dev/null +++ b/t/t5309-pack-delta-cycles.sh @@ -0,0 +1,77 @@ +#!/bin/sh + +test_description='test index-pack handling of delta cycles in packfiles' +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-pack.sh + +# Two similar-ish objects that we have computed deltas between. +A=01d7713666f4de822776c7622c10f1b07de280dc +B=e68fe8129b546b101aee9510c5328e7f21ca1d18 + +# double-check our hand-constucted packs +test_expect_success 'index-pack works with a single delta (A->B)' ' + clear_packs && + { + pack_header 2 && + pack_obj $A $B && + pack_obj $B + } >ab.pack && + pack_trailer ab.pack && + git index-pack --stdin <ab.pack && + git cat-file -t $A && + git cat-file -t $B +' + +test_expect_success 'index-pack works with a single delta (B->A)' ' + clear_packs && + { + pack_header 2 && + pack_obj $A && + pack_obj $B $A + } >ba.pack && + pack_trailer ba.pack && + git index-pack --stdin <ba.pack && + git cat-file -t $A && + git cat-file -t $B +' + +test_expect_success 'index-pack detects missing base objects' ' + clear_packs && + { + pack_header 1 && + pack_obj $A $B + } >missing.pack && + pack_trailer missing.pack && + test_must_fail git index-pack --fix-thin --stdin <missing.pack +' + +test_expect_success 'index-pack detects REF_DELTA cycles' ' + clear_packs && + { + pack_header 2 && + pack_obj $A $B && + pack_obj $B $A + } >cycle.pack && + pack_trailer cycle.pack && + test_must_fail git index-pack --fix-thin --stdin <cycle.pack +' + +test_expect_failure 'failover to an object in another pack' ' + clear_packs && + git index-pack --stdin <ab.pack && + git index-pack --stdin --fix-thin <cycle.pack +' + +test_expect_failure 'failover to a duplicate object in the same pack' ' + clear_packs && + { + pack_header 3 && + pack_obj $A $B && + pack_obj $B $A && + pack_obj $A + } >recoverable.pack && + pack_trailer recoverable.pack && + git index-pack --fix-thin --stdin <recoverable.pack +' + +test_done diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 0eace37a03..129fc88bd2 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -145,6 +145,40 @@ test_expect_success 'push --all excludes remote-tracking hierarchy' ' ) ' +test_expect_success 'receive-pack runs auto-gc in remote repo' ' + rm -rf parent child && + git init parent && + ( + # Setup a repo with 2 packs + cd parent && + echo "Some text" >file.txt && + git add . && + git commit -m "Initial commit" && + git repack -adl && + echo "Some more text" >>file.txt && + git commit -a -m "Second commit" && + git repack + ) && + cp -R parent child && + ( + # Set the child to auto-pack if more than one pack exists + cd child && + git config gc.autopacklimit 1 && + git branch test_auto_gc && + # And create a file that follows the temporary object naming + # convention for the auto-gc to remove + : >.git/objects/tmp_test_object && + test-chmtime =-1209601 .git/objects/tmp_test_object + ) && + ( + cd parent && + echo "Even more text" >>file.txt && + git commit -a -m "Third commit" && + git send-pack ../child HEAD:refs/heads/test_auto_gc + ) && + test ! -e child/.git/objects/tmp_test_object +' + rewound_push_setup() { rm -rf parent child && mkdir parent && diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh index c24003565d..2b8c0bac7d 100755 --- a/t/t5404-tracking-branches.sh +++ b/t/t5404-tracking-branches.sh @@ -36,7 +36,7 @@ test_expect_success 'prepare pushable branches' ' ' test_expect_success 'mixed-success push returns error' ' - test_must_fail git push + test_must_fail git push origin : ' test_expect_success 'check tracking branches updated correctly after push' ' diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh index baa670cea5..ea2e0d4b48 100755 --- a/t/t5407-post-rewrite-hook.sh +++ b/t/t5407-post-rewrite-hook.sh @@ -31,8 +31,8 @@ clear_hook_input () { } verify_hook_input () { - test_cmp "$TRASH_DIRECTORY"/post-rewrite.args expected.args && - test_cmp "$TRASH_DIRECTORY"/post-rewrite.data expected.data + test_cmp expected.args "$TRASH_DIRECTORY"/post-rewrite.args && + test_cmp expected.data "$TRASH_DIRECTORY"/post-rewrite.data } test_expect_success 'git commit --amend' ' diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 9bf69e9a0f..d87ddf73b7 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -114,8 +114,40 @@ pull_to_client 2nd "refs/heads/B" $((64*3)) pull_to_client 3rd "refs/heads/A" $((1*3)) +test_expect_success 'single branch clone' ' + git clone --single-branch "file://$(pwd)/." singlebranch +' + +test_expect_success 'single branch object count' ' + GIT_DIR=singlebranch/.git git count-objects -v | + grep "^in-pack:" > count.singlebranch && + echo "in-pack: 198" >expected && + test_cmp expected count.singlebranch +' + +test_expect_success 'single given branch clone' ' + git clone --single-branch --branch A "file://$(pwd)/." branch-a && + test_must_fail git --git-dir=branch-a/.git rev-parse origin/B +' + +test_expect_success 'clone shallow depth 1' ' + git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0 && + test "`git --git-dir=shallow0/.git rev-list --count HEAD`" = 1 +' + +test_expect_success 'clone shallow depth 1 with fsck' ' + git config --global fetch.fsckobjects true && + git clone --no-single-branch --depth 1 "file://$(pwd)/." shallow0fsck && + test "`git --git-dir=shallow0fsck/.git rev-list --count HEAD`" = 1 && + git config --global --unset fetch.fsckobjects +' + test_expect_success 'clone shallow' ' - git clone --depth 2 "file://$(pwd)/." shallow + git clone --no-single-branch --depth 2 "file://$(pwd)/." shallow +' + +test_expect_success 'clone shallow depth count' ' + test "`git --git-dir=shallow/.git rev-list --count HEAD`" = 2 ' test_expect_success 'clone shallow object count' ' @@ -123,7 +155,7 @@ test_expect_success 'clone shallow object count' ' cd shallow && git count-objects -v ) > count.shallow && - grep "^in-pack: 18" count.shallow + grep "^in-pack: 12" count.shallow ' test_expect_success 'clone shallow object count (part 2)' ' @@ -240,12 +272,264 @@ test_expect_success 'additional simple shallow deepenings' ' ) ' +test_expect_success 'clone shallow depth count' ' + test "`git --git-dir=shallow/.git rev-list --count HEAD`" = 11 +' + test_expect_success 'clone shallow object count' ' ( cd shallow && git count-objects -v ) > count.shallow && - grep "^count: 52" count.shallow + grep "^count: 55" count.shallow +' + +test_expect_success 'fetch --no-shallow on full repo' ' + test_must_fail git fetch --noshallow +' + +test_expect_success 'fetch --depth --no-shallow' ' + ( + cd shallow && + test_must_fail git fetch --depth=1 --noshallow + ) +' + +test_expect_success 'turn shallow to complete repository' ' + ( + cd shallow && + git fetch --unshallow && + ! test -f .git/shallow && + git fsck --full + ) +' + +test_expect_success 'clone shallow without --no-single-branch' ' + git clone --depth 1 "file://$(pwd)/." shallow2 +' + +test_expect_success 'clone shallow object count' ' + ( + cd shallow2 && + git count-objects -v + ) > count.shallow2 && + grep "^in-pack: 3" count.shallow2 +' + +test_expect_success 'clone shallow with --branch' ' + git clone --depth 1 --branch A "file://$(pwd)/." shallow3 +' + +test_expect_success 'clone shallow object count' ' + echo "in-pack: 3" > count3.expected && + GIT_DIR=shallow3/.git git count-objects -v | + grep "^in-pack" > count3.actual && + test_cmp count3.expected count3.actual +' + +test_expect_success 'clone shallow with detached HEAD' ' + git checkout HEAD^ && + git clone --depth 1 "file://$(pwd)/." shallow5 && + git checkout - && + GIT_DIR=shallow5/.git git rev-parse HEAD >actual && + git rev-parse HEAD^ >expected && + test_cmp expected actual +' + +test_expect_success 'shallow clone pulling tags' ' + git tag -a -m A TAGA1 A && + git tag -a -m B TAGB1 B && + git tag TAGA2 A && + git tag TAGB2 B && + git clone --depth 1 "file://$(pwd)/." shallow6 && + + cat >taglist.expected <<\EOF && +TAGB1 +TAGB2 +EOF + GIT_DIR=shallow6/.git git tag -l >taglist.actual && + test_cmp taglist.expected taglist.actual && + + echo "in-pack: 4" > count6.expected && + GIT_DIR=shallow6/.git git count-objects -v | + grep "^in-pack" > count6.actual && + test_cmp count6.expected count6.actual +' + +test_expect_success 'shallow cloning single tag' ' + git clone --depth 1 --branch=TAGB1 "file://$(pwd)/." shallow7 && + cat >taglist.expected <<\EOF && +TAGB1 +TAGB2 +EOF + GIT_DIR=shallow7/.git git tag -l >taglist.actual && + test_cmp taglist.expected taglist.actual && + + echo "in-pack: 4" > count7.expected && + GIT_DIR=shallow7/.git git count-objects -v | + grep "^in-pack" > count7.actual && + test_cmp count7.expected count7.actual +' + +test_expect_success 'clone shallow with packed refs' ' + git pack-refs --all && + git clone --depth 1 --branch A "file://$(pwd)/." shallow8 && + echo "in-pack: 4" > count8.expected && + GIT_DIR=shallow8/.git git count-objects -v | + grep "^in-pack" > count8.actual && + test_cmp count8.expected count8.actual +' + +test_expect_success 'fetch in shallow repo unreachable shallow objects' ' + ( + git clone --bare --branch B --single-branch "file://$(pwd)/." no-reflog && + git clone --depth 1 "file://$(pwd)/no-reflog" shallow9 && + cd no-reflog && + git tag -d TAGB1 TAGB2 && + git update-ref refs/heads/B B~~ && + git gc --prune=now && + cd ../shallow9 && + git fetch origin && + git fsck --no-dangling + ) +' +test_expect_success 'fetch creating new shallow root' ' + ( + git clone "file://$(pwd)/." shallow10 && + git commit --allow-empty -m empty && + cd shallow10 && + git fetch --depth=1 --progress 2>actual && + # This should fetch only the empty commit, no tree or + # blob objects + grep "remote: Total 1" actual + ) +' + +test_expect_success 'setup tests for the --stdin parameter' ' + for head in C D E F + do + add $head + done && + for head in A B C D E F + do + git tag $head $head + done && + cat >input <<-\EOF + refs/heads/C + refs/heads/A + refs/heads/D + refs/tags/C + refs/heads/B + refs/tags/A + refs/heads/E + refs/tags/B + refs/tags/E + refs/tags/D + EOF + sort <input >expect && + ( + echo refs/heads/E && + echo refs/tags/E && + cat input + ) >input.dup +' + +test_expect_success 'fetch refs from cmdline' ' + ( + cd client && + git fetch-pack --no-progress .. $(cat ../input) + ) >output && + cut -d " " -f 2 <output | sort >actual && + test_cmp expect actual +' + +test_expect_success 'fetch refs from stdin' ' + ( + cd client && + git fetch-pack --stdin --no-progress .. <../input + ) >output && + cut -d " " -f 2 <output | sort >actual && + test_cmp expect actual +' + +test_expect_success 'fetch mixed refs from cmdline and stdin' ' + ( + cd client && + tail -n +5 ../input | + git fetch-pack --stdin --no-progress .. $(head -n 4 ../input) + ) >output && + cut -d " " -f 2 <output | sort >actual && + test_cmp expect actual +' + +test_expect_success 'test duplicate refs from stdin' ' + ( + cd client && + git fetch-pack --stdin --no-progress .. <../input.dup + ) >output && + cut -d " " -f 2 <output | sort >actual && + test_cmp expect actual +' + +test_expect_success 'set up tests of missing reference' ' + cat >expect-error <<-\EOF + error: no such remote ref refs/heads/xyzzy + EOF +' + +test_expect_success 'test lonely missing ref' ' + ( + cd client && + test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy + ) >/dev/null 2>error-m && + test_cmp expect-error error-m +' + +test_expect_success 'test missing ref after existing' ' + ( + cd client && + test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy + ) >/dev/null 2>error-em && + test_cmp expect-error error-em +' + +test_expect_success 'test missing ref before existing' ' + ( + cd client && + test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A + ) >/dev/null 2>error-me && + test_cmp expect-error error-me +' + +test_expect_success 'test --all, --depth, and explicit head' ' + ( + cd client && + git fetch-pack --no-progress --all --depth=1 .. refs/heads/A + ) >out-adh 2>error-adh +' + +test_expect_success 'test --all, --depth, and explicit tag' ' + git tag OLDTAG refs/heads/B~5 && + ( + cd client && + git fetch-pack --no-progress --all --depth=1 .. refs/tags/OLDTAG + ) >out-adt 2>error-adt +' + +test_expect_success 'shallow fetch with tags does not break the repository' ' + mkdir repo1 && + ( + cd repo1 && + git init && + test_commit 1 && + test_commit 2 && + test_commit 3 && + mkdir repo2 && + cd repo2 && + git init && + git fetch --depth=2 ../.git master:branch && + git fsck + ) ' test_done diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh index 60de2d6ede..f30c03885c 100755 --- a/t/t5503-tagfollow.sh +++ b/t/t5503-tagfollow.sh @@ -4,10 +4,6 @@ test_description='test automatic tag following' . ./test-lib.sh -if ! test_have_prereq NOT_MINGW; then - say "GIT_DEBUG_SEND_PACK not supported - skipping tests" -fi - # End state of the repository: # # T - tag1 S - tag2 @@ -17,7 +13,7 @@ fi # \ C - origin/cat \ # origin/master master -test_expect_success NOT_MINGW setup ' +test_expect_success setup ' test_tick && echo ichi >file && git add file && @@ -39,28 +35,35 @@ test_expect_success NOT_MINGW setup ' ' U=UPLOAD_LOG +UPATH="$(pwd)/$U" -test_expect_success NOT_MINGW 'setup expect' ' +test_expect_success 'setup expect' ' cat - <<EOF >expect -#S want $A -#E EOF ' -test_expect_success NOT_MINGW 'fetch A (new commit : 1 connection)' ' +get_needs () { + test -s "$1" && + perl -alne ' + next unless $F[1] eq "upload-pack<"; + last if $F[2] eq "0000"; + print $F[2], " ", $F[3]; + ' "$1" +} + +test_expect_success 'fetch A (new commit : 1 connection)' ' rm -f $U && ( cd cloned && - GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U && + GIT_TRACE_PACKET=$UPATH git fetch && test $A = $(git rev-parse --verify origin/master) ) && - test -s $U && - cut -d" " -f1,2 $U >actual && + get_needs $U >actual && test_cmp expect actual ' -test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" ' +test_expect_success "create tag T on A, create C on branch cat" ' git tag -a -m tag1 tag1 $A && T=$(git rev-parse --verify tag1) && @@ -72,30 +75,27 @@ test_expect_success NOT_MINGW "create tag T on A, create C on branch cat" ' git checkout master ' -test_expect_success NOT_MINGW 'setup expect' ' +test_expect_success 'setup expect' ' cat - <<EOF >expect -#S want $C want $T -#E EOF ' -test_expect_success NOT_MINGW 'fetch C, T (new branch, tag : 1 connection)' ' +test_expect_success 'fetch C, T (new branch, tag : 1 connection)' ' rm -f $U && ( cd cloned && - GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U && + GIT_TRACE_PACKET=$UPATH git fetch && test $C = $(git rev-parse --verify origin/cat) && test $T = $(git rev-parse --verify tag1) && test $A = $(git rev-parse --verify tag1^0) ) && - test -s $U && - cut -d" " -f1,2 $U >actual && + get_needs $U >actual && test_cmp expect actual ' -test_expect_success NOT_MINGW "create commits O, B, tag S on B" ' +test_expect_success "create commits O, B, tag S on B" ' test_tick && echo O >file && git add file && @@ -111,39 +111,34 @@ test_expect_success NOT_MINGW "create commits O, B, tag S on B" ' S=$(git rev-parse --verify tag2) ' -test_expect_success NOT_MINGW 'setup expect' ' +test_expect_success 'setup expect' ' cat - <<EOF >expect -#S want $B want $S -#E EOF ' -test_expect_success NOT_MINGW 'fetch B, S (commit and tag : 1 connection)' ' +test_expect_success 'fetch B, S (commit and tag : 1 connection)' ' rm -f $U && ( cd cloned && - GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U && + GIT_TRACE_PACKET=$UPATH git fetch && test $B = $(git rev-parse --verify origin/master) && test $B = $(git rev-parse --verify tag2^0) && test $S = $(git rev-parse --verify tag2) ) && - test -s $U && - cut -d" " -f1,2 $U >actual && + get_needs $U >actual && test_cmp expect actual ' -test_expect_success NOT_MINGW 'setup expect' ' +test_expect_success 'setup expect' ' cat - <<EOF >expect -#S want $B want $S -#E EOF ' -test_expect_success NOT_MINGW 'new clone fetch master and tags' ' +test_expect_success 'new clone fetch master and tags' ' git branch -D cat rm -f $U ( @@ -151,15 +146,14 @@ test_expect_success NOT_MINGW 'new clone fetch master and tags' ' cd clone2 && git init && git remote add origin .. && - GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U && + GIT_TRACE_PACKET=$UPATH git fetch && test $B = $(git rev-parse --verify origin/master) && test $S = $(git rev-parse --verify tag2) && test $B = $(git rev-parse --verify tag2^0) && test $T = $(git rev-parse --verify tag1) && test $A = $(git rev-parse --verify tag1^0) ) && - test -s $U && - cut -d" " -f1,2 $U >actual && + get_needs $U >actual && test_cmp expect actual ' diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 35ec294d9a..69ee13c8be 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -89,7 +89,7 @@ test_expect_success 'push with !receive.fsckobjects' ' cat >exp <<EOF To dst -! refs/heads/master:refs/heads/test [remote rejected] (n/a (unpacker error)) +! refs/heads/master:refs/heads/test [remote rejected] (unpacker error) EOF test_expect_success 'push with receive.fsckobjects' ' diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index e8af615e6d..ac79dd915d 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -42,101 +42,104 @@ check_tracking_branch () { } test_expect_success setup ' - setup_repository one && setup_repository two && ( - cd two && git branch another + cd two && + git branch another ) && git clone one test - ' -test_expect_success 'remote information for the origin' ' -( - cd test && - tokens_match origin "$(git remote)" && - check_remote_track origin master side && - check_tracking_branch origin HEAD master side -) +test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' ' + ( + cd test && + tokens_match origin "$(git remote)" && + check_remote_track origin master side && + check_tracking_branch origin HEAD master side + ) ' test_expect_success 'add another remote' ' -( - cd test && - git remote add -f second ../two && - tokens_match "origin second" "$(git remote)" && - check_remote_track origin master side && - check_remote_track second master side another && - check_tracking_branch second master side another && - git for-each-ref "--format=%(refname)" refs/remotes | - sed -e "/^refs\/remotes\/origin\//d" \ - -e "/^refs\/remotes\/second\//d" >actual && - >expect && - test_cmp expect actual -) + ( + cd test && + git remote add -f second ../two && + tokens_match "origin second" "$(git remote)" && + check_tracking_branch second master side another && + git for-each-ref "--format=%(refname)" refs/remotes | + sed -e "/^refs\/remotes\/origin\//d" \ + -e "/^refs\/remotes\/second\//d" >actual && + >expect && + test_cmp expect actual + ) +' + +test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' ' + ( + cd test && + check_remote_track origin master side && + check_remote_track second master side another + ) ' test_expect_success 'remote forces tracking branches' ' -( - cd test && - case `git config remote.second.fetch` in - +*) true ;; - *) false ;; - esac -) + ( + cd test && + case `git config remote.second.fetch` in + +*) true ;; + *) false ;; + esac + ) ' test_expect_success 'remove remote' ' -( - cd test && - git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master && - git remote rm second -) + ( + cd test && + git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master && + git remote rm second + ) ' -test_expect_success 'remove remote' ' -( - cd test && - tokens_match origin "$(git remote)" && - check_remote_track origin master side && - git for-each-ref "--format=%(refname)" refs/remotes | - sed -e "/^refs\/remotes\/origin\//d" >actual && - >expect && - test_cmp expect actual -) +test_expect_success C_LOCALE_OUTPUT 'remove remote' ' + ( + cd test && + tokens_match origin "$(git remote)" && + check_remote_track origin master side && + git for-each-ref "--format=%(refname)" refs/remotes | + sed -e "/^refs\/remotes\/origin\//d" >actual && + >expect && + test_cmp expect actual + ) ' test_expect_success 'remove remote protects local branches' ' -( - cd test && - { cat >expect1 <<EOF -Note: A branch outside the refs/remotes/ hierarchy was not removed; -to delete it, use: - git branch -d master -EOF - } && - { cat >expect2 <<EOF -Note: Some branches outside the refs/remotes/ hierarchy were not removed; -to delete them, use: - git branch -d foobranch - git branch -d master -EOF - } && - git tag footag && - git config --add remote.oops.fetch "+refs/*:refs/*" && - git remote rm oops 2>actual1 && - git branch foobranch && - git config --add remote.oops.fetch "+refs/*:refs/*" && - git remote rm oops 2>actual2 && - git branch -d foobranch && - git tag -d footag && - test_cmp expect1 actual1 && - test_cmp expect2 actual2 -) -' - -cat > test/expect << EOF + ( + cd test && + cat >expect1 <<-\EOF && + Note: A branch outside the refs/remotes/ hierarchy was not removed; + to delete it, use: + git branch -d master + EOF + cat >expect2 <<-\EOF && + Note: Some branches outside the refs/remotes/ hierarchy were not removed; + to delete them, use: + git branch -d foobranch + git branch -d master + EOF + git tag footag && + git config --add remote.oops.fetch "+refs/*:refs/*" && + git remote remove oops 2>actual1 && + git branch foobranch && + git config --add remote.oops.fetch "+refs/*:refs/*" && + git remote rm oops 2>actual2 && + git branch -d foobranch && + git tag -d footag && + test_i18ncmp expect1 actual1 && + test_i18ncmp expect2 actual2 + ) +' + +cat >test/expect <<EOF * remote origin Fetch URL: $(pwd)/one Push URL: $(pwd)/one @@ -157,45 +160,47 @@ cat > test/expect << EOF * remote two Fetch URL: ../two Push URL: ../three - HEAD branch (remote HEAD is ambiguous, may be one of the following): - another - master + HEAD branch: master Local refs configured for 'git push': ahead forces to master (fast-forwardable) master pushes to another (up to date) EOF test_expect_success 'show' ' - (cd test && - git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream && - git fetch && - git checkout -b ahead origin/master && - echo 1 >> file && - test_tick && - git commit -m update file && - git checkout master && - git branch --track octopus origin/master && - git branch --track rebase origin/master && - git branch -d -r origin/master && - git config --add remote.two.url ../two && - git config --add remote.two.pushurl ../three && - git config branch.rebase.rebase true && - git config branch.octopus.merge "topic-a topic-b topic-c" && - (cd ../one && - echo 1 > file && - test_tick && - git commit -m update file) && - git config --add remote.origin.push : && - git config --add remote.origin.push refs/heads/master:refs/heads/upstream && - git config --add remote.origin.push +refs/tags/lastbackup && - git config --add remote.two.push +refs/heads/ahead:refs/heads/master && - git config --add remote.two.push refs/heads/master:refs/heads/another && - git remote show origin two > output && - git branch -d rebase octopus && - test_cmp expect output) -' - -cat > test/expect << EOF + ( + cd test && + git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream && + git fetch && + git checkout -b ahead origin/master && + echo 1 >>file && + test_tick && + git commit -m update file && + git checkout master && + git branch --track octopus origin/master && + git branch --track rebase origin/master && + git branch -d -r origin/master && + git config --add remote.two.url ../two && + git config --add remote.two.pushurl ../three && + git config branch.rebase.rebase true && + git config branch.octopus.merge "topic-a topic-b topic-c" && + ( + cd ../one && + echo 1 >file && + test_tick && + git commit -m update file + ) && + git config --add remote.origin.push : && + git config --add remote.origin.push refs/heads/master:refs/heads/upstream && + git config --add remote.origin.push +refs/tags/lastbackup && + git config --add remote.two.push +refs/heads/ahead:refs/heads/master && + git config --add remote.two.push refs/heads/master:refs/heads/another && + git remote show origin two >output && + git branch -d rebase octopus && + test_i18ncmp expect output + ) +' + +cat >test/expect <<EOF * remote origin Fetch URL: $(pwd)/one Push URL: $(pwd)/one @@ -213,152 +218,183 @@ cat > test/expect << EOF EOF test_expect_success 'show -n' ' - (mv one one.unreachable && - cd test && - git remote show -n origin > output && - mv ../one.unreachable ../one && - test_cmp expect output) + mv one one.unreachable && + ( + cd test && + git remote show -n origin >output && + mv ../one.unreachable ../one && + test_i18ncmp expect output + ) ' test_expect_success 'prune' ' - (cd one && - git branch -m side side2) && - (cd test && - git fetch origin && - git remote prune origin && - git rev-parse refs/remotes/origin/side2 && - test_must_fail git rev-parse refs/remotes/origin/side) + ( + cd one && + git branch -m side side2 + ) && + ( + cd test && + git fetch origin && + git remote prune origin && + git rev-parse refs/remotes/origin/side2 && + test_must_fail git rev-parse refs/remotes/origin/side + ) ' test_expect_success 'set-head --delete' ' - (cd test && - git symbolic-ref refs/remotes/origin/HEAD && - git remote set-head --delete origin && - test_must_fail git symbolic-ref refs/remotes/origin/HEAD) + ( + cd test && + git symbolic-ref refs/remotes/origin/HEAD && + git remote set-head --delete origin && + test_must_fail git symbolic-ref refs/remotes/origin/HEAD + ) ' test_expect_success 'set-head --auto' ' - (cd test && - git remote set-head --auto origin && - echo refs/remotes/origin/master >expect && - git symbolic-ref refs/remotes/origin/HEAD >output && - test_cmp expect output + ( + cd test && + git remote set-head --auto origin && + echo refs/remotes/origin/master >expect && + git symbolic-ref refs/remotes/origin/HEAD >output && + test_cmp expect output ) ' -cat >test/expect <<EOF -error: Multiple remote HEAD branches. Please choose one explicitly with: - git remote set-head two another - git remote set-head two master -EOF - -test_expect_success 'set-head --auto fails w/multiple HEADs' ' - (cd test && - test_must_fail git remote set-head --auto two >output 2>&1 && - test_cmp expect output) +test_expect_success 'set-head --auto has no problem w/multiple HEADs' ' + ( + cd test && + git fetch two "refs/heads/*:refs/remotes/two/*" && + git remote set-head --auto two >output 2>&1 && + echo "two/HEAD set to master" >expect && + test_i18ncmp expect output + ) ' -cat >test/expect <<EOF +cat >test/expect <<\EOF refs/remotes/origin/side2 EOF test_expect_success 'set-head explicit' ' - (cd test && - git remote set-head origin side2 && - git symbolic-ref refs/remotes/origin/HEAD >output && - git remote set-head origin master && - test_cmp expect output) + ( + cd test && + git remote set-head origin side2 && + git symbolic-ref refs/remotes/origin/HEAD >output && + git remote set-head origin master && + test_cmp expect output + ) ' -cat > test/expect << EOF +cat >test/expect <<EOF Pruning origin URL: $(pwd)/one * [would prune] origin/side2 EOF test_expect_success 'prune --dry-run' ' - (cd one && - git branch -m side2 side) && - (cd test && - git remote prune --dry-run origin > output && - git rev-parse refs/remotes/origin/side2 && - test_must_fail git rev-parse refs/remotes/origin/side && - (cd ../one && - git branch -m side side2) && - test_cmp expect output) + ( + cd one && + git branch -m side2 side) && + ( + cd test && + git remote prune --dry-run origin >output && + git rev-parse refs/remotes/origin/side2 && + test_must_fail git rev-parse refs/remotes/origin/side && + ( + cd ../one && + git branch -m side side2) && + test_i18ncmp expect output + ) ' test_expect_success 'add --mirror && prune' ' - (mkdir mirror && - cd mirror && - git init --bare && - git remote add --mirror -f origin ../one) && - (cd one && - git branch -m side2 side) && - (cd mirror && - git rev-parse --verify refs/heads/side2 && - test_must_fail git rev-parse --verify refs/heads/side && - git fetch origin && - git remote prune origin && - test_must_fail git rev-parse --verify refs/heads/side2 && - git rev-parse --verify refs/heads/side) + mkdir mirror && + ( + cd mirror && + git init --bare && + git remote add --mirror -f origin ../one + ) && + ( + cd one && + git branch -m side2 side + ) && + ( + cd mirror && + git rev-parse --verify refs/heads/side2 && + test_must_fail git rev-parse --verify refs/heads/side && + git fetch origin && + git remote prune origin && + test_must_fail git rev-parse --verify refs/heads/side2 && + git rev-parse --verify refs/heads/side + ) ' test_expect_success 'add --mirror=fetch' ' mkdir mirror-fetch && git init mirror-fetch/parent && - (cd mirror-fetch/parent && - test_commit one) && + ( + cd mirror-fetch/parent && + test_commit one + ) && git init --bare mirror-fetch/child && - (cd mirror-fetch/child && - git remote add --mirror=fetch -f parent ../parent) + ( + cd mirror-fetch/child && + git remote add --mirror=fetch -f parent ../parent + ) ' test_expect_success 'fetch mirrors act as mirrors during fetch' ' - (cd mirror-fetch/parent && - git branch new && - git branch -m master renamed + ( + cd mirror-fetch/parent && + git branch new && + git branch -m master renamed ) && - (cd mirror-fetch/child && - git fetch parent && - git rev-parse --verify refs/heads/new && - git rev-parse --verify refs/heads/renamed + ( + cd mirror-fetch/child && + git fetch parent && + git rev-parse --verify refs/heads/new && + git rev-parse --verify refs/heads/renamed ) ' test_expect_success 'fetch mirrors can prune' ' - (cd mirror-fetch/child && - git remote prune parent && - test_must_fail git rev-parse --verify refs/heads/master + ( + cd mirror-fetch/child && + git remote prune parent && + test_must_fail git rev-parse --verify refs/heads/master ) ' test_expect_success 'fetch mirrors do not act as mirrors during push' ' - (cd mirror-fetch/parent && - git checkout HEAD^0 + ( + cd mirror-fetch/parent && + git checkout HEAD^0 ) && - (cd mirror-fetch/child && - git branch -m renamed renamed2 && - git push parent + ( + cd mirror-fetch/child && + git branch -m renamed renamed2 && + git push parent : ) && - (cd mirror-fetch/parent && - git rev-parse --verify renamed && - test_must_fail git rev-parse --verify refs/heads/renamed2 + ( + cd mirror-fetch/parent && + git rev-parse --verify renamed && + test_must_fail git rev-parse --verify refs/heads/renamed2 ) ' test_expect_success 'add fetch mirror with specific branches' ' git init --bare mirror-fetch/track && - (cd mirror-fetch/track && - git remote add --mirror=fetch -t heads/new parent ../parent + ( + cd mirror-fetch/track && + git remote add --mirror=fetch -t heads/new parent ../parent ) ' test_expect_success 'fetch mirror respects specific branches' ' - (cd mirror-fetch/track && - git fetch parent && - git rev-parse --verify refs/heads/new && - test_must_fail git rev-parse --verify refs/heads/renamed + ( + cd mirror-fetch/track && + git fetch parent && + git rev-parse --verify refs/heads/new && + test_must_fail git rev-parse --verify refs/heads/renamed ) ' @@ -366,60 +402,72 @@ test_expect_success 'add --mirror=push' ' mkdir mirror-push && git init --bare mirror-push/public && git init mirror-push/private && - (cd mirror-push/private && - test_commit one && - git remote add --mirror=push public ../public + ( + cd mirror-push/private && + test_commit one && + git remote add --mirror=push public ../public ) ' test_expect_success 'push mirrors act as mirrors during push' ' - (cd mirror-push/private && - git branch new && - git branch -m master renamed && - git push public + ( + cd mirror-push/private && + git branch new && + git branch -m master renamed && + git push public ) && - (cd mirror-push/private && - git rev-parse --verify refs/heads/new && - git rev-parse --verify refs/heads/renamed && - test_must_fail git rev-parse --verify refs/heads/master + ( + cd mirror-push/private && + git rev-parse --verify refs/heads/new && + git rev-parse --verify refs/heads/renamed && + test_must_fail git rev-parse --verify refs/heads/master ) ' test_expect_success 'push mirrors do not act as mirrors during fetch' ' - (cd mirror-push/public && - git branch -m renamed renamed2 && - git symbolic-ref HEAD refs/heads/renamed2 + ( + cd mirror-push/public && + git branch -m renamed renamed2 && + git symbolic-ref HEAD refs/heads/renamed2 ) && - (cd mirror-push/private && - git fetch public && - git rev-parse --verify refs/heads/renamed && - test_must_fail git rev-parse --verify refs/heads/renamed2 + ( + cd mirror-push/private && + git fetch public && + git rev-parse --verify refs/heads/renamed && + test_must_fail git rev-parse --verify refs/heads/renamed2 ) ' test_expect_success 'push mirrors do not allow you to specify refs' ' git init mirror-push/track && - (cd mirror-push/track && - test_must_fail git remote add --mirror=push -t new public ../public + ( + cd mirror-push/track && + test_must_fail git remote add --mirror=push -t new public ../public ) ' test_expect_success 'add alt && prune' ' - (mkdir alttst && - cd alttst && - git init && - git remote add -f origin ../one && - git config remote.alt.url ../one && - git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*") && - (cd one && - git branch -m side side2) && - (cd alttst && - git rev-parse --verify refs/remotes/origin/side && - test_must_fail git rev-parse --verify refs/remotes/origin/side2 && - git fetch alt && - git remote prune alt && - test_must_fail git rev-parse --verify refs/remotes/origin/side && - git rev-parse --verify refs/remotes/origin/side2) + mkdir alttst && + ( + cd alttst && + git init && + git remote add -f origin ../one && + git config remote.alt.url ../one && + git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*" + ) && + ( + cd one && + git branch -m side side2 + ) && + ( + cd alttst && + git rev-parse --verify refs/remotes/origin/side && + test_must_fail git rev-parse --verify refs/remotes/origin/side2 && + git fetch alt && + git remote prune alt && + test_must_fail git rev-parse --verify refs/remotes/origin/side && + git rev-parse --verify refs/remotes/origin/side2 + ) ' cat >test/expect <<\EOF @@ -427,20 +475,24 @@ some-tag EOF test_expect_success 'add with reachable tags (default)' ' - (cd one && - >foobar && - git add foobar && - git commit -m "Foobar" && - git tag -a -m "Foobar tag" foobar-tag && - git reset --hard HEAD~1 && - git tag -a -m "Some tag" some-tag) && - (mkdir add-tags && - cd add-tags && - git init && - git remote add -f origin ../one && - git tag -l some-tag >../test/output && - git tag -l foobar-tag >>../test/output && - test_must_fail git config remote.origin.tagopt) && + ( + cd one && + >foobar && + git add foobar && + git commit -m "Foobar" && + git tag -a -m "Foobar tag" foobar-tag && + git reset --hard HEAD~1 && + git tag -a -m "Some tag" some-tag + ) && + mkdir add-tags && + ( + cd add-tags && + git init && + git remote add -f origin ../one && + git tag -l some-tag >../test/output && + git tag -l foobar-tag >>../test/output && + test_must_fail git config remote.origin.tagopt + ) && test_cmp test/expect test/output ' @@ -451,14 +503,16 @@ foobar-tag EOF test_expect_success 'add --tags' ' - (rm -rf add-tags && - mkdir add-tags && - cd add-tags && - git init && - git remote add -f --tags origin ../one && - git tag -l some-tag >../test/output && - git tag -l foobar-tag >>../test/output && - git config remote.origin.tagopt >>../test/output) && + rm -rf add-tags && + ( + mkdir add-tags && + cd add-tags && + git init && + git remote add -f --tags origin ../one && + git tag -l some-tag >../test/output && + git tag -l foobar-tag >>../test/output && + git config remote.origin.tagopt >>../test/output + ) && test_cmp test/expect test/output ' @@ -467,25 +521,31 @@ cat >test/expect <<\EOF EOF test_expect_success 'add --no-tags' ' - (rm -rf add-tags && - mkdir add-no-tags && - cd add-no-tags && - git init && - git remote add -f --no-tags origin ../one && - git tag -l some-tag >../test/output && - git tag -l foobar-tag >../test/output && - git config remote.origin.tagopt >>../test/output) && - (cd one && - git tag -d some-tag foobar-tag) && + rm -rf add-tags && + ( + mkdir add-no-tags && + cd add-no-tags && + git init && + git remote add -f --no-tags origin ../one && + git tag -l some-tag >../test/output && + git tag -l foobar-tag >../test/output && + git config remote.origin.tagopt >>../test/output + ) && + ( + cd one && + git tag -d some-tag foobar-tag + ) && test_cmp test/expect test/output ' test_expect_success 'reject --no-no-tags' ' - (cd add-no-tags && - test_must_fail git remote add -f --no-no-tags neworigin ../one) + ( + cd add-no-tags && + test_must_fail git remote add -f --no-no-tags neworigin ../one + ) ' -cat > one/expect << EOF +cat >one/expect <<\EOF apis/master apis/side drosophila/another @@ -494,17 +554,17 @@ cat > one/expect << EOF EOF test_expect_success 'update' ' - - (cd one && - git remote add drosophila ../two && - git remote add apis ../mirror && - git remote update && - git branch -r > output && - test_cmp expect output) - + ( + cd one && + git remote add drosophila ../two && + git remote add apis ../mirror && + git remote update && + git branch -r >output && + test_cmp expect output + ) ' -cat > one/expect << EOF +cat >one/expect <<\EOF drosophila/another drosophila/master drosophila/side @@ -515,34 +575,40 @@ cat > one/expect << EOF EOF test_expect_success 'update with arguments' ' - - (cd one && - for b in $(git branch -r) - do + ( + cd one && + for b in $(git branch -r) + do git branch -r -d $b || break - done && - git remote add manduca ../mirror && - git remote add megaloprepus ../mirror && - git config remotes.phobaeticus "drosophila megaloprepus" && - git config remotes.titanus manduca && - git remote update phobaeticus titanus && - git branch -r > output && - test_cmp expect output) - + done && + git remote add manduca ../mirror && + git remote add megaloprepus ../mirror && + git config remotes.phobaeticus "drosophila megaloprepus" && + git config remotes.titanus manduca && + git remote update phobaeticus titanus && + git branch -r >output && + test_cmp expect output + ) ' test_expect_success 'update --prune' ' - - (cd one && - git branch -m side2 side3) && - (cd test && - git remote update --prune && - (cd ../one && git branch -m side3 side2) && - git rev-parse refs/remotes/origin/side3 && - test_must_fail git rev-parse refs/remotes/origin/side2) + ( + cd one && + git branch -m side2 side3 + ) && + ( + cd test && + git remote update --prune && + ( + cd ../one && + git branch -m side3 side2 + ) && + git rev-parse refs/remotes/origin/side3 && + test_must_fail git rev-parse refs/remotes/origin/side2 + ) ' -cat > one/expect << EOF +cat >one/expect <<-\EOF apis/master apis/side manduca/master @@ -552,176 +618,204 @@ cat > one/expect << EOF EOF test_expect_success 'update default' ' - - (cd one && - for b in $(git branch -r) - do + ( + cd one && + for b in $(git branch -r) + do git branch -r -d $b || break - done && - git config remote.drosophila.skipDefaultUpdate true && - git remote update default && - git branch -r > output && - test_cmp expect output) - + done && + git config remote.drosophila.skipDefaultUpdate true && + git remote update default && + git branch -r >output && + test_cmp expect output + ) ' -cat > one/expect << EOF +cat >one/expect <<\EOF drosophila/another drosophila/master drosophila/side EOF test_expect_success 'update default (overridden, with funny whitespace)' ' - - (cd one && - for b in $(git branch -r) - do + ( + cd one && + for b in $(git branch -r) + do git branch -r -d $b || break - done && - git config remotes.default "$(printf "\t drosophila \n")" && - git remote update default && - git branch -r > output && - test_cmp expect output) - + done && + git config remotes.default "$(printf "\t drosophila \n")" && + git remote update default && + git branch -r >output && + test_cmp expect output + ) ' test_expect_success 'update (with remotes.default defined)' ' - - (cd one && - for b in $(git branch -r) - do + ( + cd one && + for b in $(git branch -r) + do git branch -r -d $b || break - done && - git config remotes.default "drosophila" && - git remote update && - git branch -r > output && - test_cmp expect output) - + done && + git config remotes.default "drosophila" && + git remote update && + git branch -r >output && + test_cmp expect output + ) ' test_expect_success '"remote show" does not show symbolic refs' ' - git clone one three && - (cd three && - git remote show origin > output && - ! grep "^ *HEAD$" < output && - ! grep -i stale < output) - + ( + cd three && + git remote show origin >output && + ! grep "^ *HEAD$" < output && + ! grep -i stale < output + ) ' test_expect_success 'reject adding remote with an invalid name' ' - test_must_fail git remote add some:url desired-name - ' # The first three test if the tracking branches are properly renamed, # the last two ones check if the config is updated. test_expect_success 'rename a remote' ' - git clone one four && - (cd four && - git remote rename origin upstream && - rmdir .git/refs/remotes/origin && - test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" && - test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" && - test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" && - test "$(git config branch.master.remote)" = "upstream") - + ( + cd four && + git remote rename origin upstream && + rmdir .git/refs/remotes/origin && + test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" && + test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" && + test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" && + test "$(git config branch.master.remote)" = "upstream" + ) ' test_expect_success 'rename does not update a non-default fetch refspec' ' - git clone one four.one && - (cd four.one && - git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* && - git remote rename origin upstream && - test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" && - git rev-parse -q origin/master) - + ( + cd four.one && + git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* && + git remote rename origin upstream && + test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" && + git rev-parse -q origin/master + ) ' test_expect_success 'rename a remote with name part of fetch spec' ' - git clone one four.two && - (cd four.two && - git remote rename origin remote && - git remote rename remote upstream && - test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*") - + ( + cd four.two && + git remote rename origin remote && + git remote rename remote upstream && + test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" + ) ' test_expect_success 'rename a remote with name prefix of other remote' ' - git clone one four.three && - (cd four.three && - git remote add o git://example.com/repo.git && - git remote rename o upstream && - test "$(git rev-parse origin/master)" = "$(git rev-parse master)") - + ( + cd four.three && + git remote add o git://example.com/repo.git && + git remote rename o upstream && + test "$(git rev-parse origin/master)" = "$(git rev-parse master)" + ) ' -cat > remotes_origin << EOF +cat >remotes_origin <<EOF URL: $(pwd)/one Push: refs/heads/master:refs/heads/upstream +Push: refs/heads/next:refs/heads/upstream2 Pull: refs/heads/master:refs/heads/origin +Pull: refs/heads/next:refs/heads/origin2 EOF test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' ' git clone one five && origin_url=$(pwd)/one && - (cd five && - git remote rm origin && - mkdir -p .git/remotes && - cat ../remotes_origin > .git/remotes/origin && - git remote rename origin origin && - ! test -f .git/remotes/origin && - test "$(git config remote.origin.url)" = "$origin_url" && - test "$(git config remote.origin.push)" = "refs/heads/master:refs/heads/upstream" && - test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin") + ( + cd five && + git remote remove origin && + mkdir -p .git/remotes && + cat ../remotes_origin >.git/remotes/origin && + git remote rename origin origin && + test_path_is_missing .git/remotes/origin && + test "$(git config remote.origin.url)" = "$origin_url" && + cat >push_expected <<-\EOF && + refs/heads/master:refs/heads/upstream + refs/heads/next:refs/heads/upstream2 + EOF + cat >fetch_expected <<-\EOF && + refs/heads/master:refs/heads/origin + refs/heads/next:refs/heads/origin2 + EOF + git config --get-all remote.origin.push >push_actual && + git config --get-all remote.origin.fetch >fetch_actual && + test_cmp push_expected push_actual && + test_cmp fetch_expected fetch_actual + ) ' test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' ' git clone one six && origin_url=$(pwd)/one && - (cd six && - git remote rm origin && - echo "$origin_url" > .git/branches/origin && - git remote rename origin origin && - ! test -f .git/branches/origin && - test "$(git config remote.origin.url)" = "$origin_url" && - test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin") + ( + cd six && + git remote rm origin && + echo "$origin_url" >.git/branches/origin && + git remote rename origin origin && + test_path_is_missing .git/branches/origin && + test "$(git config remote.origin.url)" = "$origin_url" && + test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin" && + test "$(git config remote.origin.push)" = "HEAD:refs/heads/master" + ) ' -test_expect_success 'remote prune to cause a dangling symref' ' +test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' ' git clone one seven && ( + cd seven && + git remote rm origin && + echo "quux#foom" > .git/branches/origin && + git remote rename origin origin && + test_path_is_missing .git/branches/origin && + test "$(git config remote.origin.url)" = "quux" && + test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin" + test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom" + ) +' + +test_expect_success 'remote prune to cause a dangling symref' ' + git clone one eight && + ( cd one && git checkout side2 && git branch -D master ) && ( - cd seven && + cd eight && git remote prune origin ) >err 2>&1 && - grep "has become dangling" err && + test_i18ngrep "has become dangling" err && : And the dangling symref will not cause other annoying errors && ( - cd seven && + cd eight && git branch -a ) 2>err && ! grep "points nowhere" err && ( - cd seven && + cd eight && test_must_fail git branch nomore origin ) 2>err && grep "dangling symref" err ' test_expect_success 'show empty remote' ' - test_create_repo empty && git clone empty empty-clone && ( @@ -997,4 +1091,26 @@ test_expect_success 'remote set-url --delete baz' ' cmp expect actual ' +test_expect_success 'extra args: setup' ' + # add a dummy origin so that this does not trigger failure + git remote add origin . +' + +test_extra_arg () { + test_expect_success "extra args: $*" " + test_must_fail git remote $* bogus_extra_arg 2>actual && + grep '^usage:' actual + " +} + +test_extra_arg add nick url +test_extra_arg rename origin newname +test_extra_arg remove origin +test_extra_arg set-head origin master +# set-branches takes any number of args +test_extra_arg set-url origin newurl oldurl +# show takes any number of args +# prune takes any number of args +# update takes any number of args + test_done diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh index 308c02ea75..1f0f8e6827 100755 --- a/t/t5510-fetch.sh +++ b/t/t5510-fetch.sh @@ -162,6 +162,36 @@ test_expect_success 'fetch following tags' ' ' +test_expect_success 'fetch uses remote ref names to describe new refs' ' + cd "$D" && + git init descriptive && + ( + cd descriptive && + git config remote.o.url .. && + git config remote.o.fetch "refs/heads/*:refs/crazyheads/*" && + git config --add remote.o.fetch "refs/others/*:refs/heads/*" && + git fetch o + ) && + git tag -a -m "Descriptive tag" descriptive-tag && + git branch descriptive-branch && + git checkout descriptive-branch && + echo "Nuts" >crazy && + git add crazy && + git commit -a -m "descriptive commit" && + git update-ref refs/others/crazy HEAD && + ( + cd descriptive && + git fetch o 2>actual && + grep " -> refs/crazyheads/descriptive-branch$" actual | + test_i18ngrep "new branch" && + grep " -> descriptive-tag$" actual | + test_i18ngrep "new tag" && + grep " -> crazy$" actual | + test_i18ngrep "new ref" + ) && + git checkout master +' + test_expect_success 'fetch must not resolve short tag name' ' cd "$D" && @@ -340,30 +370,39 @@ test_expect_success 'bundle should record HEAD correctly' ' ' -test_expect_success 'explicit fetch should not update tracking' ' +test_expect_success 'mark initial state of origin/master' ' + ( + cd three && + git tag base-origin-master refs/remotes/origin/master + ) +' + +test_expect_success 'explicit fetch should update tracking' ' cd "$D" && git branch -f side && ( cd three && + git update-ref refs/remotes/origin/master base-origin-master && o=$(git rev-parse --verify refs/remotes/origin/master) && git fetch origin master && n=$(git rev-parse --verify refs/remotes/origin/master) && - test "$o" = "$n" && + test "$o" != "$n" && test_must_fail git rev-parse --verify refs/remotes/origin/side ) ' -test_expect_success 'explicit pull should not update tracking' ' +test_expect_success 'explicit pull should update tracking' ' cd "$D" && git branch -f side && ( cd three && + git update-ref refs/remotes/origin/master base-origin-master && o=$(git rev-parse --verify refs/remotes/origin/master) && git pull origin master && n=$(git rev-parse --verify refs/remotes/origin/master) && - test "$o" = "$n" && + test "$o" != "$n" && test_must_fail git rev-parse --verify refs/remotes/origin/side ) ' @@ -374,6 +413,7 @@ test_expect_success 'configured fetch updates tracking' ' git branch -f side && ( cd three && + git update-ref refs/remotes/origin/master base-origin-master && o=$(git rev-parse --verify refs/remotes/origin/master) && git fetch origin && n=$(git rev-parse --verify refs/remotes/origin/master) && @@ -382,6 +422,22 @@ test_expect_success 'configured fetch updates tracking' ' ) ' +test_expect_success 'non-matching refspecs do not confuse tracking update' ' + cd "$D" && + git update-ref refs/odd/location HEAD && + ( + cd three && + git update-ref refs/remotes/origin/master base-origin-master && + git config --add remote.origin.fetch \ + refs/odd/location:refs/remotes/origin/odd && + o=$(git rev-parse --verify refs/remotes/origin/master) && + git fetch origin master && + n=$(git rev-parse --verify refs/remotes/origin/master) && + test "$o" != "$n" && + test_must_fail git rev-parse --verify refs/remotes/origin/odd + ) +' + test_expect_success 'pushing nonexistent branch by mistake should not segv' ' cd "$D" && @@ -441,6 +497,88 @@ test_expect_success "should be able to fetch with duplicate refspecs" ' ) ' +# configured prune tests + +set_config_tristate () { + # var=$1 val=$2 + case "$2" in + unset) test_unconfig "$1" ;; + *) git config "$1" "$2" ;; + esac +} + +test_configured_prune () { + fetch_prune=$1 remote_origin_prune=$2 cmdline=$3 expected=$4 + + test_expect_success "prune fetch.prune=$1 remote.origin.prune=$2${3:+ $3}; $4" ' + # make sure a newbranch is there in . and also in one + git branch -f newbranch && + ( + cd one && + test_unconfig fetch.prune && + test_unconfig remote.origin.prune && + git fetch && + git rev-parse --verify refs/remotes/origin/newbranch + ) + + # now remove it + git branch -d newbranch && + + # then test + ( + cd one && + set_config_tristate fetch.prune $fetch_prune && + set_config_tristate remote.origin.prune $remote_origin_prune && + + git fetch $cmdline && + case "$expected" in + pruned) + test_must_fail git rev-parse --verify refs/remotes/origin/newbranch + ;; + kept) + git rev-parse --verify refs/remotes/origin/newbranch + ;; + esac + ) + ' +} + +test_configured_prune unset unset "" kept +test_configured_prune unset unset "--no-prune" kept +test_configured_prune unset unset "--prune" pruned + +test_configured_prune false unset "" kept +test_configured_prune false unset "--no-prune" kept +test_configured_prune false unset "--prune" pruned + +test_configured_prune true unset "" pruned +test_configured_prune true unset "--prune" pruned +test_configured_prune true unset "--no-prune" kept + +test_configured_prune unset false "" kept +test_configured_prune unset false "--no-prune" kept +test_configured_prune unset false "--prune" pruned + +test_configured_prune false false "" kept +test_configured_prune false false "--no-prune" kept +test_configured_prune false false "--prune" pruned + +test_configured_prune true false "" kept +test_configured_prune true false "--prune" pruned +test_configured_prune true false "--no-prune" kept + +test_configured_prune unset true "" pruned +test_configured_prune unset true "--no-prune" kept +test_configured_prune unset true "--prune" pruned + +test_configured_prune false true "" pruned +test_configured_prune false true "--no-prune" kept +test_configured_prune false true "--prune" pruned + +test_configured_prune true true "" pruned +test_configured_prune true true "--prune" pruned +test_configured_prune true true "--no-prune" kept + test_expect_success 'all boundary commits are excluded' ' test_commit base && test_commit oneside && diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 6764d511ce..321c3e5234 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -87,17 +87,15 @@ test_expect_success 'use branch.<name>.remote if possible' ' test_expect_success 'confuses pattern as remote when no remote specified' ' cat >exp <<-\EOF && fatal: '\''refs*master'\'' does not appear to be a git repository - fatal: The remote end hung up unexpectedly + fatal: Could not read from remote repository. + + Please make sure you have the correct access rights + and the repository exists. EOF # - # Do not expect "git ls-remote <pattern>" to work; ls-remote, correctly, - # confuses <pattern> for <remote>. Although ugly, this behaviour is akin - # to the confusion of refspecs for remotes by git-fetch and git-push, - # eg: - # - # $ git fetch branch - # - + # Do not expect "git ls-remote <pattern>" to work; ls-remote needs + # <remote> if you want to feed <pattern>, just like you cannot say + # fetch <branch>. # We could just as easily have used "master"; the "*" emphasizes its # role as a pattern. test_must_fail git ls-remote refs*master >actual 2>&1 && @@ -128,4 +126,16 @@ test_expect_success 'Report match with --exit-code' ' test_cmp expect actual ' +for configsection in transfer uploadpack +do + test_expect_success "Hide some refs with $configsection.hiderefs" ' + test_config $configsection.hiderefs refs/tags && + git ls-remote . >actual && + test_unconfig $configsection.hiderefs && + git ls-remote . | + sed -e "/ refs\/tags\//d" >expect && + test_cmp expect actual + ' +done + test_done diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh index 227dd56137..0f8140957f 100755 --- a/t/t5514-fetch-multiple.sh +++ b/t/t5514-fetch-multiple.sh @@ -151,4 +151,34 @@ test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' ' test_cmp ../expect output) ' +test_expect_success 'git fetch --all --no-tags' ' + >expect && + git clone one test5 && + git clone test5 test6 && + (cd test5 && git tag test-tag) && + ( + cd test6 && + git fetch --all --no-tags && + git tag >output + ) && + test_cmp expect test6/output +' + +test_expect_success 'git fetch --all --tags' ' + echo test-tag >expect && + git clone one test7 && + git clone test7 test8 && + ( + cd test7 && + test_commit test-tag && + git reset --hard HEAD^ + ) && + ( + cd test8 && + git fetch --all --tags && + git tag >output + ) && + test_cmp expect test8/output +' + test_done diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index b69cf574d7..99c32d7539 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1,16 +1,28 @@ #!/bin/sh -test_description='fetching and pushing, with or without wildcard' +test_description='Basic fetch/push functionality. + +This test checks the following functionality: + +* command-line syntax +* refspecs +* fast-forward detection, and overriding it +* configuration +* hooks +* --porcelain output format +* hiderefs +' . ./test-lib.sh D=`pwd` mk_empty () { - rm -fr testrepo && - mkdir testrepo && + repo_name="$1" + rm -fr "$repo_name" && + mkdir "$repo_name" && ( - cd testrepo && + cd "$repo_name" && git init && git config receive.denyCurrentBranch warn && mv .git/hooks .git/hooks-disabled @@ -18,32 +30,33 @@ mk_empty () { } mk_test () { - mk_empty && + repo_name="$1" + shift + + mk_empty "$repo_name" && ( for ref in "$@" do - git push testrepo $the_first_commit:refs/$ref || { - echo "Oops, push refs/$ref failure" - exit 1 - } + git push "$repo_name" $the_first_commit:refs/$ref || + exit done && - cd testrepo && + cd "$repo_name" && for ref in "$@" do - r=$(git show-ref -s --verify refs/$ref) && - test "z$r" = "z$the_first_commit" || { - echo "Oops, refs/$ref is wrong" - exit 1 - } + echo "$the_first_commit" >expect && + git show-ref -s --verify refs/$ref >actual && + test_cmp expect actual || + exit done && git fsck --full ) } mk_test_with_hooks() { + repo_name=$1 mk_test "$@" && ( - cd testrepo && + cd "$repo_name" && mkdir .git/hooks && cd .git/hooks && @@ -75,22 +88,23 @@ mk_test_with_hooks() { } mk_child() { - rm -rf "$1" && - git clone testrepo "$1" + rm -rf "$2" && + git clone "$1" "$2" } check_push_result () { + repo_name="$1" + shift + ( - cd testrepo && - it="$1" && - shift + cd "$repo_name" && + echo "$1" >expect && + shift && for ref in "$@" do - r=$(git show-ref -s --verify refs/$ref) && - test "z$r" = "z$it" || { - echo "Oops, refs/$ref is wrong" - exit 1 - } + git show-ref -s --verify refs/$ref >actual && + test_cmp expect actual || + exit done && git fsck --full ) @@ -113,35 +127,33 @@ test_expect_success setup ' ' test_expect_success 'fetch without wildcard' ' - mk_empty && + mk_empty testrepo && ( cd testrepo && git fetch .. refs/heads/master:refs/remotes/origin/master && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) ' test_expect_success 'fetch with wildcard' ' - mk_empty && + mk_empty testrepo && ( cd testrepo && git config remote.up.url .. && git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" && git fetch up && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) ' test_expect_success 'fetch with insteadOf' ' - mk_empty && + mk_empty testrepo && ( TRASH=$(pwd)/ && cd testrepo && @@ -150,15 +162,14 @@ test_expect_success 'fetch with insteadOf' ' git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" && git fetch up && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) ' test_expect_success 'fetch with pushInsteadOf (should not rewrite)' ' - mk_empty && + mk_empty testrepo && ( TRASH=$(pwd)/ && cd testrepo && @@ -167,321 +178,310 @@ test_expect_success 'fetch with pushInsteadOf (should not rewrite)' ' git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" && git fetch up && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) ' test_expect_success 'push without wildcard' ' - mk_empty && + mk_empty testrepo && git push testrepo refs/heads/master:refs/remotes/origin/master && ( cd testrepo && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) ' test_expect_success 'push with wildcard' ' - mk_empty && + mk_empty testrepo && git push testrepo "refs/heads/*:refs/remotes/origin/*" && ( cd testrepo && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) ' test_expect_success 'push with insteadOf' ' - mk_empty && + mk_empty testrepo && TRASH="$(pwd)/" && - git config "url.$TRASH.insteadOf" trash/ && + test_config "url.$TRASH.insteadOf" trash/ && git push trash/testrepo refs/heads/master:refs/remotes/origin/master && ( cd testrepo && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) ' test_expect_success 'push with pushInsteadOf' ' - mk_empty && + mk_empty testrepo && TRASH="$(pwd)/" && - git config "url.$TRASH.pushInsteadOf" trash/ && + test_config "url.$TRASH.pushInsteadOf" trash/ && git push trash/testrepo refs/heads/master:refs/remotes/origin/master && ( cd testrepo && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) ' test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' ' - mk_empty && - TRASH="$(pwd)/" && - git config "url.trash2/.pushInsteadOf" trash/ && - git config remote.r.url trash/wrong && - git config remote.r.pushurl "$TRASH/testrepo" && + mk_empty testrepo && + test_config "url.trash2/.pushInsteadOf" testrepo/ && + test_config "url.trash3/.pusnInsteadOf" trash/wrong && + test_config remote.r.url trash/wrong && + test_config remote.r.pushurl "testrepo/" && git push r refs/heads/master:refs/remotes/origin/master && ( cd testrepo && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) ' test_expect_success 'push with matching heads' ' - mk_test heads/master && - git push testrepo && - check_push_result $the_commit heads/master + mk_test testrepo heads/master && + git push testrepo : && + check_push_result testrepo $the_commit heads/master ' test_expect_success 'push with matching heads on the command line' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo : && - check_push_result $the_commit heads/master + check_push_result testrepo $the_commit heads/master ' test_expect_success 'failed (non-fast-forward) push with matching heads' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo : && git commit --amend -massaged && test_must_fail git push testrepo && - check_push_result $the_commit heads/master && + check_push_result testrepo $the_commit heads/master && git reset --hard $the_commit ' test_expect_success 'push --force with matching heads' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo : && git commit --amend -massaged && - git push --force testrepo && - ! check_push_result $the_commit heads/master && + git push --force testrepo : && + ! check_push_result testrepo $the_commit heads/master && git reset --hard $the_commit ' test_expect_success 'push with matching heads and forced update' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo : && git commit --amend -massaged && git push testrepo +: && - ! check_push_result $the_commit heads/master && + ! check_push_result testrepo $the_commit heads/master && git reset --hard $the_commit ' test_expect_success 'push with no ambiguity (1)' ' - mk_test heads/master && + mk_test testrepo heads/master && git push testrepo master:master && - check_push_result $the_commit heads/master + check_push_result testrepo $the_commit heads/master ' test_expect_success 'push with no ambiguity (2)' ' - mk_test remotes/origin/master && + mk_test testrepo remotes/origin/master && git push testrepo master:origin/master && - check_push_result $the_commit remotes/origin/master + check_push_result testrepo $the_commit remotes/origin/master ' test_expect_success 'push with colon-less refspec, no ambiguity' ' - mk_test heads/master heads/t/master && + mk_test testrepo heads/master heads/t/master && git branch -f t/master master && git push testrepo master && - check_push_result $the_commit heads/master && - check_push_result $the_first_commit heads/t/master + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit heads/t/master ' test_expect_success 'push with weak ambiguity (1)' ' - mk_test heads/master remotes/origin/master && + mk_test testrepo heads/master remotes/origin/master && git push testrepo master:master && - check_push_result $the_commit heads/master && - check_push_result $the_first_commit remotes/origin/master + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit remotes/origin/master ' test_expect_success 'push with weak ambiguity (2)' ' - mk_test heads/master remotes/origin/master remotes/another/master && + mk_test testrepo heads/master remotes/origin/master remotes/another/master && git push testrepo master:master && - check_push_result $the_commit heads/master && - check_push_result $the_first_commit remotes/origin/master remotes/another/master + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit remotes/origin/master remotes/another/master ' test_expect_success 'push with ambiguity' ' - mk_test heads/frotz tags/frotz && - if git push testrepo master:frotz - then - echo "Oops, should have failed" - false - else - check_push_result $the_first_commit heads/frotz tags/frotz - fi + mk_test testrepo heads/frotz tags/frotz && + test_must_fail git push testrepo master:frotz && + check_push_result testrepo $the_first_commit heads/frotz tags/frotz ' test_expect_success 'push with colon-less refspec (1)' ' - mk_test heads/frotz tags/frotz && + mk_test testrepo heads/frotz tags/frotz && git branch -f frotz master && git push testrepo frotz && - check_push_result $the_commit heads/frotz && - check_push_result $the_first_commit tags/frotz + check_push_result testrepo $the_commit heads/frotz && + check_push_result testrepo $the_first_commit tags/frotz ' test_expect_success 'push with colon-less refspec (2)' ' - mk_test heads/frotz tags/frotz && + mk_test testrepo heads/frotz tags/frotz && if git show-ref --verify -q refs/heads/frotz then git branch -D frotz fi && git tag -f frotz && - git push testrepo frotz && - check_push_result $the_commit tags/frotz && - check_push_result $the_first_commit heads/frotz + git push -f testrepo frotz && + check_push_result testrepo $the_commit tags/frotz && + check_push_result testrepo $the_first_commit heads/frotz ' test_expect_success 'push with colon-less refspec (3)' ' - mk_test && + mk_test testrepo && if git show-ref --verify -q refs/tags/frotz then git tag -d frotz fi && git branch -f frotz master && git push testrepo frotz && - check_push_result $the_commit heads/frotz && + check_push_result testrepo $the_commit heads/frotz && test 1 = $( cd testrepo && git show-ref | wc -l ) ' test_expect_success 'push with colon-less refspec (4)' ' - mk_test && + mk_test testrepo && if git show-ref --verify -q refs/heads/frotz then git branch -D frotz fi && git tag -f frotz && git push testrepo frotz && - check_push_result $the_commit tags/frotz && + check_push_result testrepo $the_commit tags/frotz && test 1 = $( cd testrepo && git show-ref | wc -l ) ' test_expect_success 'push head with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && git push testrepo master:branch && - check_push_result $the_commit heads/branch + check_push_result testrepo $the_commit heads/branch ' test_expect_success 'push tag with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && git tag -f v1.0 && git push testrepo v1.0:tag && - check_push_result $the_commit tags/tag + check_push_result testrepo $the_commit tags/tag ' test_expect_success 'push sha1 with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && test_must_fail git push testrepo `git rev-parse master`:foo ' test_expect_success 'push ref expression with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && test_must_fail git push testrepo master^:branch ' test_expect_success 'push with HEAD' ' - mk_test heads/master && + mk_test testrepo heads/master && git checkout master && git push testrepo HEAD && - check_push_result $the_commit heads/master + check_push_result testrepo $the_commit heads/master ' test_expect_success 'push with HEAD nonexisting at remote' ' - mk_test heads/master && + mk_test testrepo heads/master && git checkout -b local master && git push testrepo HEAD && - check_push_result $the_commit heads/local + check_push_result testrepo $the_commit heads/local ' test_expect_success 'push with +HEAD' ' - mk_test heads/master && + mk_test testrepo heads/master && git checkout master && git branch -D local && git checkout -b local && git push testrepo master local && - check_push_result $the_commit heads/master && - check_push_result $the_commit heads/local && + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_commit heads/local && # Without force rewinding should fail git reset --hard HEAD^ && test_must_fail git push testrepo HEAD && - check_push_result $the_commit heads/local && + check_push_result testrepo $the_commit heads/local && # With force rewinding should succeed git push testrepo +HEAD && - check_push_result $the_first_commit heads/local + check_push_result testrepo $the_first_commit heads/local ' test_expect_success 'push HEAD with non-existent, incomplete dest' ' - mk_test && + mk_test testrepo && git checkout master && git push testrepo HEAD:branch && - check_push_result $the_commit heads/branch + check_push_result testrepo $the_commit heads/branch ' test_expect_success 'push with config remote.*.push = HEAD' ' - mk_test heads/local && + mk_test testrepo heads/local && git checkout master && git branch -f local $the_commit && ( @@ -489,46 +489,68 @@ test_expect_success 'push with config remote.*.push = HEAD' ' git checkout local && git reset --hard $the_first_commit ) && - git config remote.there.url testrepo && - git config remote.there.push HEAD && - git config branch.master.remote there && + test_config remote.there.url testrepo && + test_config remote.there.push HEAD && + test_config branch.master.remote there && git push && - check_push_result $the_commit heads/master && - check_push_result $the_first_commit heads/local + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit heads/local +' + +test_expect_success 'push with remote.pushdefault' ' + mk_test up_repo heads/master && + mk_test down_repo heads/master && + test_config remote.up.url up_repo && + test_config remote.down.url down_repo && + test_config branch.master.remote up && + test_config remote.pushdefault down && + test_config push.default matching && + git push && + check_push_result up_repo $the_first_commit heads/master && + check_push_result down_repo $the_commit heads/master ' -# clean up the cruft left with the previous one -git config --remove-section remote.there -git config --remove-section branch.master - test_expect_success 'push with config remote.*.pushurl' ' - mk_test heads/master && + mk_test testrepo heads/master && git checkout master && - git config remote.there.url test2repo && - git config remote.there.pushurl testrepo && - git push there && - check_push_result $the_commit heads/master + test_config remote.there.url test2repo && + test_config remote.there.pushurl testrepo && + git push there : && + check_push_result testrepo $the_commit heads/master +' + +test_expect_success 'push with config branch.*.pushremote' ' + mk_test up_repo heads/master && + mk_test side_repo heads/master && + mk_test down_repo heads/master && + test_config remote.up.url up_repo && + test_config remote.pushdefault side_repo && + test_config remote.down.url down_repo && + test_config branch.master.remote up && + test_config branch.master.pushremote down && + test_config push.default matching && + git push && + check_push_result up_repo $the_first_commit heads/master && + check_push_result side_repo $the_first_commit heads/master && + check_push_result down_repo $the_commit heads/master ' -# clean up the cruft left with the previous one -git config --remove-section remote.there - test_expect_success 'push with dry-run' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && old_commit=$(git show-ref -s --verify refs/heads/master) ) && - git push --dry-run testrepo && - check_push_result $old_commit heads/master + git push --dry-run testrepo : && + check_push_result testrepo $old_commit heads/master ' test_expect_success 'push updates local refs' ' - mk_test heads/master && - mk_child child && + mk_test testrepo heads/master && + mk_child testrepo child && ( cd child && git pull .. master && @@ -541,9 +563,9 @@ test_expect_success 'push updates local refs' ' test_expect_success 'push updates up-to-date local refs' ' - mk_test heads/master && - mk_child child1 && - mk_child child2 && + mk_test testrepo heads/master && + mk_child testrepo child1 && + mk_child testrepo child2 && (cd child1 && git pull .. master && git push) && ( cd child2 && @@ -557,8 +579,8 @@ test_expect_success 'push updates up-to-date local refs' ' test_expect_success 'push preserves up-to-date packed refs' ' - mk_test heads/master && - mk_child child && + mk_test testrepo heads/master && + mk_child testrepo child && ( cd child && git push && @@ -569,8 +591,8 @@ test_expect_success 'push preserves up-to-date packed refs' ' test_expect_success 'push does not update local refs on failure' ' - mk_test heads/master && - mk_child child && + mk_test testrepo heads/master && + mk_child testrepo child && mkdir testrepo/.git/hooks && echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive && chmod +x testrepo/.git/hooks/pre-receive && @@ -586,7 +608,7 @@ test_expect_success 'push does not update local refs on failure' ' test_expect_success 'allow deleting an invalid remote ref' ' - mk_test heads/master && + mk_test testrepo heads/master && rm -f testrepo/.git/objects/??/* && git push testrepo :refs/heads/master && (cd testrepo && test_must_fail git rev-parse --verify refs/heads/master) @@ -594,7 +616,7 @@ test_expect_success 'allow deleting an invalid remote ref' ' ' test_expect_success 'pushing valid refs triggers post-receive and post-update hooks' ' - mk_test_with_hooks heads/master heads/next && + mk_test_with_hooks testrepo heads/master heads/next && orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && newmaster=$(git show-ref -s --verify refs/heads/master) && orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) && @@ -630,7 +652,7 @@ test_expect_success 'pushing valid refs triggers post-receive and post-update ho ' test_expect_success 'deleting dangling ref triggers hooks with correct args' ' - mk_test_with_hooks heads/master && + mk_test_with_hooks testrepo heads/master && rm -f testrepo/.git/objects/??/* && git push testrepo :refs/heads/master && ( @@ -659,7 +681,7 @@ test_expect_success 'deleting dangling ref triggers hooks with correct args' ' ' test_expect_success 'deletion of a non-existent ref is not fed to post-receive and post-update hooks' ' - mk_test_with_hooks heads/master && + mk_test_with_hooks testrepo heads/master && orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && newmaster=$(git show-ref -s --verify refs/heads/master) && git push testrepo master :refs/heads/nonexistent && @@ -691,7 +713,7 @@ test_expect_success 'deletion of a non-existent ref is not fed to post-receive a ' test_expect_success 'deletion of a non-existent ref alone does trigger post-receive and post-update hooks' ' - mk_test_with_hooks heads/master && + mk_test_with_hooks testrepo heads/master && git push testrepo :refs/heads/nonexistent && ( cd testrepo/.git && @@ -711,7 +733,7 @@ test_expect_success 'deletion of a non-existent ref alone does trigger post-rece ' test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks with correct input' ' - mk_test_with_hooks heads/master heads/next heads/pu && + mk_test_with_hooks testrepo heads/master heads/next heads/pu && orgmaster=$(cd testrepo && git show-ref -s --verify refs/heads/master) && newmaster=$(git show-ref -s --verify refs/heads/master) && orgnext=$(cd testrepo && git show-ref -s --verify refs/heads/next) && @@ -757,14 +779,14 @@ test_expect_success 'mixed ref updates, deletes, invalid deletes trigger hooks w ' test_expect_success 'allow deleting a ref using --delete' ' - mk_test heads/master && + mk_test testrepo heads/master && (cd testrepo && git config receive.denyDeleteCurrent warn) && git push testrepo --delete master && (cd testrepo && test_must_fail git rev-parse --verify refs/heads/master) ' test_expect_success 'allow deleting a tag using --delete' ' - mk_test heads/master && + mk_test testrepo heads/master && git tag -a -m dummy_message deltag heads/master && git push testrepo --tags && (cd testrepo && git rev-parse --verify -q refs/tags/deltag) && @@ -773,17 +795,17 @@ test_expect_success 'allow deleting a tag using --delete' ' ' test_expect_success 'push --delete without args aborts' ' - mk_test heads/master && + mk_test testrepo heads/master && test_must_fail git push testrepo --delete ' test_expect_success 'push --delete refuses src:dest refspecs' ' - mk_test heads/master && + mk_test testrepo heads/master && test_must_fail git push testrepo --delete master:foo ' test_expect_success 'warn on push to HEAD of non-bare repository' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && git checkout master && @@ -794,7 +816,7 @@ test_expect_success 'warn on push to HEAD of non-bare repository' ' ' test_expect_success 'deny push to HEAD of non-bare repository' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && git checkout master && @@ -804,7 +826,7 @@ test_expect_success 'deny push to HEAD of non-bare repository' ' ' test_expect_success 'allow push to HEAD of bare repository (bare)' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && git checkout master && @@ -816,7 +838,7 @@ test_expect_success 'allow push to HEAD of bare repository (bare)' ' ' test_expect_success 'allow push to HEAD of non-bare repository (config)' ' - mk_test heads/master && + mk_test testrepo heads/master && ( cd testrepo && git checkout master && @@ -827,63 +849,63 @@ test_expect_success 'allow push to HEAD of non-bare repository (config)' ' ' test_expect_success 'fetch with branches' ' - mk_empty && + mk_empty testrepo && git branch second $the_first_commit && git checkout second && echo ".." > testrepo/.git/branches/branch1 && ( cd testrepo && git fetch branch1 && - r=$(git show-ref -s --verify refs/heads/branch1) && - test "z$r" = "z$the_commit" && - test 1 = $(git for-each-ref refs/heads | wc -l) + echo "$the_commit commit refs/heads/branch1" >expect && + git for-each-ref refs/heads >actual && + test_cmp expect actual ) && git checkout master ' test_expect_success 'fetch with branches containing #' ' - mk_empty && + mk_empty testrepo && echo "..#second" > testrepo/.git/branches/branch2 && ( cd testrepo && git fetch branch2 && - r=$(git show-ref -s --verify refs/heads/branch2) && - test "z$r" = "z$the_first_commit" && - test 1 = $(git for-each-ref refs/heads | wc -l) + echo "$the_first_commit commit refs/heads/branch2" >expect && + git for-each-ref refs/heads >actual && + test_cmp expect actual ) && git checkout master ' test_expect_success 'push with branches' ' - mk_empty && + mk_empty testrepo && git checkout second && echo "testrepo" > .git/branches/branch1 && git push branch1 && ( cd testrepo && - r=$(git show-ref -s --verify refs/heads/master) && - test "z$r" = "z$the_first_commit" && - test 1 = $(git for-each-ref refs/heads | wc -l) + echo "$the_first_commit commit refs/heads/master" >expect && + git for-each-ref refs/heads >actual && + test_cmp expect actual ) ' test_expect_success 'push with branches containing #' ' - mk_empty && + mk_empty testrepo && echo "testrepo#branch3" > .git/branches/branch2 && git push branch2 && ( cd testrepo && - r=$(git show-ref -s --verify refs/heads/branch3) && - test "z$r" = "z$the_first_commit" && - test 1 = $(git for-each-ref refs/heads | wc -l) + echo "$the_first_commit commit refs/heads/branch3" >expect && + git for-each-ref refs/heads >actual && + test_cmp expect actual ) && git checkout master ' test_expect_success 'push into aliased refs (consistent)' ' - mk_test heads/master && - mk_child child1 && - mk_child child2 && + mk_test testrepo heads/master && + mk_child testrepo child1 && + mk_child testrepo child2 && ( cd child1 && git branch foo && @@ -903,9 +925,9 @@ test_expect_success 'push into aliased refs (consistent)' ' ' test_expect_success 'push into aliased refs (inconsistent)' ' - mk_test heads/master && - mk_child child1 && - mk_child child2 && + mk_test testrepo heads/master && + mk_child testrepo child1 && + mk_child testrepo child2 && ( cd child1 && git branch foo && @@ -929,29 +951,50 @@ test_expect_success 'push into aliased refs (inconsistent)' ' ) ' +test_expect_success 'push requires --force to update lightweight tag' ' + mk_test testrepo heads/master && + mk_child testrepo child1 && + mk_child testrepo child2 && + ( + cd child1 && + git tag Tag && + git push ../child2 Tag && + git push ../child2 Tag && + >file1 && + git add file1 && + git commit -m "file1" && + git tag -f Tag && + test_must_fail git push ../child2 Tag && + git push --force ../child2 Tag && + git tag -f Tag && + test_must_fail git push ../child2 Tag HEAD~ && + git push --force ../child2 Tag + ) +' + test_expect_success 'push --porcelain' ' - mk_empty && + mk_empty testrepo && echo >.git/foo "To testrepo" && echo >>.git/foo "* refs/heads/master:refs/remotes/origin/master [new branch]" && echo >>.git/foo "Done" && git push >.git/bar --porcelain testrepo refs/heads/master:refs/remotes/origin/master && ( cd testrepo && - r=$(git show-ref -s --verify refs/remotes/origin/master) && - test "z$r" = "z$the_commit" && - test 1 = $(git for-each-ref refs/remotes/origin | wc -l) + echo "$the_commit commit refs/remotes/origin/master" >expect && + git for-each-ref refs/remotes/origin >actual && + test_cmp expect actual ) && test_cmp .git/foo .git/bar ' test_expect_success 'push --porcelain bad url' ' - mk_empty && + mk_empty testrepo && test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master && test_must_fail grep -q Done .git/bar ' test_expect_success 'push --porcelain rejected' ' - mk_empty && + mk_empty testrepo && git push testrepo refs/heads/master:refs/remotes/origin/master && (cd testrepo && git reset --hard origin/master^ @@ -965,7 +1008,7 @@ test_expect_success 'push --porcelain rejected' ' ' test_expect_success 'push --porcelain --dry-run rejected' ' - mk_empty && + mk_empty testrepo && git push testrepo refs/heads/master:refs/remotes/origin/master && (cd testrepo && git reset --hard origin/master @@ -979,4 +1022,171 @@ test_expect_success 'push --porcelain --dry-run rejected' ' test_cmp .git/foo .git/bar ' +test_expect_success 'push --prune' ' + mk_test testrepo heads/master heads/second heads/foo heads/bar && + git push --prune testrepo : && + check_push_result testrepo $the_commit heads/master && + check_push_result testrepo $the_first_commit heads/second && + ! check_push_result testrepo $the_first_commit heads/foo heads/bar +' + +test_expect_success 'push --prune refspec' ' + mk_test testrepo tmp/master tmp/second tmp/foo tmp/bar && + git push --prune testrepo "refs/heads/*:refs/tmp/*" && + check_push_result testrepo $the_commit tmp/master && + check_push_result testrepo $the_first_commit tmp/second && + ! check_push_result testrepo $the_first_commit tmp/foo tmp/bar +' + +for configsection in transfer receive +do + test_expect_success "push to update a ref hidden by $configsection.hiderefs" ' + mk_test testrepo heads/master hidden/one hidden/two hidden/three && + ( + cd testrepo && + git config $configsection.hiderefs refs/hidden + ) && + + # push to unhidden ref succeeds normally + git push testrepo master:refs/heads/master && + check_push_result testrepo $the_commit heads/master && + + # push to update a hidden ref should fail + test_must_fail git push testrepo master:refs/hidden/one && + check_push_result testrepo $the_first_commit hidden/one && + + # push to delete a hidden ref should fail + test_must_fail git push testrepo :refs/hidden/two && + check_push_result testrepo $the_first_commit hidden/two && + + # idempotent push to update a hidden ref should fail + test_must_fail git push testrepo $the_first_commit:refs/hidden/three && + check_push_result testrepo $the_first_commit hidden/three + ' +done + +test_expect_success 'fetch exact SHA1' ' + mk_test testrepo heads/master hidden/one && + git push testrepo master:refs/hidden/one && + ( + cd testrepo && + git config transfer.hiderefs refs/hidden + ) && + check_push_result testrepo $the_commit hidden/one && + + mk_child testrepo child && + ( + cd child && + + # make sure $the_commit does not exist here + git repack -a -d && + git prune && + test_must_fail git cat-file -t $the_commit && + + # fetching the hidden object should fail by default + test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy && + test_must_fail git rev-parse --verify refs/heads/copy && + + # the server side can allow it to succeed + ( + cd ../testrepo && + git config uploadpack.allowtipsha1inwant true + ) && + + git fetch -v ../testrepo $the_commit:refs/heads/copy && + result=$(git rev-parse --verify refs/heads/copy) && + test "$the_commit" = "$result" + ) +' + +test_expect_success 'fetch follows tags by default' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + ( + cd src && + git pull ../testrepo master && + git tag -m "annotated" tag && + git for-each-ref >tmp1 && + ( + cat tmp1 + sed -n "s|refs/heads/master$|refs/remotes/origin/master|p" tmp1 + ) | + sort -k 3 >../expect + ) && + git init dst && + ( + cd dst && + git remote add origin ../src && + git config branch.master.remote origin && + git config branch.master.merge refs/heads/master && + git pull && + git for-each-ref >../actual + ) && + test_cmp expect actual +' + +test_expect_success 'push does not follow tags by default' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git tag -m "annotated" tag && + git checkout -b another && + git commit --allow-empty -m "future commit" && + git tag -m "future" future && + git checkout master && + git for-each-ref refs/heads/master >../expect && + git push ../dst master + ) && + ( + cd dst && + git for-each-ref >../actual + ) && + test_cmp expect actual +' + +test_expect_success 'push --follow-tag only pushes relevant tags' ' + mk_test testrepo heads/master && + rm -fr src dst && + git init src && + git init --bare dst && + ( + cd src && + git pull ../testrepo master && + git tag -m "annotated" tag && + git checkout -b another && + git commit --allow-empty -m "future commit" && + git tag -m "future" future && + git checkout master && + git for-each-ref refs/heads/master refs/tags/tag >../expect + git push --follow-tag ../dst master + ) && + ( + cd dst && + git for-each-ref >../actual + ) && + test_cmp expect actual +' + +test_expect_success 'push --no-thin must produce non-thin pack' ' + cat >>path1 <<\EOF && +keep base version of path1 big enough, compared to the new changes +later, in order to pass size heuristics in +builtin/pack-objects.c:try_delta() +EOF + git commit -am initial && + git init no-thin && + git --git-dir=no-thin/.git config receive.unpacklimit 0 && + git push no-thin/.git refs/heads/master:refs/heads/foo && + echo modified >> path1 && + git commit -am modified && + git repack -adf && + rcvpck="git receive-pack --reject-thin-pack-for-testing" && + git push --no-thin --receive-pack="$rcvpck" no-thin/.git refs/heads/master:refs/heads/foo +' + test_done diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh index e2ad260508..12a5dfb17e 100755 --- a/t/t5517-push-mirror.sh +++ b/t/t5517-push-mirror.sh @@ -256,7 +256,7 @@ test_expect_success 'remote.foo.mirror=no has no effect' ' git branch keep master && git push --mirror up && git branch -D keep && - git push up + git push up : ) && ( cd mirror && diff --git a/t/t5519-push-alternates.sh b/t/t5519-push-alternates.sh index c00c9b071d..11fcd37700 100755 --- a/t/t5519-push-alternates.sh +++ b/t/t5519-push-alternates.sh @@ -40,7 +40,7 @@ test_expect_success 'alice works and pushes' ' cd alice-work && echo more >file && git commit -a -m second && - git push ../alice-pub + git push ../alice-pub : ) ' @@ -57,7 +57,7 @@ test_expect_success 'bob fetches from alice, works and pushes' ' git pull ../alice-pub master && echo more bob >file && git commit -a -m third && - git push ../bob-pub + git push ../bob-pub : ) && # Check that the second commit by Alice is not sent @@ -86,7 +86,7 @@ test_expect_success 'alice works and pushes again' ' cd alice-work && echo more alice >file && git commit -a -m fourth && - git push ../alice-pub + git push ../alice-pub : ) ' @@ -99,7 +99,7 @@ test_expect_success 'bob works and pushes' ' cd bob-work && echo yet more bob >file && git commit -a -m fifth && - git push ../bob-pub + git push ../bob-pub : ) ' @@ -115,7 +115,7 @@ test_expect_success 'alice works and pushes yet again' ' git commit -a -m sixth.2 && echo more and more alice >>file && git commit -a -m sixth.3 && - git push ../alice-pub + git push ../alice-pub : ) ' @@ -136,7 +136,7 @@ test_expect_success 'bob works and pushes again' ' git hash-object -t commit -w commit && echo even more bob >file && git commit -a -m seventh && - git push ../bob-pub + git push ../bob-pub : ) ' diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 35304b41e9..227d293350 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -57,6 +57,35 @@ test_expect_success 'pulling into void does not overwrite untracked files' ' ) ' +test_expect_success 'pulling into void does not overwrite staged files' ' + git init cloned-staged-colliding && + ( + cd cloned-staged-colliding && + echo "alternate content" >file && + git add file && + test_must_fail git pull .. master && + echo "alternate content" >expect && + test_cmp expect file && + git cat-file blob :file >file.index && + test_cmp expect file.index + ) +' + + +test_expect_success 'pulling into void does not remove new staged files' ' + git init cloned-staged-new && + ( + cd cloned-staged-new && + echo "new tracked file" >newfile && + git add newfile && + git pull .. master && + echo "new tracked file" >expect && + test_cmp expect newfile && + git cat-file blob :newfile >newfile.index && + test_cmp expect newfile.index + ) +' + test_expect_success 'test . as a remote' ' git branch copy master && @@ -96,8 +125,7 @@ test_expect_success '--rebase' ' ' test_expect_success 'pull.rebase' ' git reset --hard before-rebase && - git config --bool pull.rebase true && - test_when_finished "git config --unset pull.rebase" && + test_config pull.rebase true && git pull . copy && test $(git rev-parse HEAD^) = $(git rev-parse copy) && test new = $(git show HEAD:file2) @@ -105,8 +133,7 @@ test_expect_success 'pull.rebase' ' test_expect_success 'branch.to-rebase.rebase' ' git reset --hard before-rebase && - git config --bool branch.to-rebase.rebase true && - test_when_finished "git config --unset branch.to-rebase.rebase" && + test_config branch.to-rebase.rebase true && git pull . copy && test $(git rev-parse HEAD^) = $(git rev-parse copy) && test new = $(git show HEAD:file2) @@ -114,15 +141,102 @@ test_expect_success 'branch.to-rebase.rebase' ' test_expect_success 'branch.to-rebase.rebase should override pull.rebase' ' git reset --hard before-rebase && - git config --bool pull.rebase true && - test_when_finished "git config --unset pull.rebase" && - git config --bool branch.to-rebase.rebase false && - test_when_finished "git config --unset branch.to-rebase.rebase" && + test_config pull.rebase true && + test_config branch.to-rebase.rebase false && git pull . copy && test $(git rev-parse HEAD^) != $(git rev-parse copy) && test new = $(git show HEAD:file2) ' +# add a feature branch, keep-merge, that is merged into master, so the +# test can try preserving the merge commit (or not) with various +# --rebase flags/pull.rebase settings. +test_expect_success 'preserve merge setup' ' + git reset --hard before-rebase && + git checkout -b keep-merge second^ && + test_commit file3 && + git checkout to-rebase && + git merge keep-merge && + git tag before-preserve-rebase +' + +test_expect_success 'pull.rebase=false create a new merge commit' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase false && + git pull . copy && + test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) && + test $(git rev-parse HEAD^2) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success 'pull.rebase=true flattens keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase true && + git pull . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success 'pull.rebase=1 is treated as true and flattens keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase 1 && + git pull . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success 'pull.rebase=preserve rebases and merges keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase preserve && + git pull . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge) +' + +test_expect_success 'pull.rebase=invalid fails' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase invalid && + ! git pull . copy +' + +test_expect_success '--rebase=false create a new merge commit' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase true && + git pull --rebase=false . copy && + test $(git rev-parse HEAD^1) = $(git rev-parse before-preserve-rebase) && + test $(git rev-parse HEAD^2) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success '--rebase=true rebases and flattens keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase preserve && + git pull --rebase=true . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + +test_expect_success '--rebase=preserve rebases and merges keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase true && + git pull --rebase=preserve . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test $(git rev-parse HEAD^2) = $(git rev-parse keep-merge) +' + +test_expect_success '--rebase=invalid fails' ' + git reset --hard before-preserve-rebase && + ! git pull --rebase=invalid . copy +' + +test_expect_success '--rebase overrides pull.rebase=preserve and flattens keep-merge' ' + git reset --hard before-preserve-rebase && + test_config pull.rebase preserve && + git pull --rebase . copy && + test $(git rev-parse HEAD^^) = $(git rev-parse copy) && + test file3 = $(git show HEAD:file3.t) +' + test_expect_success '--rebase with rebased upstream' ' git remote add -f me . && @@ -171,9 +285,9 @@ test_expect_success 'pull --rebase dies early with dirty working directory' ' git update-ref refs/remotes/me/copy copy^ && COPY=$(git rev-parse --verify me/copy) && git rebase --onto $COPY copy && - git config branch.to-rebase.remote me && - git config branch.to-rebase.merge refs/heads/copy && - git config branch.to-rebase.rebase true && + test_config branch.to-rebase.remote me && + test_config branch.to-rebase.merge refs/heads/copy && + test_config branch.to-rebase.rebase true && echo dirty >> file && git add file && test_must_fail git pull && diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh index 1b06691bb4..453aba53f4 100755 --- a/t/t5521-pull-options.sh +++ b/t/t5521-pull-options.sh @@ -15,8 +15,19 @@ test_expect_success 'git pull -q' ' mkdir clonedq && (cd clonedq && git init && git pull -q "../parent" >out 2>err && - test ! -s err && - test ! -s out) + test_must_be_empty err && + test_must_be_empty out) +' + +test_expect_success 'git pull -q --rebase' ' + mkdir clonedqrb && + (cd clonedqrb && git init && + git pull -q --rebase "../parent" >out 2>err && + test_must_be_empty err && + test_must_be_empty out && + git pull -q --rebase "../parent" >out 2>err && + test_must_be_empty err && + test_must_be_empty out) ' test_expect_success 'git pull' ' @@ -24,7 +35,15 @@ test_expect_success 'git pull' ' (cd cloned && git init && git pull "../parent" >out 2>err && test -s err && - test ! -s out) + test_must_be_empty out) +' + +test_expect_success 'git pull --rebase' ' + mkdir clonedrb && + (cd clonedrb && git init && + git pull --rebase "../parent" >out 2>err && + test -s err && + test_must_be_empty out) ' test_expect_success 'git pull -v' ' @@ -32,22 +51,30 @@ test_expect_success 'git pull -v' ' (cd clonedv && git init && git pull -v "../parent" >out 2>err && test -s err && - test ! -s out) + test_must_be_empty out) +' + +test_expect_success 'git pull -v --rebase' ' + mkdir clonedvrb && + (cd clonedvrb && git init && + git pull -v --rebase "../parent" >out 2>err && + test -s err && + test_must_be_empty out) ' test_expect_success 'git pull -v -q' ' mkdir clonedvq && (cd clonedvq && git init && git pull -v -q "../parent" >out 2>err && - test ! -s out && - test ! -s err) + test_must_be_empty out && + test_must_be_empty err) ' test_expect_success 'git pull -q -v' ' mkdir clonedqv && (cd clonedqv && git init && git pull -q -v "../parent" >out 2>err && - test ! -s out && + test_must_be_empty out && test -s err) ' diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh new file mode 100755 index 0000000000..6a5ac3add4 --- /dev/null +++ b/t/t5528-push-default.sh @@ -0,0 +1,175 @@ +#!/bin/sh + +test_description='check various push.default settings' +. ./test-lib.sh + +test_expect_success 'setup bare remotes' ' + git init --bare repo1 && + git remote add parent1 repo1 && + git init --bare repo2 && + git remote add parent2 repo2 && + test_commit one && + git push parent1 HEAD && + git push parent2 HEAD +' + +# $1 = local revision +# $2 = remote revision (tested to be equal to the local one) +# $3 = [optional] repo to check for actual output (repo1 by default) +check_pushed_commit () { + git log -1 --format='%h %s' "$1" >expect && + git --git-dir="${3:-repo1}" log -1 --format='%h %s' "$2" >actual && + test_cmp expect actual +} + +# $1 = push.default value +# $2 = expected target branch for the push +# $3 = [optional] repo to check for actual output (repo1 by default) +test_push_success () { + git -c push.default="$1" push && + check_pushed_commit HEAD "$2" "$3" +} + +# $1 = push.default value +# check that push fails and does not modify any remote branch +test_push_failure () { + git --git-dir=repo1 log --no-walk --format='%h %s' --all >expect && + test_must_fail git -c push.default="$1" push && + git --git-dir=repo1 log --no-walk --format='%h %s' --all >actual && + test_cmp expect actual +} + +# $1 = success or failure +# $2 = push.default value +# $3 = branch to check for actual output (master or foo) +# $4 = [optional] switch to triangular workflow +test_pushdefault_workflow () { + workflow=central + pushdefault=parent1 + if test -n "${4-}"; then + workflow=triangular + pushdefault=parent2 + fi + test_expect_success "push.default = $2 $1 in $workflow workflows" " + test_config branch.master.remote parent1 && + test_config branch.master.merge refs/heads/foo && + test_config remote.pushdefault $pushdefault && + test_commit commit-for-$2${4+-triangular} && + test_push_$1 $2 $3 ${4+repo2} + " +} + +test_expect_success '"upstream" pushes to configured upstream' ' + git checkout master && + test_config branch.master.remote parent1 && + test_config branch.master.merge refs/heads/foo && + test_commit two && + test_push_success upstream foo +' + +test_expect_success '"upstream" does not push on unconfigured remote' ' + git checkout master && + test_unconfig branch.master.remote && + test_commit three && + test_push_failure upstream +' + +test_expect_success '"upstream" does not push on unconfigured branch' ' + git checkout master && + test_config branch.master.remote parent1 && + test_unconfig branch.master.merge && + test_commit four && + test_push_failure upstream +' + +test_expect_success '"upstream" does not push when remotes do not match' ' + git checkout master && + test_config branch.master.remote parent1 && + test_config branch.master.merge refs/heads/foo && + test_config push.default upstream && + test_commit five && + test_must_fail git push parent2 +' + +test_expect_success 'push from/to new branch with upstream, matching and simple' ' + git checkout -b new-branch && + test_push_failure simple && + test_push_failure matching && + test_push_failure upstream +' + +test_expect_success 'push from/to new branch with current creates remote branch' ' + test_config branch.new-branch.remote repo1 && + git checkout new-branch && + test_push_success current new-branch +' + +test_expect_success 'push to existing branch, with no upstream configured' ' + test_config branch.master.remote repo1 && + git checkout master && + test_push_failure simple && + test_push_failure upstream +' + +test_expect_success 'push to existing branch, upstream configured with same name' ' + test_config branch.master.remote repo1 && + test_config branch.master.merge refs/heads/master && + git checkout master && + test_commit six && + test_push_success upstream master && + test_commit seven && + test_push_success simple master +' + +test_expect_success 'push to existing branch, upstream configured with different name' ' + test_config branch.master.remote repo1 && + test_config branch.master.merge refs/heads/other-name && + git checkout master && + test_commit eight && + test_push_success upstream other-name && + test_commit nine && + test_push_failure simple && + git --git-dir=repo1 log -1 --format="%h %s" "other-name" >expect-other-name && + test_push_success current master && + git --git-dir=repo1 log -1 --format="%h %s" "other-name" >actual-other-name && + test_cmp expect-other-name actual-other-name +' + +# We are on 'master', which integrates with 'foo' from parent1 +# remote (set in test_pushdefault_workflow helper). Push to +# parent1 in centralized, and push to parent2 in triangular workflow. +# The parent1 repository has 'master' and 'foo' branches, while +# the parent2 repository has only 'master' branch. +# +# test_pushdefault_workflow() arguments: +# $1 = success or failure +# $2 = push.default value +# $3 = branch to check for actual output (master or foo) +# $4 = [optional] switch to triangular workflow + +# update parent1's master (which is not our upstream) +test_pushdefault_workflow success current master + +# update parent1's foo (which is our upstream) +test_pushdefault_workflow success upstream foo + +# upsream is foo which is not the name of the current branch +test_pushdefault_workflow failure simple master + +# master and foo are updated +test_pushdefault_workflow success matching master + +# master is updated +test_pushdefault_workflow success current master triangular + +# upstream mode cannot be used in triangular +test_pushdefault_workflow failure upstream foo triangular + +# in triangular, 'simple' works as 'current' and update the branch +# with the same name. +test_pushdefault_workflow success simple master triangular + +# master is updated (parent2 does not have foo) +test_pushdefault_workflow success matching master triangular + +test_done diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh index 6b2a5f4a65..3932e797f7 100755 --- a/t/t5530-upload-pack-error.sh +++ b/t/t5530-upload-pack-error.sh @@ -35,8 +35,8 @@ test_expect_success 'upload-pack fails due to error in pack-objects packing' ' printf "0032want %s\n00000009done\n0000" \ $(git rev-parse HEAD) >input && test_must_fail git upload-pack . <input >/dev/null 2>output.err && - grep "unable to read" output.err && - grep "pack-objects died" output.err + test_i18ngrep "unable to read" output.err && + test_i18ngrep "pack-objects died" output.err ' test_expect_success 'corrupt repo differently' ' @@ -54,9 +54,6 @@ test_expect_success 'upload-pack fails due to error in rev-list' ' printf "0032want %s\n0034shallow %s00000009done\n0000" \ $(git rev-parse HEAD) $(git rev-parse HEAD^) >input && test_must_fail git upload-pack . <input >/dev/null 2>output.err && - # pack-objects survived - grep "Total.*, reused" output.err && - # but there was an error, which must have been in rev-list grep "bad tree object" output.err ' diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh index 30bec4b5f9..8c16e045a0 100755 --- a/t/t5531-deep-submodule-push.sh +++ b/t/t5531-deep-submodule-push.sh @@ -16,6 +16,7 @@ test_expect_success setup ' ( cd gar/bage && git init && + git config push.default matching && >junk && git add junk && git commit -m "Initial junk" @@ -119,4 +120,98 @@ test_expect_success 'push succeeds if submodule has no remote and is on the firs ) ' +test_expect_success 'push unpushed submodules when not needed' ' + ( + cd work && + ( + cd gar/bage && + git checkout master && + >junk5 && + git add junk5 && + git commit -m "Fifth junk" && + git push && + git rev-parse origin/master >../../../expected + ) && + git checkout master && + git add gar/bage && + git commit -m "Fifth commit for gar/bage" && + git push --recurse-submodules=on-demand ../pub.git master + ) && + ( + cd submodule.git && + git rev-parse master >../actual + ) && + test_cmp expected actual +' + +test_expect_success 'push unpushed submodules when not needed 2' ' + ( + cd submodule.git && + git rev-parse master >../expected + ) && + ( + cd work && + ( + cd gar/bage && + >junk6 && + git add junk6 && + git commit -m "Sixth junk" + ) && + >junk2 && + git add junk2 && + git commit -m "Second junk for work" && + git push --recurse-submodules=on-demand ../pub.git master + ) && + ( + cd submodule.git && + git rev-parse master >../actual + ) && + test_cmp expected actual +' + +test_expect_success 'push unpushed submodules recursively' ' + ( + cd work && + ( + cd gar/bage && + git checkout master && + > junk7 && + git add junk7 && + git commit -m "Seventh junk" && + git rev-parse master >../../../expected + ) && + git checkout master && + git add gar/bage && + git commit -m "Seventh commit for gar/bage" && + git push --recurse-submodules=on-demand ../pub.git master + ) && + ( + cd submodule.git && + git rev-parse master >../actual + ) && + test_cmp expected actual +' + +test_expect_success 'push unpushable submodule recursively fails' ' + ( + cd work && + ( + cd gar/bage && + git rev-parse origin/master >../../../expected && + git checkout master~0 && + > junk8 && + git add junk8 && + git commit -m "Eighth junk" + ) && + git add gar/bage && + git commit -m "Eighth commit for gar/bage" && + test_must_fail git push --recurse-submodules=on-demand ../pub.git master + ) && + ( + cd submodule.git && + git rev-parse master >../actual + ) && + test_cmp expected actual +' + test_done diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh index 62f2460047..5531bd1af4 100755 --- a/t/t5532-fetch-proxy.sh +++ b/t/t5532-fetch-proxy.sh @@ -15,7 +15,7 @@ test_expect_success 'setup remote repo' ' cat >proxy <<'EOF' #!/bin/sh echo >&2 "proxying for $*" -cmd=`perl -e ' +cmd=`"$PERL_PATH" -e ' read(STDIN, $buf, 4); my $n = hex($buf) - 4; read(STDIN, $buf, $n); diff --git a/t/t5533-push-cas.sh b/t/t5533-push-cas.sh new file mode 100755 index 0000000000..ba20d83333 --- /dev/null +++ b/t/t5533-push-cas.sh @@ -0,0 +1,189 @@ +#!/bin/sh + +test_description='compare & swap push force/delete safety' + +. ./test-lib.sh + +setup_srcdst_basic () { + rm -fr src dst && + git clone --no-local . src && + git clone --no-local src dst && + ( + cd src && git checkout HEAD^0 + ) +} + +test_expect_success setup ' + : create template repository + test_commit A && + test_commit B && + test_commit C +' + +test_expect_success 'push to update (protected)' ' + setup_srcdst_basic && + ( + cd dst && + test_commit D && + test_must_fail git push --force-with-lease=master:master origin master + ) && + git ls-remote . refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (protected, forced)' ' + setup_srcdst_basic && + ( + cd dst && + test_commit D && + git push --force --force-with-lease=master:master origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (protected, tracking)' ' + setup_srcdst_basic && + ( + cd src && + git checkout master && + test_commit D && + git checkout HEAD^0 + ) && + git ls-remote src refs/heads/master >expect && + ( + cd dst && + test_commit E && + git ls-remote . refs/remotes/origin/master >expect && + test_must_fail git push --force-with-lease=master origin master && + git ls-remote . refs/remotes/origin/master >actual && + test_cmp expect actual + ) && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (protected, tracking, forced)' ' + setup_srcdst_basic && + ( + cd src && + git checkout master && + test_commit D && + git checkout HEAD^0 + ) && + ( + cd dst && + test_commit E && + git ls-remote . refs/remotes/origin/master >expect && + git push --force --force-with-lease=master origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (allowed)' ' + setup_srcdst_basic && + ( + cd dst && + test_commit D && + git push --force-with-lease=master:master^ origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (allowed, tracking)' ' + setup_srcdst_basic && + ( + cd dst && + test_commit D && + git push --force-with-lease=master origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to update (allowed even though no-ff)' ' + setup_srcdst_basic && + ( + cd dst && + git reset --hard HEAD^ && + test_commit D && + git push --force-with-lease=master origin master + ) && + git ls-remote dst refs/heads/master >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to delete (protected)' ' + setup_srcdst_basic && + git ls-remote src refs/heads/master >expect && + ( + cd dst && + test_must_fail git push --force-with-lease=master:master^ origin :master + ) && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to delete (protected, forced)' ' + setup_srcdst_basic && + ( + cd dst && + git push --force --force-with-lease=master:master^ origin :master + ) && + >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'push to delete (allowed)' ' + setup_srcdst_basic && + ( + cd dst && + git push --force-with-lease=master origin :master + ) && + >expect && + git ls-remote src refs/heads/master >actual && + test_cmp expect actual +' + +test_expect_success 'cover everything with default force-with-lease (protected)' ' + setup_srcdst_basic && + ( + cd src && + git branch naster master^ + ) + git ls-remote src refs/heads/\* >expect && + ( + cd dst && + test_must_fail git push --force-with-lease origin master master:naster + ) && + git ls-remote src refs/heads/\* >actual && + test_cmp expect actual +' + +test_expect_success 'cover everything with default force-with-lease (allowed)' ' + setup_srcdst_basic && + ( + cd src && + git branch naster master^ + ) + ( + cd dst && + git fetch && + git push --force-with-lease origin master master:naster + ) && + git ls-remote dst refs/heads/master | + sed -e "s/master/naster/" >expect && + git ls-remote src refs/heads/naster >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t5535-fetch-push-symref.sh b/t/t5535-fetch-push-symref.sh new file mode 100755 index 0000000000..8ed58d27f2 --- /dev/null +++ b/t/t5535-fetch-push-symref.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +test_description='avoiding conflicting update thru symref aliasing' + +. ./test-lib.sh + +test_expect_success 'setup' ' + test_commit one && + git clone . src && + git clone src dst1 && + git clone src dst2 && + test_commit two && + ( cd src && git pull ) +' + +test_expect_success 'push' ' + ( + cd src && + git push ../dst1 "refs/remotes/*:refs/remotes/*" + ) && + git ls-remote src "refs/remotes/*" >expect && + git ls-remote dst1 "refs/remotes/*" >actual && + test_cmp expect actual && + ( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect && + ( cd dst1 && git symbolic-ref refs/remotes/origin/HEAD ) >actual && + test_cmp expect actual +' + +test_expect_success 'fetch' ' + ( + cd dst2 && + git fetch ../src "refs/remotes/*:refs/remotes/*" + ) && + git ls-remote src "refs/remotes/*" >expect && + git ls-remote dst2 "refs/remotes/*" >actual && + test_cmp expect actual && + ( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect && + ( cd dst2 && git symbolic-ref refs/remotes/origin/HEAD ) >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh index 1eea647656..01d0d95b4d 100755 --- a/t/t5540-http-push.sh +++ b/t/t5540-http-push.sh @@ -46,15 +46,7 @@ test_expect_success 'create password-protected repository' ' "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" ' -test_expect_success 'setup askpass helper' ' - cat >askpass <<-\EOF && - #!/bin/sh - echo user@host - EOF - chmod +x askpass && - GIT_ASKPASS="$PWD/askpass" && - export GIT_ASKPASS -' +setup_askpass_helper test_expect_success 'clone remote repository' ' cd "$ROOT_PATH" && @@ -117,7 +109,7 @@ test_expect_success 'http-push fetches packed objects' ' # By reset, we force git to retrieve the packed object (cd "$ROOT_PATH"/test_repo_clone_packed && git reset --hard HEAD^ && - git remote rm origin && + git remote remove origin && git reflog expire --expire=0 --all && git prune && git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master) @@ -162,6 +154,7 @@ test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \ test_expect_success 'push to password-protected repository (user in URL)' ' test_commit pw-user && + set_askpass user@host && git push "$HTTPD_URL_USER/auth/dumb/test_repo.git" HEAD && git rev-parse --verify HEAD >expect && git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \ @@ -169,9 +162,15 @@ test_expect_success 'push to password-protected repository (user in URL)' ' test_cmp expect actual ' +test_expect_failure 'user was prompted only once for password' ' + expect_askpass pass user@host +' + test_expect_failure 'push to password-protected repository (no user in URL)' ' test_commit pw-nouser && + set_askpass user@host && git push "$HTTPD_URL/auth/dumb/test_repo.git" HEAD && + expect_askpass both user@host git rev-parse --verify HEAD >expect && git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/test_repo.git" \ rev-parse --verify HEAD >actual && diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh index cc6f081711..470ac54295 100755 --- a/t/t5541-http-push.sh +++ b/t/t5541-http-push.sh @@ -30,11 +30,14 @@ test_expect_success 'setup remote repository' ' git clone --bare test_repo test_repo.git && cd test_repo.git && git config http.receivepack true && + git config core.logallrefupdates true && ORIG_HEAD=$(git rev-parse --verify HEAD) && cd - && mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH" ' +setup_askpass_helper + cat >exp <<EOF GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200 POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200 @@ -63,7 +66,10 @@ test_expect_success 'no empty path components' ' test_expect_success 'clone remote repository' ' rm -rf test_repo_clone && - git clone $HTTPD_URL/smart/test_repo.git test_repo_clone + git clone $HTTPD_URL/smart/test_repo.git test_repo_clone && + ( + cd test_repo_clone && git config push.default matching + ) ' test_expect_success 'push to remote repository (standard)' ' @@ -147,7 +153,7 @@ test_expect_success 'used receive-pack service' ' ' test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \ - "$ROOT_PATH"/test_repo_clone master + "$ROOT_PATH"/test_repo_clone master success test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' ' # create a dissimilarly-named remote ref so that git is unable to match the @@ -167,7 +173,7 @@ test_expect_success 'push fails for non-fast-forward refs unmatched by remote he ' test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper: our output' ' - test_i18ngrep "To prevent you from losing history, non-fast-forward updates were rejected" \ + test_i18ngrep "Updates were rejected because" \ output ' @@ -175,8 +181,7 @@ test_expect_success 'push (chunked)' ' git checkout master && test_commit commit path3 && HEAD=$(git rev-parse --verify HEAD) && - git config http.postbuffer 4 && - test_when_finished "git config --unset http.postbuffer" && + test_config http.postbuffer 4 && git push -v -v origin $BRANCH 2>err && grep "POST git-receive-pack (chunked)" err && (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git && @@ -215,12 +220,109 @@ test_expect_success 'push --mirror to repo with alternates' ' git push --mirror "$HTTPD_URL"/smart/alternates-mirror.git ' -test_expect_success TTY 'quiet push' ' +test_expect_success TTY 'push shows progress when stderr is a tty' ' + cd "$ROOT_PATH"/test_repo_clone && + test_commit noisy && + test_terminal git push >output 2>&1 && + grep "^Writing objects" output +' + +test_expect_success TTY 'push --quiet silences status and progress' ' cd "$ROOT_PATH"/test_repo_clone && test_commit quiet && - test_terminal git push --quiet --no-progress 2>&1 | tee output && + test_terminal git push --quiet >output 2>&1 && test_cmp /dev/null output ' +test_expect_success TTY 'push --no-progress silences progress but not status' ' + cd "$ROOT_PATH"/test_repo_clone && + test_commit no-progress && + test_terminal git push --no-progress >output 2>&1 && + grep "^To http" output && + ! grep "^Writing objects" +' + +test_expect_success 'push --progress shows progress to non-tty' ' + cd "$ROOT_PATH"/test_repo_clone && + test_commit progress && + git push --progress >output 2>&1 && + grep "^To http" output && + grep "^Writing objects" output +' + +test_expect_success 'http push gives sane defaults to reflog' ' + cd "$ROOT_PATH"/test_repo_clone && + test_commit reflog-test && + git push "$HTTPD_URL"/smart/test_repo.git && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ + log -g -1 --format="%gn <%ge>" >actual && + echo "anonymous <anonymous@http.127.0.0.1>" >expect && + test_cmp expect actual +' + +test_expect_success 'http push respects GIT_COMMITTER_* in reflog' ' + cd "$ROOT_PATH"/test_repo_clone && + test_commit custom-reflog-test && + git push "$HTTPD_URL"/smart_custom_env/test_repo.git && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ + log -g -1 --format="%gn <%ge>" >actual && + echo "Custom User <custom@example.com>" >expect && + test_cmp expect actual +' + +test_expect_success 'push over smart http with auth' ' + cd "$ROOT_PATH/test_repo_clone" && + echo push-auth-test >expect && + test_commit push-auth-test && + set_askpass user@host && + git push "$HTTPD_URL"/auth/smart/test_repo.git && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ + log -1 --format=%s >actual && + expect_askpass both user@host && + test_cmp expect actual +' + +test_expect_success 'push to auth-only-for-push repo' ' + cd "$ROOT_PATH/test_repo_clone" && + echo push-half-auth >expect && + test_commit push-half-auth && + set_askpass user@host && + git push "$HTTPD_URL"/auth-push/smart/test_repo.git && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git" \ + log -1 --format=%s >actual && + expect_askpass both user@host && + test_cmp expect actual +' + +test_expect_success 'create repo without http.receivepack set' ' + cd "$ROOT_PATH" && + git init half-auth && + ( + cd half-auth && + test_commit one + ) && + git clone --bare half-auth "$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" +' + +test_expect_success 'clone via half-auth-complete does not need password' ' + cd "$ROOT_PATH" && + set_askpass wrong && + git clone "$HTTPD_URL"/half-auth-complete/smart/half-auth.git \ + half-auth-clone && + expect_askpass none +' + +test_expect_success 'push into half-auth-complete requires password' ' + cd "$ROOT_PATH/half-auth-clone" && + echo two >expect && + test_commit two && + set_askpass user@host && + git push "$HTTPD_URL/half-auth-complete/smart/half-auth.git" && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/half-auth.git" \ + log -1 --format=%s >actual && + expect_askpass both user@host && + test_cmp expect actual +' + stop_httpd test_done diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh index e5e6b8f643..f7d0f146f0 100755 --- a/t/t5550-http-fetch.sh +++ b/t/t5550-http-fetch.sh @@ -13,17 +13,23 @@ LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'} start_httpd test_expect_success 'setup repository' ' - echo content >file && + git config push.default matching && + echo content1 >file && git add file && git commit -m one + echo content2 >file && + git add file && + git commit -m two ' -test_expect_success 'create http-accessible bare repository' ' - mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && +test_expect_success 'create http-accessible bare repository with loose objects' ' + cp -R .git "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && - git --bare init && + git config core.bare true && + mkdir -p hooks && echo "exec git update-server-info" >hooks/post-update && - chmod +x hooks/post-update + chmod +x hooks/post-update && + hooks/post-update ) && git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && git push public master:master @@ -36,68 +42,34 @@ test_expect_success 'clone http repository' ' ' test_expect_success 'create password-protected repository' ' - mkdir "$HTTPD_DOCUMENT_ROOT_PATH/auth/" && + mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" && cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ - "$HTTPD_DOCUMENT_ROOT_PATH/auth/repo.git" -' - -test_expect_success 'setup askpass helpers' ' - cat >askpass <<-EOF && - #!/bin/sh - echo >>"$PWD/askpass-query" "askpass: \$*" && - cat "$PWD/askpass-response" - EOF - chmod +x askpass && - GIT_ASKPASS="$PWD/askpass" && - export GIT_ASKPASS -' - -expect_askpass() { - dest=$HTTPD_DEST - { - case "$1" in - none) - ;; - pass) - echo "askpass: Password for 'http://$2@$dest': " - ;; - both) - echo "askpass: Username for 'http://$dest': " - echo "askpass: Password for 'http://$2@$dest': " - ;; - *) - false - ;; - esac - } >askpass-expect && - test_cmp askpass-expect askpass-query -} + "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/repo.git" +' + +setup_askpass_helper test_expect_success 'cloning password-protected repository can fail' ' - >askpass-query && - echo wrong >askpass-response && - test_must_fail git clone "$HTTPD_URL/auth/repo.git" clone-auth-fail && + set_askpass wrong && + test_must_fail git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-fail && expect_askpass both wrong ' test_expect_success 'http auth can use user/pass in URL' ' - >askpass-query && - echo wrong >askpass-response && - git clone "$HTTPD_URL_USER_PASS/auth/repo.git" clone-auth-none && + set_askpass wrong && + git clone "$HTTPD_URL_USER_PASS/auth/dumb/repo.git" clone-auth-none && expect_askpass none ' test_expect_success 'http auth can use just user in URL' ' - >askpass-query && - echo user@host >askpass-response && - git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-pass && + set_askpass user@host && + git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-pass && expect_askpass pass user@host ' test_expect_success 'http auth can request both user and pass' ' - >askpass-query && - echo user@host >askpass-response && - git clone "$HTTPD_URL/auth/repo.git" clone-auth-both && + set_askpass user@host && + git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-both && expect_askpass both user@host ' @@ -107,25 +79,22 @@ test_expect_success 'http auth respects credential helper config' ' echo username=user@host echo password=user@host }; f" && - >askpass-query && - echo wrong >askpass-response && - git clone "$HTTPD_URL/auth/repo.git" clone-auth-helper && + set_askpass wrong && + git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-helper && expect_askpass none ' test_expect_success 'http auth can get username from config' ' test_config_global "credential.$HTTPD_URL.username" user@host && - >askpass-query && - echo user@host >askpass-response && - git clone "$HTTPD_URL/auth/repo.git" clone-auth-user && + set_askpass user@host && + git clone "$HTTPD_URL/auth/dumb/repo.git" clone-auth-user && expect_askpass pass user@host ' test_expect_success 'configured username does not override URL' ' test_config_global "credential.$HTTPD_URL.username" wrong && - >askpass-query && - echo user@host >askpass-response && - git clone "$HTTPD_URL_USER/auth/repo.git" clone-auth-user2 && + set_askpass user@host && + git clone "$HTTPD_URL_USER/auth/dumb/repo.git" clone-auth-user2 && expect_askpass pass user@host ' diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh index 26d355725f..afb439e09c 100755 --- a/t/t5551-http-fetch.sh +++ b/t/t5551-http-fetch.sh @@ -13,6 +13,7 @@ LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5551'} start_httpd test_expect_success 'setup repository' ' + git config push.default matching && echo content >file && git add file && git commit -m one @@ -27,16 +28,19 @@ test_expect_success 'create http-accessible bare repository' ' git push public master:master ' +setup_askpass_helper + cat >exp <<EOF > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 > Accept: */* +> Accept-Encoding: gzip > Pragma: no-cache < HTTP/1.1 200 OK < Pragma: no-cache < Cache-Control: no-cache, max-age=0, must-revalidate < Content-Type: application/x-git-upload-pack-advertisement > POST /smart/repo.git/git-upload-pack HTTP/1.1 -> Accept-Encoding: deflate, gzip +> Accept-Encoding: gzip > Content-Type: application/x-git-upload-pack-request > Accept: application/x-git-upload-pack-result > Content-Length: xxx @@ -109,5 +113,141 @@ test_expect_success 'follow redirects (302)' ' git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t ' +test_expect_success 'redirects re-root further requests' ' + git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited +' + +test_expect_success 'clone from password-protected repository' ' + echo two >expect && + set_askpass user@host && + git clone --bare "$HTTPD_URL/auth/smart/repo.git" smart-auth && + expect_askpass both user@host && + git --git-dir=smart-auth log -1 --format=%s >actual && + test_cmp expect actual +' + +test_expect_success 'clone from auth-only-for-push repository' ' + echo two >expect && + set_askpass wrong && + git clone --bare "$HTTPD_URL/auth-push/smart/repo.git" smart-noauth && + expect_askpass none && + git --git-dir=smart-noauth log -1 --format=%s >actual && + test_cmp expect actual +' + +test_expect_success 'clone from auth-only-for-objects repository' ' + echo two >expect && + set_askpass user@host && + git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth && + expect_askpass both user@host && + git --git-dir=half-auth log -1 --format=%s >actual && + test_cmp expect actual +' + +test_expect_success 'no-op half-auth fetch does not require a password' ' + set_askpass wrong && + git --git-dir=half-auth fetch && + expect_askpass none +' + +test_expect_success 'redirects send auth to new location' ' + set_askpass user@host && + git -c credential.useHttpPath=true \ + clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth && + expect_askpass both user@host auth/smart/repo.git +' + +test_expect_success 'disable dumb http on server' ' + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ + config http.getanyfile false +' + +test_expect_success 'GIT_SMART_HTTP can disable smart http' ' + (GIT_SMART_HTTP=0 && + export GIT_SMART_HTTP && + cd clone && + test_must_fail git fetch) +' + +test_expect_success 'invalid Content-Type rejected' ' + test_must_fail git clone $HTTPD_URL/broken_smart/repo.git 2>actual + grep "not valid:" actual +' + +test_expect_success 'create namespaced refs' ' + test_commit namespaced && + git push public HEAD:refs/namespaces/ns/refs/heads/master && + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ + symbolic-ref refs/namespaces/ns/HEAD refs/namespaces/ns/refs/heads/master +' + +test_expect_success 'smart clone respects namespace' ' + git clone "$HTTPD_URL/smart_namespace/repo.git" ns-smart && + echo namespaced >expect && + git --git-dir=ns-smart/.git log -1 --format=%s >actual && + test_cmp expect actual +' + +test_expect_success 'dumb clone via http-backend respects namespace' ' + git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ + config http.getanyfile true && + GIT_SMART_HTTP=0 git clone \ + "$HTTPD_URL/smart_namespace/repo.git" ns-dumb && + echo namespaced >expect && + git --git-dir=ns-dumb/.git log -1 --format=%s >actual && + test_cmp expect actual +' + +cat >cookies.txt <<EOF +127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue +EOF +cat >expect_cookies.txt <<EOF + +127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue +127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value +EOF +test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' ' + git config http.cookiefile cookies.txt && + git config http.savecookies true && + git ls-remote $HTTPD_URL/smart_cookies/repo.git master && + tail -3 cookies.txt > cookies_tail.txt + test_cmp expect_cookies.txt cookies_tail.txt +' + +test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE + +test_expect_success EXPENSIVE 'create 50,000 tags in the repo' ' + ( + cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + for i in `test_seq 50000` + do + echo "commit refs/heads/too-many-refs" + echo "mark :$i" + echo "committer git <git@example.com> $i +0000" + echo "data 0" + echo "M 644 inline bla.txt" + echo "data 4" + echo "bla" + # make every commit dangling by always + # rewinding the branch after each commit + echo "reset refs/heads/too-many-refs" + echo "from :1" + done | git fast-import --export-marks=marks && + + # now assign tags to all the dangling commits we created above + tag=$(perl -e "print \"bla\" x 30") && + sed -e "s|^:\([^ ]*\) \(.*\)$|\2 refs/tags/$tag-\1|" <marks >>packed-refs + ) +' + +test_expect_success EXPENSIVE 'clone the 50,000 tag repo to check OS command line overflow' ' + git clone $HTTPD_URL/smart/repo.git too-many-refs 2>err && + test_line_count = 0 err && + ( + cd too-many-refs && + test $(git for-each-ref refs/tags | wc -l) = 50000 + ) +' + stop_httpd test_done diff --git a/t/t5560-http-backend-noserver.sh b/t/t5560-http-backend-noserver.sh index ef98d95e00..9be9ae3436 100755 --- a/t/t5560-http-backend-noserver.sh +++ b/t/t5560-http-backend-noserver.sh @@ -5,7 +5,7 @@ test_description='test git-http-backend-noserver' HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY" -test_have_prereq MINGW && export GREP_OPTIONS=-U +test_have_prereq GREP_STRIPS_CR && export GREP_OPTIONS=-U run_backend() { echo "$2" | diff --git a/t/t5570-git-daemon.sh b/t/t5570-git-daemon.sh new file mode 100755 index 0000000000..e06146835c --- /dev/null +++ b/t/t5570-git-daemon.sh @@ -0,0 +1,146 @@ +#!/bin/sh + +test_description='test fetching over git protocol' +. ./test-lib.sh + +LIB_GIT_DAEMON_PORT=${LIB_GIT_DAEMON_PORT-5570} +. "$TEST_DIRECTORY"/lib-git-daemon.sh +start_git_daemon + +test_expect_success 'setup repository' ' + git config push.default matching && + echo content >file && + git add file && + git commit -m one +' + +test_expect_success 'create git-accessible bare repository' ' + mkdir "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" && + (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" && + git --bare init && + : >git-daemon-export-ok + ) && + git remote add public "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" && + git push public master:master +' + +test_expect_success 'clone git repository' ' + git clone "$GIT_DAEMON_URL/repo.git" clone && + test_cmp file clone/file +' + +test_expect_success 'fetch changes via git protocol' ' + echo content >>file && + git commit -a -m two && + git push public && + (cd clone && git pull) && + test_cmp file clone/file +' + +test_expect_success 'remote detects correct HEAD' ' + git push public master:other && + (cd clone && + git remote set-head -d origin && + git remote set-head -a origin && + git symbolic-ref refs/remotes/origin/HEAD > output && + echo refs/remotes/origin/master > expect && + test_cmp expect output + ) +' + +test_expect_success 'prepare pack objects' ' + cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git && + (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git && + git --bare repack -a -d + ) +' + +test_expect_success 'fetch notices corrupt pack' ' + cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git && + (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad1.git && + p=`ls objects/pack/pack-*.pack` && + chmod u+w $p && + printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc + ) && + mkdir repo_bad1.git && + (cd repo_bad1.git && + git --bare init && + test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad1.git" && + test 0 = `ls objects/pack/pack-*.pack | wc -l` + ) +' + +test_expect_success 'fetch notices corrupt idx' ' + cp -R "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_pack.git "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git && + (cd "$GIT_DAEMON_DOCUMENT_ROOT_PATH"/repo_bad2.git && + p=`ls objects/pack/pack-*.idx` && + chmod u+w $p && + printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc + ) && + mkdir repo_bad2.git && + (cd repo_bad2.git && + git --bare init && + test_must_fail git --bare fetch "$GIT_DAEMON_URL/repo_bad2.git" && + test 0 = `ls objects/pack | wc -l` + ) +' + +test_remote_error() +{ + do_export=YesPlease + while test $# -gt 0 + do + case $1 in + -x) + shift + chmod -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" + ;; + -n) + shift + do_export= + ;; + *) + break + esac + done + + msg=$1 + shift + cmd=$1 + shift + repo=$1 + shift || error "invalid number of arguments" + + if test -x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo" + then + if test -n "$do_export" + then + : >"$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok" + else + rm -f "$GIT_DAEMON_DOCUMENT_ROOT_PATH/$repo/git-daemon-export-ok" + fi + fi + + test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output && + test_i18ngrep "fatal: remote error: $msg: /$repo" output && + ret=$? + chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git" + (exit $ret) +} + +msg="access denied or repository not exported" +test_expect_success 'clone non-existent' "test_remote_error '$msg' clone nowhere.git " +test_expect_success 'push disabled' "test_remote_error '$msg' push repo.git master" +test_expect_success 'read access denied' "test_remote_error -x '$msg' fetch repo.git " +test_expect_success 'not exported' "test_remote_error -n '$msg' fetch repo.git " + +stop_git_daemon +start_git_daemon --informative-errors + +test_expect_success 'clone non-existent' "test_remote_error 'no such repository' clone nowhere.git " +test_expect_success 'push disabled' "test_remote_error 'service not enabled' push repo.git master" +test_expect_success 'read access denied' "test_remote_error -x 'no such repository' fetch repo.git " +test_expect_success 'not exported' "test_remote_error -n 'repository not exported' fetch repo.git " + +stop_git_daemon +test_done diff --git a/t/t5571-pre-push-hook.sh b/t/t5571-pre-push-hook.sh new file mode 100755 index 0000000000..6f9916a390 --- /dev/null +++ b/t/t5571-pre-push-hook.sh @@ -0,0 +1,131 @@ +#!/bin/sh + +test_description='check pre-push hooks' +. ./test-lib.sh + +# Setup hook that always succeeds +HOOKDIR="$(git rev-parse --git-dir)/hooks" +HOOK="$HOOKDIR/pre-push" +mkdir -p "$HOOKDIR" +write_script "$HOOK" <<EOF +cat >/dev/null +exit 0 +EOF + +test_expect_success 'setup' ' + git config push.default upstream && + git init --bare repo1 && + git remote add parent1 repo1 && + test_commit one && + git push parent1 HEAD:foreign +' +write_script "$HOOK" <<EOF +cat >/dev/null +exit 1 +EOF + +COMMIT1="$(git rev-parse HEAD)" +export COMMIT1 + +test_expect_success 'push with failing hook' ' + test_commit two && + test_must_fail git push parent1 HEAD +' + +test_expect_success '--no-verify bypasses hook' ' + git push --no-verify parent1 HEAD +' + +COMMIT2="$(git rev-parse HEAD)" +export COMMIT2 + +write_script "$HOOK" <<'EOF' +echo "$1" >actual +echo "$2" >>actual +cat >>actual +EOF + +cat >expected <<EOF +parent1 +repo1 +refs/heads/master $COMMIT2 refs/heads/foreign $COMMIT1 +EOF + +test_expect_success 'push with hook' ' + git push parent1 master:foreign && + diff expected actual +' + +test_expect_success 'add a branch' ' + git checkout -b other parent1/foreign && + test_commit three +' + +COMMIT3="$(git rev-parse HEAD)" +export COMMIT3 + +cat >expected <<EOF +parent1 +repo1 +refs/heads/other $COMMIT3 refs/heads/foreign $COMMIT2 +EOF + +test_expect_success 'push to default' ' + git push && + diff expected actual +' + +cat >expected <<EOF +parent1 +repo1 +refs/tags/one $COMMIT1 refs/tags/tag1 $_z40 +HEAD~ $COMMIT2 refs/heads/prev $_z40 +EOF + +test_expect_success 'push non-branches' ' + git push parent1 one:tag1 HEAD~:refs/heads/prev && + diff expected actual +' + +cat >expected <<EOF +parent1 +repo1 +(delete) $_z40 refs/heads/prev $COMMIT2 +EOF + +test_expect_success 'push delete' ' + git push parent1 :prev && + diff expected actual +' + +cat >expected <<EOF +repo1 +repo1 +HEAD $COMMIT3 refs/heads/other $_z40 +EOF + +test_expect_success 'push to URL' ' + git push repo1 HEAD && + diff expected actual +' + +# Test that filling pipe buffers doesn't cause failure +# Too slow to leave enabled for general use +if false +then + printf 'parent1\nrepo1\n' >expected + nr=1000 + while test $nr -lt 2000 + do + nr=$(( $nr + 1 )) + git branch b/$nr $COMMIT3 + echo "refs/heads/b/$nr $COMMIT3 refs/heads/b/$nr $_z40" >>expected + done + + test_expect_success 'push many refs' ' + git push parent1 "refs/heads/b/*:refs/heads/b/*" && + diff expected actual + ' +fi + +test_done diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh index ee06d28649..4435693bb2 100755 --- a/t/t5600-clone-fail-cleanup.sh +++ b/t/t5600-clone-fail-cleanup.sh @@ -37,6 +37,16 @@ test_expect_success \ test_expect_success \ 'successful clone must leave the directory' \ - 'cd bar' + 'test -d bar' + +test_expect_success 'failed clone --separate-git-dir should not leave any directories' ' + mkdir foo/.git/objects.bak/ && + mv foo/.git/objects/* foo/.git/objects.bak/ && + test_must_fail git clone --separate-git-dir gitdir foo worktree && + test_must_fail test -e gitdir && + test_must_fail test -e worktree && + mv foo/.git/objects.bak/* foo/.git/objects/ && + rmdir foo/.git/objects.bak +' test_done diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 87ee01662c..1d1c8755ea 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -9,10 +9,13 @@ test_expect_success setup ' rm -fr .git && test_create_repo src && ( - cd src - >file - git add file - git commit -m initial + cd src && + >file && + git add file && + git commit -m initial && + echo 1 >file && + git add file && + git commit -m updated ) ' @@ -33,7 +36,7 @@ test_expect_success 'clone with excess parameters (2)' ' test_expect_success C_LOCALE_OUTPUT 'output from clone' ' rm -fr dst && - git clone -n "file://$(pwd)/src" dst >output && + git clone -n "file://$(pwd)/src" dst >output 2>&1 && test $(grep Clon output | wc -l) = 1 ' @@ -88,6 +91,26 @@ test_expect_success 'clone --mirror' ' ' +test_expect_success 'clone --mirror with detached HEAD' ' + + ( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) && + git clone --mirror src mirror.detached && + ( cd src && git checkout - ) && + GIT_DIR=mirror.detached git rev-parse HEAD >actual && + test_cmp expected actual + +' + +test_expect_success 'clone --bare with detached HEAD' ' + + ( cd src && git checkout HEAD^ && git rev-parse HEAD >../expected ) && + git clone --bare src bare.detached && + ( cd src && git checkout - ) && + GIT_DIR=bare.detached git rev-parse HEAD >actual && + test_cmp expected actual + +' + test_expect_success 'clone --bare names the local repository <name>.git' ' git clone --bare src && @@ -248,4 +271,73 @@ test_expect_success 'clone from original with relative alternate' ' grep /src/\\.git/objects target-10/objects/info/alternates ' +test_expect_success 'clone checking out a tag' ' + git clone --branch=some-tag src dst.tag && + GIT_DIR=src/.git git rev-parse some-tag >expected && + test_cmp expected dst.tag/.git/HEAD && + GIT_DIR=dst.tag/.git git config remote.origin.fetch >fetch.actual && + echo "+refs/heads/*:refs/remotes/origin/*" >fetch.expected && + test_cmp fetch.expected fetch.actual +' + +test_expect_success 'setup ssh wrapper' ' + write_script "$TRASH_DIRECTORY/ssh-wrapper" <<-\EOF && + echo >>"$TRASH_DIRECTORY/ssh-output" "ssh: $*" && + # throw away all but the last argument, which should be the + # command + while test $# -gt 1; do shift; done + eval "$1" + EOF + + GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper" && + export GIT_SSH && + export TRASH_DIRECTORY +' + +clear_ssh () { + >"$TRASH_DIRECTORY/ssh-output" +} + +expect_ssh () { + { + case "$1" in + none) + ;; + *) + echo "ssh: $1 git-upload-pack '$2'" + esac + } >"$TRASH_DIRECTORY/ssh-expect" && + (cd "$TRASH_DIRECTORY" && test_cmp ssh-expect ssh-output) +} + +test_expect_success 'cloning myhost:src uses ssh' ' + clear_ssh && + git clone myhost:src ssh-clone && + expect_ssh myhost src +' + +test_expect_success NOT_MINGW,NOT_CYGWIN 'clone local path foo:bar' ' + clear_ssh && + cp -R src "foo:bar" && + git clone "./foo:bar" foobar && + expect_ssh none +' + +test_expect_success 'bracketed hostnames are still ssh' ' + clear_ssh && + git clone "[myhost:123]:src" ssh-bracket-clone && + expect_ssh myhost:123 src +' + +test_expect_success 'clone from a repository with two identical branches' ' + + ( + cd src && + git checkout -b another master + ) && + git clone src target-11 && + test "z$( cd target-11 && git symbolic-ref HEAD )" = zrefs/heads/another + +' + test_done diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh index c4c375ac04..6537911a43 100755 --- a/t/t5700-clone-reference.sh +++ b/t/t5700-clone-reference.sh @@ -34,7 +34,7 @@ test_expect_success 'cloning with reference (-l -s)' \ cd "$base_dir" test_expect_success 'existence of info/alternates' \ -'test `wc -l <C/.git/objects/info/alternates` = 2' +'test_line_count = 2 C/.git/objects/info/alternates' cd "$base_dir" @@ -52,18 +52,21 @@ test_cmp expected current' cd "$base_dir" -rm -f "$U" +rm -f "$U.D" -test_expect_success 'cloning with reference (no -l -s)' \ -'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>"$U"' +test_expect_success 'cloning with reference (no -l -s)' ' + GIT_TRACE_PACKET=$U.D git clone --reference B "file://$(pwd)/A" D +' -test_expect_success 'fetched no objects' \ -'! grep "^want" "$U"' +test_expect_success 'fetched no objects' ' + test -s "$U.D" && + ! grep " want" "$U.D" +' cd "$base_dir" test_expect_success 'existence of info/alternates' \ -'test `wc -l <D/.git/objects/info/alternates` = 1' +'test_line_count = 1 D/.git/objects/info/alternates' cd "$base_dir" @@ -153,4 +156,46 @@ test_expect_success 'clone with reference from a tagged repository' ' git clone --reference=A A I ' +test_expect_success 'prepare branched repository' ' + git clone A J && + ( + cd J && + git checkout -b other master^ && + echo other >otherfile && + git add otherfile && + git commit -m other && + git checkout master + ) +' + +rm -f "$U.K" + +test_expect_success 'fetch with incomplete alternates' ' + git init K && + echo "$base_dir/A/.git/objects" >K/.git/objects/info/alternates && + ( + cd K && + git remote add J "file://$base_dir/J" && + GIT_TRACE_PACKET=$U.K git fetch J + ) && + master_object=$(cd A && git for-each-ref --format="%(objectname)" refs/heads/master) && + test -s "$U.K" && + ! grep " want $master_object" "$U.K" && + tag_object=$(cd A && git for-each-ref --format="%(objectname)" refs/tags/HEAD) && + ! grep " want $tag_object" "$U.K" +' + +test_expect_success 'clone using repo with gitfile as a reference' ' + git clone --separate-git-dir=L A M && + git clone --reference=M A N && + echo "$base_dir/L/objects" >expected && + test_cmp expected "$base_dir/N/.git/objects/info/alternates" +' + +test_expect_success 'clone using repo pointed at by gitfile as reference' ' + git clone --reference=M/.git A O && + echo "$base_dir/L/objects" >expected && + test_cmp expected "$base_dir/O/.git/objects/info/alternates" +' + test_done diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh index 6972258b27..c4903687fb 100755 --- a/t/t5701-clone-local.sh +++ b/t/t5701-clone-local.sh @@ -3,7 +3,10 @@ test_description='test local clone' . ./test-lib.sh -D=`pwd` +repo_is_hardlinked() { + find "$1/objects" -type f -links 1 >output && + test_line_count = 0 output +} test_expect_success 'preparing origin repository' ' : >file && git add . && git commit -m1 && @@ -19,105 +22,72 @@ test_expect_success 'preparing origin repository' ' ' test_expect_success 'local clone without .git suffix' ' - cd "$D" && git clone -l -s a b && - cd b && + (cd b && test "$(GIT_CONFIG=.git/config git config --bool core.bare)" = false && - git fetch + git fetch) ' test_expect_success 'local clone with .git suffix' ' - cd "$D" && git clone -l -s a.git c && - cd c && - git fetch + (cd c && git fetch) ' test_expect_success 'local clone from x' ' - cd "$D" && git clone -l -s x y && - cd y && - git fetch + (cd y && git fetch) ' test_expect_success 'local clone from x.git that does not exist' ' - cd "$D" && - if git clone -l -s x.git z - then - echo "Oops, should have failed" - false - else - echo happy - fi + test_must_fail git clone -l -s x.git z ' test_expect_success 'With -no-hardlinks, local will make a copy' ' - cd "$D" && git clone --bare --no-hardlinks x w && - cd w && - linked=$(find objects -type f ! -links 1 | wc -l) && - test 0 = $linked + ! repo_is_hardlinked w ' test_expect_success 'Even without -l, local will make a hardlink' ' - cd "$D" && rm -fr w && git clone -l --bare x w && - cd w && - copied=$(find objects -type f -links 1 | wc -l) && - test 0 = $copied + repo_is_hardlinked w ' test_expect_success 'local clone of repo with nonexistent ref in HEAD' ' - cd "$D" && echo "ref: refs/heads/nonexistent" > a.git/HEAD && git clone a d && - cd d && + (cd d && git fetch && - test ! -e .git/refs/remotes/origin/HEAD' + test ! -e .git/refs/remotes/origin/HEAD) +' test_expect_success 'bundle clone without .bundle suffix' ' - cd "$D" && git clone dir/b3 && - cd b3 && - git fetch + (cd b3 && git fetch) ' test_expect_success 'bundle clone with .bundle suffix' ' - cd "$D" && git clone b1.bundle && - cd b1 && - git fetch + (cd b1 && git fetch) ' test_expect_success 'bundle clone from b4' ' - cd "$D" && git clone b4 bdl && - cd bdl && - git fetch + (cd bdl && git fetch) ' test_expect_success 'bundle clone from b4.bundle that does not exist' ' - cd "$D" && - if git clone b4.bundle bb - then - echo "Oops, should have failed" - false - else - echo happy - fi + test_must_fail git clone b4.bundle bb ' test_expect_success 'bundle clone with nonexistent HEAD' ' - cd "$D" && git clone b2.bundle b2 && - cd b2 && + (cd b2 && git fetch && - test ! -e .git/refs/heads/master + test_must_fail git rev-parse --verify refs/heads/master) ' test_expect_success 'clone empty repository' ' - cd "$D" && mkdir empty && (cd empty && git init && @@ -135,7 +105,6 @@ test_expect_success 'clone empty repository' ' ' test_expect_success 'clone empty repository, and then push should not segfault.' ' - cd "$D" && rm -fr empty/ empty-clone/ && mkdir empty && (cd empty && git init) && @@ -145,16 +114,28 @@ test_expect_success 'clone empty repository, and then push should not segfault.' ' test_expect_success 'cloning non-existent directory fails' ' - cd "$D" && rm -rf does-not-exist && test_must_fail git clone does-not-exist ' test_expect_success 'cloning non-git directory fails' ' - cd "$D" && rm -rf not-a-git-repo not-a-git-repo-clone && mkdir not-a-git-repo && test_must_fail git clone not-a-git-repo not-a-git-repo-clone ' +test_expect_success 'cloning file:// does not hardlink' ' + git clone --bare file://"$(pwd)"/a non-local && + ! repo_is_hardlinked non-local +' + +test_expect_success 'cloning a local path with --no-local does not hardlink' ' + git clone --bare --no-local a force-nonlocal && + ! repo_is_hardlinked force-nonlocal +' + +test_expect_success 'cloning locally respects "-u" for fetching refs' ' + test_must_fail git clone --bare -u false a should_not_work.git +' + test_done diff --git a/t/t5702-clone-options.sh b/t/t5702-clone-options.sh index 02cb024723..9e24ec88e6 100755 --- a/t/t5702-clone-options.sh +++ b/t/t5702-clone-options.sh @@ -19,17 +19,19 @@ test_expect_success 'clone -o' ' ' -test_expect_success 'redirected clone' ' +test_expect_success 'redirected clone does not show progress' ' git clone "file://$(pwd)/parent" clone-redirected >out 2>err && - test ! -s err + ! grep % err && + test_i18ngrep ! "Checking connectivity" err ' -test_expect_success 'redirected clone -v' ' + +test_expect_success 'redirected clone -v does show progress' ' git clone --progress "file://$(pwd)/parent" clone-redirected-progress \ >out 2>err && - test -s err + grep % err ' diff --git a/t/t5704-bundle.sh b/t/t5704-bundle.sh index 9e43731fe5..a45c31692e 100755 --- a/t/t5704-bundle.sh +++ b/t/t5704-bundle.sh @@ -58,4 +58,14 @@ test_expect_success 'ridiculously long subject in boundary' ' grep "^-[0-9a-f]\\{40\\} " boundary ' +test_expect_success 'prerequisites with an empty commit message' ' + : >file1 && + git add file1 && + test_tick && + git commit --allow-empty-message -m "" && + test_commit file2 && + git bundle create bundle HEAD^.. && + git bundle verify bundle +' + test_done diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh index f3f9a76056..6e7a7be052 100755 --- a/t/t5706-clone-branch.sh +++ b/t/t5706-clone-branch.sh @@ -20,7 +20,9 @@ test_expect_success 'setup' ' echo one >file && git add file && git commit -m one && git checkout -b two && echo two >file && git add file && git commit -m two && - git checkout master) + git checkout master) && + mkdir empty && + (cd empty && git init) ' test_expect_success 'vanilla clone chooses HEAD' ' @@ -57,12 +59,12 @@ test_expect_success 'clone -b does not munge remotes/origin/HEAD' ' ) ' -test_expect_success 'clone -b with bogus branch chooses HEAD' ' - git clone -b bogus parent clone-bogus && - (cd clone-bogus && - check_HEAD master && - check_file one - ) +test_expect_success 'clone -b with bogus branch' ' + test_must_fail git clone -b bogus parent clone-bogus +' + +test_expect_success 'clone -b not allowed with empty repos' ' + test_must_fail git clone -b branch empty clone-branch-empty ' test_done diff --git a/t/t5709-clone-refspec.sh b/t/t5709-clone-refspec.sh new file mode 100755 index 0000000000..6f1ea984d4 --- /dev/null +++ b/t/t5709-clone-refspec.sh @@ -0,0 +1,156 @@ +#!/bin/sh + +test_description='test refspec written by clone-command' +. ./test-lib.sh + +test_expect_success 'setup' ' + # Make two branches, "master" and "side" + echo one >file && + git add file && + git commit -m one && + echo two >file && + git commit -a -m two && + git tag two && + echo three >file && + git commit -a -m three && + git checkout -b side && + echo four >file && + git commit -a -m four && + git checkout master && + + # default clone + git clone . dir_all && + + # default --single that follows HEAD=master + git clone --single-branch . dir_master && + + # default --single that follows HEAD=side + git checkout side && + git clone --single-branch . dir_side && + + # explicit --single that follows side + git checkout master && + git clone --single-branch --branch side . dir_side2 && + + # default --single with --mirror + git clone --single-branch --mirror . dir_mirror && + + # default --single with --branch and --mirror + git clone --single-branch --mirror --branch side . dir_mirror_side && + + # --single that does not know what branch to follow + git checkout two^ && + git clone --single-branch . dir_detached && + + # explicit --single with tag + git clone --single-branch --branch two . dir_tag && + + # advance both "master" and "side" branches + git checkout side && + echo five >file && + git commit -a -m five && + git checkout master && + echo six >file && + git commit -a -m six && + + # update tag + git tag -d two && git tag two +' + +test_expect_success 'by default all branches will be kept updated' ' + ( + cd dir_all && git fetch && + git for-each-ref refs/remotes/origin | + sed -e "/HEAD$/d" \ + -e "s|/remotes/origin/|/heads/|" >../actual + ) && + # follow both master and side + git for-each-ref refs/heads >expect && + test_cmp expect actual +' + +test_expect_success 'by default no tags will be kept updated' ' + ( + cd dir_all && git fetch && + git for-each-ref refs/tags >../actual + ) && + git for-each-ref refs/tags >expect && + test_must_fail test_cmp expect actual +' + +test_expect_success '--single-branch while HEAD pointing at master' ' + ( + cd dir_master && git fetch && + git for-each-ref refs/remotes/origin | + sed -e "/HEAD$/d" \ + -e "s|/remotes/origin/|/heads/|" >../actual + ) && + # only follow master + git for-each-ref refs/heads/master >expect && + test_cmp expect actual +' + +test_expect_success '--single-branch while HEAD pointing at side' ' + ( + cd dir_side && git fetch && + git for-each-ref refs/remotes/origin | + sed -e "/HEAD$/d" \ + -e "s|/remotes/origin/|/heads/|" >../actual + ) && + # only follow side + git for-each-ref refs/heads/side >expect && + test_cmp expect actual +' + +test_expect_success '--single-branch with explicit --branch side' ' + ( + cd dir_side2 && git fetch && + git for-each-ref refs/remotes/origin | + sed -e "/HEAD$/d" \ + -e "s|/remotes/origin/|/heads/|" >../actual + ) && + # only follow side + git for-each-ref refs/heads/side >expect && + test_cmp expect actual +' + +test_expect_success '--single-branch with explicit --branch with tag fetches updated tag' ' + ( + cd dir_tag && git fetch && + git for-each-ref refs/tags >../actual + ) && + git for-each-ref refs/tags >expect && + test_cmp expect actual +' + +test_expect_success '--single-branch with --mirror' ' + ( + cd dir_mirror && git fetch && + git for-each-ref refs > ../actual + ) && + git for-each-ref refs >expect && + test_cmp expect actual +' + +test_expect_success '--single-branch with explicit --branch and --mirror' ' + ( + cd dir_mirror_side && git fetch && + git for-each-ref refs > ../actual + ) && + git for-each-ref refs >expect && + test_cmp expect actual +' + +test_expect_success '--single-branch with detached' ' + ( + cd dir_detached && git fetch && + git for-each-ref refs/remotes/origin | + sed -e "/HEAD$/d" \ + -e "s|/remotes/origin/|/heads/|" >../actual + ) + # nothing + >expect && + test_cmp expect actual +' + +test_done diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh index ef7127c1b3..5a6e49d18d 100755 --- a/t/t5710-info-alternate.sh +++ b/t/t5710-info-alternate.sh @@ -18,7 +18,7 @@ reachable_via() { test_valid_repo() { git fsck --full > fsck.log && - test `wc -l < fsck.log` = 0 + test_line_count = 0 fsck.log } base_dir=`pwd` @@ -58,7 +58,7 @@ test_expect_success 'creating too deep nesting' \ git clone -l -s D E && git clone -l -s E F && git clone -l -s F G && -git clone -l -s G H' +git clone --bare -l -s G H' test_expect_success 'invalidity of deepest repository' \ 'cd H && { diff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh deleted file mode 100755 index 1c62001fce..0000000000 --- a/t/t5800-remote-helpers.sh +++ /dev/null @@ -1,135 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2010 Sverre Rabbelier -# - -test_description='Test remote-helper import and export commands' - -. ./test-lib.sh - -if ! test_have_prereq PYTHON ; then - skip_all='skipping git-remote-hg tests, python not available' - test_done -fi - -"$PYTHON_PATH" -c ' -import sys -if sys.hexversion < 0x02040000: - sys.exit(1) -' || { - skip_all='skipping git-remote-hg tests, python version < 2.4' - test_done -} - -compare_refs() { - git --git-dir="$1/.git" rev-parse --verify $2 >expect && - git --git-dir="$3/.git" rev-parse --verify $4 >actual && - test_cmp expect actual -} - -test_expect_success 'setup repository' ' - git init --bare server/.git && - git clone server public && - (cd public && - echo content >file && - git add file && - git commit -m one && - git push origin master) -' - -test_expect_success 'cloning from local repo' ' - git clone "testgit::${PWD}/server" localclone && - test_cmp public/file localclone/file -' - -test_expect_success 'cloning from remote repo' ' - git clone "testgit::file://${PWD}/server" clone && - test_cmp public/file clone/file -' - -test_expect_success 'create new commit on remote' ' - (cd public && - echo content >>file && - git commit -a -m two && - git push) -' - -test_expect_success 'pulling from local repo' ' - (cd localclone && git pull) && - test_cmp public/file localclone/file -' - -test_expect_success 'pulling from remote remote' ' - (cd clone && git pull) && - test_cmp public/file clone/file -' - -test_expect_success 'pushing to local repo' ' - (cd localclone && - echo content >>file && - git commit -a -m three && - git push) && - compare_refs localclone HEAD server HEAD -' - -test_expect_success 'synch with changes from localclone' ' - (cd clone && - git pull) -' - -test_expect_success 'pushing remote local repo' ' - (cd clone && - echo content >>file && - git commit -a -m four && - git push) && - compare_refs clone HEAD server HEAD -' - -test_expect_success 'fetch new branch' ' - (cd public && - git checkout -b new && - echo content >>file && - git commit -a -m five && - git push origin new - ) && - (cd localclone && - git fetch origin new - ) && - compare_refs public HEAD localclone FETCH_HEAD -' - -test_expect_success 'fetch multiple branches' ' - (cd localclone && - git fetch - ) && - compare_refs server master localclone refs/remotes/origin/master && - compare_refs server new localclone refs/remotes/origin/new -' - -test_expect_success 'push when remote has extra refs' ' - (cd clone && - echo content >>file && - git commit -a -m six && - git push - ) && - compare_refs clone master server master -' - -test_expect_success 'push new branch by name' ' - (cd clone && - git checkout -b new-name && - echo content >>file && - git commit -a -m seven && - git push origin new-name - ) && - compare_refs clone HEAD server refs/heads/new-name -' - -test_expect_failure 'push new branch with old:new refspec' ' - (cd clone && - git push origin new-name:new-refspec - ) && - compare_refs clone HEAD server refs/heads/new-refspec -' - -test_done diff --git a/t/t5801-remote-helpers.sh b/t/t5801-remote-helpers.sh new file mode 100755 index 0000000000..613f69a254 --- /dev/null +++ b/t/t5801-remote-helpers.sh @@ -0,0 +1,242 @@ +#!/bin/sh +# +# Copyright (c) 2010 Sverre Rabbelier +# + +test_description='Test remote-helper import and export commands' + +. ./test-lib.sh +. "$TEST_DIRECTORY"/lib-gpg.sh + +compare_refs() { + git --git-dir="$1/.git" rev-parse --verify $2 >expect && + git --git-dir="$3/.git" rev-parse --verify $4 >actual && + test_cmp expect actual +} + +test_expect_success 'setup repository' ' + git init server && + (cd server && + echo content >file && + git add file && + git commit -m one) +' + +test_expect_success 'cloning from local repo' ' + git clone "testgit::${PWD}/server" local && + test_cmp server/file local/file +' + +test_expect_success 'create new commit on remote' ' + (cd server && + echo content >>file && + git commit -a -m two) +' + +test_expect_success 'pulling from local repo' ' + (cd local && git pull) && + test_cmp server/file local/file +' + +test_expect_success 'pushing to local repo' ' + (cd local && + echo content >>file && + git commit -a -m three && + git push) && + compare_refs local HEAD server HEAD +' + +test_expect_success 'fetch new branch' ' + (cd server && + git reset --hard && + git checkout -b new && + echo content >>file && + git commit -a -m five + ) && + (cd local && + git fetch origin new + ) && + compare_refs server HEAD local FETCH_HEAD +' + +test_expect_success 'fetch multiple branches' ' + (cd local && + git fetch + ) && + compare_refs server master local refs/remotes/origin/master && + compare_refs server new local refs/remotes/origin/new +' + +test_expect_success 'push when remote has extra refs' ' + (cd local && + git reset --hard origin/master && + echo content >>file && + git commit -a -m six && + git push + ) && + compare_refs local master server master +' + +test_expect_success 'push new branch by name' ' + (cd local && + git checkout -b new-name && + echo content >>file && + git commit -a -m seven && + git push origin new-name + ) && + compare_refs local HEAD server refs/heads/new-name +' + +test_expect_failure 'push new branch with old:new refspec' ' + (cd local && + git push origin new-name:new-refspec + ) && + compare_refs local HEAD server refs/heads/new-refspec +' + +test_expect_success 'cloning without refspec' ' + GIT_REMOTE_TESTGIT_REFSPEC="" \ + git clone "testgit::${PWD}/server" local2 2>error && + grep "This remote helper should implement refspec capability" error && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'pulling without refspecs' ' + (cd local2 && + git reset --hard && + GIT_REMOTE_TESTGIT_REFSPEC="" git pull 2>../error) && + grep "This remote helper should implement refspec capability" error && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'pushing without refspecs' ' + test_when_finished "(cd local2 && git reset --hard origin)" && + (cd local2 && + echo content >>file && + git commit -a -m ten && + GIT_REMOTE_TESTGIT_REFSPEC="" && + export GIT_REMOTE_TESTGIT_REFSPEC && + test_must_fail git push 2>../error) && + grep "remote-helper doesn.t support push; refspec needed" error +' + +test_expect_success 'pulling without marks' ' + (cd local2 && + GIT_REMOTE_TESTGIT_NO_MARKS=1 git pull) && + compare_refs local2 HEAD server HEAD +' + +test_expect_failure 'pushing without marks' ' + test_when_finished "(cd local2 && git reset --hard origin)" && + (cd local2 && + echo content >>file && + git commit -a -m twelve && + GIT_REMOTE_TESTGIT_NO_MARKS=1 git push) && + compare_refs local2 HEAD server HEAD +' + +test_expect_success 'push all with existing object' ' + (cd local && + git branch dup2 master && + git push origin --all + ) && + compare_refs local dup2 server dup2 +' + +test_expect_success 'push ref with existing object' ' + (cd local && + git branch dup master && + git push origin dup + ) && + compare_refs local dup server dup +' + +test_expect_success GPG 'push signed tag' ' + (cd local && + git checkout master && + git tag -s -m signed-tag signed-tag && + git push origin signed-tag + ) && + compare_refs local signed-tag^{} server signed-tag^{} && + test_must_fail compare_refs local signed-tag server signed-tag +' + +test_expect_success GPG 'push signed tag with signed-tags capability' ' + (cd local && + git checkout master && + git tag -s -m signed-tag signed-tag-2 && + GIT_REMOTE_TESTGIT_SIGNED_TAGS=1 git push origin signed-tag-2 + ) && + compare_refs local signed-tag-2 server signed-tag-2 +' + +test_expect_success 'push update refs' ' + (cd local && + git checkout -b update master && + echo update >>file && + git commit -a -m update && + git push origin update && + git rev-parse --verify remotes/origin/update >expect && + git rev-parse --verify testgit/origin/heads/update >actual && + test_cmp expect actual + ) +' + +test_expect_success 'push update refs disabled by no-private-update' ' + (cd local && + echo more-update >>file && + git commit -a -m more-update && + git rev-parse --verify testgit/origin/heads/update >expect && + GIT_REMOTE_TESTGIT_NO_PRIVATE_UPDATE=t git push origin update && + git rev-parse --verify testgit/origin/heads/update >actual && + test_cmp expect actual + ) +' + +test_expect_success 'push update refs failure' ' + (cd local && + git checkout update && + echo "update fail" >>file && + git commit -a -m "update fail" && + git rev-parse --verify testgit/origin/heads/update >expect && + GIT_REMOTE_TESTGIT_PUSH_ERROR="non-fast forward" && + export GIT_REMOTE_TESTGIT_PUSH_ERROR && + test_expect_code 1 git push origin update && + git rev-parse --verify testgit/origin/heads/update >actual && + test_cmp expect actual + ) +' + +test_expect_success 'proper failure checks for fetching' ' + (GIT_REMOTE_TESTGIT_FAILURE=1 && + export GIT_REMOTE_TESTGIT_FAILURE && + cd local && + test_must_fail git fetch 2> error && + cat error && + grep -q "Error while running fast-import" error + ) +' + +test_expect_success 'proper failure checks for pushing' ' + (GIT_REMOTE_TESTGIT_FAILURE=1 && + export GIT_REMOTE_TESTGIT_FAILURE && + cd local && + test_must_fail git push --all + ) +' + +test_expect_success 'push messages' ' + (cd local && + git checkout -b new_branch master && + echo new >>file && + git commit -a -m new && + git push origin new_branch && + git fetch origin && + echo new >>file && + git commit -a -m new && + git push origin new_branch 2> msg && + ! grep "\[new branch\]" msg + ) +' + +test_done diff --git a/t/t5802-connect-helper.sh b/t/t5802-connect-helper.sh new file mode 100755 index 0000000000..878faf2b63 --- /dev/null +++ b/t/t5802-connect-helper.sh @@ -0,0 +1,72 @@ +#!/bin/sh + +test_description='ext::cmd remote "connect" helper' +. ./test-lib.sh + +test_expect_success setup ' + test_tick && + git commit --allow-empty -m initial && + test_tick && + git commit --allow-empty -m second && + test_tick && + git commit --allow-empty -m third && + test_tick && + git tag -a -m "tip three" three && + + test_tick && + git commit --allow-empty -m fourth +' + +test_expect_success clone ' + cmd=$(echo "echo >&2 ext::sh invoked && %S .." | sed -e "s/ /% /g") && + git clone "ext::sh -c %S% ." dst && + git for-each-ref refs/heads/ refs/tags/ >expect && + ( + cd dst && + git config remote.origin.url "ext::sh -c $cmd" && + git for-each-ref refs/heads/ refs/tags/ + ) >actual && + test_cmp expect actual +' + +test_expect_success 'update following tag' ' + test_tick && + git commit --allow-empty -m fifth && + test_tick && + git tag -a -m "tip five" five && + git for-each-ref refs/heads/ refs/tags/ >expect && + ( + cd dst && + git pull && + git for-each-ref refs/heads/ refs/tags/ >../actual + ) && + test_cmp expect actual +' + +test_expect_success 'update backfilled tag' ' + test_tick && + git commit --allow-empty -m sixth && + test_tick && + git tag -a -m "tip two" two three^1 && + git for-each-ref refs/heads/ refs/tags/ >expect && + ( + cd dst && + git pull && + git for-each-ref refs/heads/ refs/tags/ >../actual + ) && + test_cmp expect actual +' + +test_expect_success 'update backfilled tag without primary transfer' ' + test_tick && + git tag -a -m "tip one " one two^1 && + git for-each-ref refs/heads/ refs/tags/ >expect && + ( + cd dst && + git pull && + git for-each-ref refs/heads/ refs/tags/ >../actual + ) && + test_cmp expect actual +' + +test_done diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh index b10685af4e..15e3d6476c 100755 --- a/t/t6000-rev-list-misc.sh +++ b/t/t6000-rev-list-misc.sh @@ -48,4 +48,12 @@ test_expect_success 'rev-list --objects with pathspecs and copied files' ' ! grep one output ' +test_expect_success 'rev-list A..B and rev-list ^A B are the same' ' + git commit --allow-empty -m another && + git tag -a -m "annotated" v1.0 && + git rev-list --objects ^v1.0^ v1.0 >expect && + git rev-list --objects v1.0^..v1.0 >actual && + test_cmp expect actual +' + test_done diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh index fb07536a0f..43ad772484 100755 --- a/t/t6002-rev-list-bisect.sh +++ b/t/t6002-rev-list-bisect.sh @@ -39,25 +39,25 @@ test_bisection_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 +on_committer_date "00:00" hide_error save_tag root unique_commit root tree +on_committer_date "00:01" save_tag l0 unique_commit l0 tree -p root +on_committer_date "00:02" save_tag l1 unique_commit l1 tree -p l0 +on_committer_date "00:03" save_tag l2 unique_commit l2 tree -p l1 +on_committer_date "00:04" save_tag a0 unique_commit a0 tree -p l2 +on_committer_date "00:05" save_tag a1 unique_commit a1 tree -p a0 +on_committer_date "00:06" save_tag b1 unique_commit b1 tree -p a0 +on_committer_date "00:07" save_tag c1 unique_commit c1 tree -p b1 +on_committer_date "00:08" save_tag b2 unique_commit b2 tree -p b1 +on_committer_date "00:09" save_tag b3 unique_commit b2 tree -p b2 +on_committer_date "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2 +on_committer_date "00:11" save_tag c3 unique_commit c3 tree -p c2 +on_committer_date "00:12" save_tag a2 unique_commit a2 tree -p a1 +on_committer_date "00:13" save_tag a3 unique_commit a3 tree -p a2 +on_committer_date "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3 +on_committer_date "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3 +on_committer_date "00:16" save_tag l3 unique_commit l3 tree -p a4 +on_committer_date "00:17" save_tag l4 unique_commit l4 tree -p l3 +on_committer_date "00:18" save_tag l5 unique_commit l5 tree -p l4 git update-ref HEAD $(tag l5) @@ -90,29 +90,29 @@ git update-ref HEAD $(tag l5) # 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 +on_committer_date "00:00" hide_error save_tag F unique_commit F tree +on_committer_date "00:01" save_tag e8 unique_commit e8 tree -p F +on_committer_date "00:02" save_tag e7 unique_commit e7 tree -p e8 +on_committer_date "00:03" save_tag e6 unique_commit e6 tree -p e7 +on_committer_date "00:04" save_tag e5 unique_commit e5 tree -p e6 +on_committer_date "00:05" save_tag f4 unique_commit f4 tree -p F +on_committer_date "00:06" save_tag f3 unique_commit f3 tree -p f4 +on_committer_date "00:07" save_tag f2 unique_commit f2 tree -p f3 +on_committer_date "00:08" save_tag f1 unique_commit f1 tree -p f2 +on_committer_date "00:09" save_tag e4 unique_commit e4 tree -p e5 +on_committer_date "00:10" save_tag e3 unique_commit e3 tree -p e4 +on_committer_date "00:11" save_tag e2 unique_commit e2 tree -p e3 +on_committer_date "00:12" save_tag e1 unique_commit e1 tree -p e2 +on_committer_date "00:13" save_tag E unique_commit E tree -p e1 -p f1 + +on_committer_date "00:00" hide_error save_tag U unique_commit U tree +on_committer_date "00:01" save_tag u0 unique_commit u0 tree -p U +on_committer_date "00:01" save_tag u1 unique_commit u1 tree -p u0 +on_committer_date "00:02" save_tag u2 unique_commit u2 tree -p u0 +on_committer_date "00:03" save_tag u3 unique_commit u3 tree -p u0 +on_committer_date "00:04" save_tag u4 unique_commit u4 tree -p u0 +on_committer_date "00:05" save_tag u5 unique_commit u5 tree -p u0 +on_committer_date "00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5 test_sequence() { diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh index e4c52b0214..24d1836f41 100755 --- a/t/t6003-rev-list-topo-order.sh +++ b/t/t6003-rev-list-topo-order.sh @@ -16,39 +16,34 @@ list_duplicates() 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 +on_dates "00:00" "00:00" hide_error save_tag root unique_commit root tree +on_dates "00:01" "00:01" save_tag l0 unique_commit l0 tree -p root +on_dates "00:02" "00:02" save_tag l1 unique_commit l1 tree -p l0 +on_dates "00:03" "00:03" save_tag l2 unique_commit l2 tree -p l1 +on_dates "00:04" "00:04" save_tag a0 unique_commit a0 tree -p l2 +on_dates "00:05" "00:05" save_tag a1 unique_commit a1 tree -p a0 +on_dates "00:06" "00:06" save_tag b1 unique_commit b1 tree -p a0 +on_dates "00:07" "00:07" save_tag c1 unique_commit c1 tree -p b1 +on_dates "00:08" "00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1 +on_dates "00:09" "00:09" save_tag b3 unique_commit b3 tree -p b2 +on_dates "00:10" "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2 +on_dates "00:11" "00:11" save_tag c3 unique_commit c3 tree -p c2 +on_dates "00:12" "00:00" save_tag a2 unique_commit a2 tree -p a1 +on_dates "00:13" "00:01" save_tag a3 unique_commit a3 tree -p a2 +on_dates "00:14" "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3 +on_dates "00:15" "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3 +on_dates "00:16" "00:16" save_tag l3 unique_commit l3 tree -p a4 +on_dates "00:17" "00:17" save_tag l4 unique_commit l4 tree -p l3 +on_dates "00:18" "00:18" save_tag l5 unique_commit l5 tree -p l4 +on_dates "00:19" "00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3 +on_dates "00:20" "00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4 +on_dates "00:21" "00:21" hide_error save_tag alt_root unique_commit alt_root tree +on_dates "00:22" "00:22" save_tag r0 unique_commit r0 tree -p alt_root +on_dates "00:23" "00:23" save_tag r1 unique_commit r1 tree -p r0 +on_dates "00:24" "00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1 +on_dates "00:25" "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 @@ -105,6 +100,50 @@ l0 root EOF +test_output_expect_success 'simple date order' 'git rev-list --date-order HEAD' <<EOF +l5 +l4 +l3 +a4 +b4 +a3 +a2 +c3 +c2 +b3 +b2 +c1 +b1 +a1 +a0 +l2 +l1 +l0 +root +EOF + +test_output_expect_success 'simple author-date order' 'git rev-list --author-date-order HEAD' <<EOF +l5 +l4 +l3 +a4 +b4 +c3 +c2 +b3 +b2 +c1 +b1 +a3 +a2 +a1 +a0 +l2 +l1 +l0 +root +EOF + test_output_expect_success 'two diamonds topo order (g6)' 'git rev-list --topo-order g4' <<EOF g4 h2 diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index 444279077e..98744038ec 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -1,67 +1,105 @@ #!/bin/sh +# Copyright (c) 2009 Jens Lehmann +# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests) + test_description='git rev-list --pretty=format test' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-terminal.sh test_tick +# String "added" in German +# (translated with Google Translate), +# encoded in UTF-8, used as a commit log message below. +added=$(printf "added (hinzugef\303\274gt) foo") +added_iso88591=$(echo "$added" | iconv -f utf-8 -t iso8859-1) +# same but "changed" +changed=$(printf "changed (ge\303\244ndert) foo") +changed_iso88591=$(echo "$changed" | iconv -f utf-8 -t iso8859-1) + test_expect_success 'setup' ' -touch foo && git add foo && git commit -m "added foo" && - echo changed >foo && git commit -a -m "changed foo" + : >foo && + git add foo && + git config i18n.commitEncoding iso8859-1 && + git commit -m "$added_iso88591" && + head1=$(git rev-parse --verify HEAD) && + head1_short=$(git rev-parse --verify --short $head1) && + tree1=$(git rev-parse --verify HEAD:) && + tree1_short=$(git rev-parse --verify --short $tree1) && + echo "$changed" > foo && + git commit -a -m "$changed_iso88591" && + head2=$(git rev-parse --verify HEAD) && + head2_short=$(git rev-parse --verify --short $head2) && + tree2=$(git rev-parse --verify HEAD:) && + tree2_short=$(git rev-parse --verify --short $tree2) + git config --unset i18n.commitEncoding ' -# usage: test_format name format_string <expected_output -test_format() { +# usage: test_format name format_string [failure] <expected_output +test_format () { cat >expect.$1 - test_expect_success "format $1" " -git rev-list --pretty=format:'$2' master >output.$1 && -test_cmp expect.$1 output.$1 -" + test_expect_${3:-success} "format $1" " + git rev-list --pretty=format:'$2' master >output.$1 && + test_cmp expect.$1 output.$1 + " } -test_format percent %%h <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d +# Feed to --format to provide predictable colored sequences. +AUTO_COLOR='%C(auto,red)foo%C(auto,reset)' +has_color () { + printf '\033[31mfoo\033[m\n' >expect && + test_cmp expect "$1" +} + +has_no_color () { + echo foo >expect && + test_cmp expect "$1" +} + +test_format percent %%h <<EOF +commit $head2 %h -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +commit $head1 %h EOF -test_format hash %H%n%h <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d -131a310eb913d107dd3c09a65d1651175898735d -131a310 -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 -86c75cfd708a0e5868dc876ed5b8bb66c80b4873 -86c75cf +test_format hash %H%n%h <<EOF +commit $head2 +$head2 +$head2_short +commit $head1 +$head1 +$head1_short EOF -test_format tree %T%n%t <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d -fe722612f26da5064c32ca3843aa154bdb0b08a0 -fe72261 -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 -4d5fcadc293a348e88f777dc0920f11e7d71441c -4d5fcad +test_format tree %T%n%t <<EOF +commit $head2 +$tree2 +$tree2_short +commit $head1 +$tree1 +$tree1_short EOF -test_format parents %P%n%p <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d -86c75cfd708a0e5868dc876ed5b8bb66c80b4873 -86c75cf -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +test_format parents %P%n%p <<EOF +commit $head2 +$head1 +$head1_short +commit $head1 EOF # we don't test relative here -test_format author %an%n%ae%n%ad%n%aD%n%at <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d +test_format author %an%n%ae%n%ad%n%aD%n%at <<EOF +commit $head2 A U Thor author@example.com Thu Apr 7 15:13:13 2005 -0700 Thu, 7 Apr 2005 15:13:13 -0700 1112911993 -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +commit $head1 A U Thor author@example.com Thu Apr 7 15:13:13 2005 -0700 @@ -69,14 +107,14 @@ Thu, 7 Apr 2005 15:13:13 -0700 1112911993 EOF -test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d +test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<EOF +commit $head2 C O Mitter committer@example.com Thu Apr 7 15:13:13 2005 -0700 Thu, 7 Apr 2005 15:13:13 -0700 1112911993 -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +commit $head1 C O Mitter committer@example.com Thu Apr 7 15:13:13 2005 -0700 @@ -84,86 +122,155 @@ Thu, 7 Apr 2005 15:13:13 -0700 1112911993 EOF -test_format encoding %e <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +test_format encoding %e <<EOF +commit $head2 +iso8859-1 +commit $head1 +iso8859-1 EOF -test_format subject %s <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d -changed foo -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 -added foo +test_format subject %s <<EOF +commit $head2 +$changed +commit $head1 +$added EOF -test_format body %b <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +test_format body %b <<EOF +commit $head2 +commit $head1 EOF -test_format raw-body %B <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d -changed foo +test_format raw-body %B <<EOF +commit $head2 +$changed -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 -added foo +commit $head1 +$added EOF -test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d +test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<EOF +commit $head2 [31mfoo[32mbar[34mbaz[mxyzzy -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +commit $head1 [31mfoo[32mbar[34mbaz[mxyzzy EOF -test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<'EOF' -commit 131a310eb913d107dd3c09a65d1651175898735d +test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<EOF +commit $head2 [1;31;43mfoo[m -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +commit $head1 [1;31;43mfoo[m EOF -cat >commit-msg <<'EOF' +test_expect_success '%C(auto) does not enable color by default' ' + git log --format=$AUTO_COLOR -1 >actual && + has_no_color actual +' + +test_expect_success '%C(auto) enables colors for color.diff' ' + git -c color.diff=always log --format=$AUTO_COLOR -1 >actual && + has_color actual +' + +test_expect_success '%C(auto) enables colors for color.ui' ' + git -c color.ui=always log --format=$AUTO_COLOR -1 >actual && + has_color actual +' + +test_expect_success '%C(auto) respects --color' ' + git log --format=$AUTO_COLOR -1 --color >actual && + has_color actual +' + +test_expect_success '%C(auto) respects --no-color' ' + git -c color.ui=always log --format=$AUTO_COLOR -1 --no-color >actual && + has_no_color actual +' + +test_expect_success TTY '%C(auto) respects --color=auto (stdout is tty)' ' + ( + TERM=vt100 && export TERM && + test_terminal \ + git log --format=$AUTO_COLOR -1 --color=auto >actual && + has_color actual + ) +' + +test_expect_success '%C(auto) respects --color=auto (stdout not tty)' ' + ( + TERM=vt100 && export TERM && + git log --format=$AUTO_COLOR -1 --color=auto >actual && + has_no_color actual + ) +' + +iconv -f utf-8 -t iso8859-1 > commit-msg <<EOF Test printing of complex bodies This commit message is much longer than the others, and it will be encoded in iso8859-1. We should therefore include an iso8859 character: ¡bueno! EOF + test_expect_success 'setup complex body' ' -git config i18n.commitencoding iso8859-1 && - echo change2 >foo && git commit -a -F commit-msg + git config i18n.commitencoding iso8859-1 && + echo change2 >foo && git commit -a -F commit-msg && + head3=$(git rev-parse --verify HEAD) && + head3_short=$(git rev-parse --short $head3) ' -test_format complex-encoding %e <<'EOF' -commit f58db70b055c5718631e5c61528b28b12090cdea +test_format complex-encoding %e <<EOF +commit $head3 +iso8859-1 +commit $head2 +iso8859-1 +commit $head1 iso8859-1 -commit 131a310eb913d107dd3c09a65d1651175898735d -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 EOF -test_format complex-subject %s <<'EOF' -commit f58db70b055c5718631e5c61528b28b12090cdea +test_format complex-subject %s <<EOF +commit $head3 Test printing of complex bodies -commit 131a310eb913d107dd3c09a65d1651175898735d -changed foo -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 -added foo +commit $head2 +$changed_iso88591 +commit $head1 +$added_iso88591 EOF -test_format complex-body %b <<'EOF' -commit f58db70b055c5718631e5c61528b28b12090cdea -This commit message is much longer than the others, -and it will be encoded in iso8859-1. We should therefore -include an iso8859 character: ¡bueno! +test_expect_success 'prepare expected messages (for test %b)' ' + cat <<-EOF >expected.utf-8 && + commit $head3 + This commit message is much longer than the others, + and it will be encoded in iso8859-1. We should therefore + include an iso8859 character: ¡bueno! + + commit $head2 + commit $head1 + EOF + iconv -f utf-8 -t iso8859-1 expected.utf-8 >expected.iso8859-1 +' + +test_format complex-body %b <expected.iso8859-1 -commit 131a310eb913d107dd3c09a65d1651175898735d -commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +# Git uses i18n.commitEncoding if no i18n.logOutputEncoding set +# so unset i18n.commitEncoding to test encoding conversion +git config --unset i18n.commitEncoding + +test_format complex-subject-commitencoding-unset %s <<EOF +commit $head3 +Test printing of complex bodies +commit $head2 +$changed +commit $head1 +$added EOF +test_format complex-body-commitencoding-unset %b <expected.utf-8 + test_expect_success '%x00 shows NUL' ' - echo >expect commit f58db70b055c5718631e5c61528b28b12090cdea && + echo >expect commit $head3 && echo >>expect fooQbar && git rev-list -1 --format=foo%x00bar HEAD >actual.nul && nul_to_q <actual.nul >actual && @@ -188,34 +295,34 @@ test_expect_success 'empty email' ' test_expect_success 'del LF before empty (1)' ' git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD^^ >actual && - test $(wc -l <actual) = 2 + test_line_count = 2 actual ' test_expect_success 'del LF before empty (2)' ' git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD >actual && - test $(wc -l <actual) = 6 && + test_line_count = 6 actual && grep "^$" actual ' test_expect_success 'add LF before non-empty (1)' ' git show -s --pretty=format:"%s%+b%nThanks%n" HEAD^^ >actual && - test $(wc -l <actual) = 2 + test_line_count = 2 actual ' test_expect_success 'add LF before non-empty (2)' ' git show -s --pretty=format:"%s%+b%nThanks%n" HEAD >actual && - test $(wc -l <actual) = 6 && + test_line_count = 6 actual && grep "^$" actual ' test_expect_success 'add SP before non-empty (1)' ' git show -s --pretty=format:"%s% bThanks" HEAD^^ >actual && - test $(wc -w <actual) = 2 + test $(wc -w <actual) = 3 ' test_expect_success 'add SP before non-empty (2)' ' git show -s --pretty=format:"%s% sThanks" HEAD^^ >actual && - test $(wc -w <actual) = 4 + test $(wc -w <actual) = 6 ' test_expect_success '--abbrev' ' @@ -278,8 +385,16 @@ test_expect_success 'oneline with empty message' ' git commit -m "dummy" --allow-empty && git filter-branch --msg-filter "sed -e s/dummy//" HEAD^^.. && git rev-list --oneline HEAD >test.txt && - test $(git rev-list --oneline HEAD | wc -l) -eq 5 && - test $(git rev-list --oneline --graph HEAD | wc -l) -eq 5 + test_line_count = 5 test.txt && + git rev-list --oneline --graph HEAD >testg.txt && + test_line_count = 5 testg.txt +' + +test_expect_success 'single-character name is parsed correctly' ' + git commit --author="a <a@example.com>" --allow-empty -m foo && + echo "a <a@example.com>" >expect && + git log -1 --format="%an <%ae>" >actual && + test_cmp expect actual ' test_done diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh index 30507407ff..66cda17ef3 100755 --- a/t/t6009-rev-list-parent.sh +++ b/t/t6009-rev-list-parent.sh @@ -133,4 +133,17 @@ test_expect_success 'dodecapus' ' check_revlist "--min-parents=13" && check_revlist "--min-parents=4 --max-parents=11" tetrapus ' + +test_expect_success 'ancestors with the same commit time' ' + + test_tick_keep=$test_tick && + for i in 1 2 3 4 5 6 7 8; do + test_tick=$test_tick_keep + test_commit t$i + done && + git rev-list t1^! --not t$i >result && + >expect && + test_cmp expect result +' + test_done diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh index 839ad97b79..fde5e712eb 100755 --- a/t/t6012-rev-list-simplify.sh +++ b/t/t6012-rev-list-simplify.sh @@ -14,21 +14,24 @@ unnote () { test_expect_success setup ' echo "Hi there" >file && - git add file && - test_tick && git commit -m "Initial file" && + echo "initial" >lost && + git add file lost && + test_tick && git commit -m "Initial file and lost" && note A && git branch other-branch && echo "Hello" >file && - git add file && - test_tick && git commit -m "Modified file" && + echo "second" >lost && + git add file lost && + test_tick && git commit -m "Modified file and lost" && note B && git checkout other-branch && echo "Hello" >file && - git add file && + >lost && + git add file lost && test_tick && git commit -m "Modified the file identically" && note C && @@ -37,7 +40,9 @@ test_expect_success setup ' test_tick && git commit -m "Add another file" && note D && - test_tick && git merge -m "merge" master && + test_tick && + test_must_fail git merge -m "merge" master && + >lost && git commit -a -m "merge" && note E && echo "Yet another" >elif && @@ -56,19 +61,37 @@ test_expect_success setup ' echo "Final change" >file && test_tick && git commit -a -m "Final change" && - note I + note I && + + git symbolic-ref HEAD refs/heads/unrelated && + git rm -f "*" && + echo "Unrelated branch" >side && + git add side && + test_tick && git commit -m "Side root" && + note J && + + git checkout master && + test_tick && git merge -m "Coolest" unrelated && + note K && + + echo "Immaterial" >elif && + git add elif && + test_tick && git commit -m "Last" && + note L ' FMT='tformat:%P %H | %s' -check_result () { +check_outcome () { + outcome=$1 + shift for c in $1 do echo "$c" done >expect && shift && param="$*" && - test_expect_success "log $param" ' + test_expect_$outcome "log $param" ' git log --pretty="$FMT" --parents $param | unnote >actual && sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual && @@ -79,13 +102,35 @@ check_result () { ' } -check_result 'I H G F E D C B A' --full-history -check_result 'I H E C B A' --full-history -- file -check_result 'I H E C B A' --full-history --topo-order -- file -check_result 'I H E C B A' --full-history --date-order -- file +check_result () { + check_outcome success "$@" +} + +check_result 'L K J I H G F E D C B A' --full-history +check_result 'K I H E C B A' --full-history -- file +check_result 'K I H E C B A' --full-history --topo-order -- file +check_result 'K I H E C B A' --full-history --date-order -- file check_result 'I E C B A' --simplify-merges -- file check_result 'I B A' -- file check_result 'I B A' --topo-order -- file check_result 'H' --first-parent -- another-file +check_result 'E C B A' --full-history E -- lost +test_expect_success 'full history simplification without parent' ' + printf "%s\n" E C B A >expect && + git log --pretty="$FMT" --full-history E -- lost | + unnote >actual && + sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual && + test_cmp expect check || { + cat actual + false + } +' + +test_expect_success '--full-diff is not affected by --parents' ' + git log -p --pretty="%H" --full-diff -- file >expected && + git log -p --pretty="%H" --full-diff --parents -- file >actual && + test_cmp expected actual +' + test_done diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh index 39b4cb0ecd..dabebaee0b 100755 --- a/t/t6019-rev-list-ancestry-path.sh +++ b/t/t6019-rev-list-ancestry-path.sh @@ -13,6 +13,13 @@ test_description='--ancestry-path' # # D..M -- M.t == M # --ancestry-path D..M -- M.t == M +# +# F...I == F G H I +# --ancestry-path F...I == F H I +# +# G..M -- G.t == [nothing - was dropped in "-s ours" merge L] +# --ancestry-path G..M -- G.t == L +# --ancestry-path --simplify-merges G^..M -- G.t == G L . ./test-lib.sh @@ -63,13 +70,52 @@ test_expect_success 'rev-list D..M -- M.t' ' test_cmp expect actual ' -test_expect_success 'rev-list --ancestry-patch D..M -- M.t' ' +test_expect_success 'rev-list --ancestry-path D..M -- M.t' ' echo M >expect && git rev-list --ancestry-path --format=%s D..M -- M.t | sed -e "/^commit /d" >actual && test_cmp expect actual ' +test_expect_success 'rev-list F...I' ' + for c in F G H I; do echo $c; done >expect && + git rev-list --format=%s F...I | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +' + +test_expect_success 'rev-list --ancestry-path F...I' ' + for c in F H I; do echo $c; done >expect && + git rev-list --ancestry-path --format=%s F...I | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +' + +# G.t is dropped in an "-s ours" merge +test_expect_success 'rev-list G..M -- G.t' ' + >expect && + git rev-list --format=%s G..M -- G.t | + sed -e "/^commit /d" >actual && + test_cmp expect actual +' + +test_expect_success 'rev-list --ancestry-path G..M -- G.t' ' + echo L >expect && + git rev-list --ancestry-path --format=%s G..M -- G.t | + sed -e "/^commit /d" >actual && + test_cmp expect actual +' + +test_expect_success 'rev-list --ancestry-path --simplify-merges G^..M -- G.t' ' + for c in G L; do echo $c; done >expect && + git rev-list --ancestry-path --simplify-merges --format=%s G^..M -- G.t | + sed -e "/^commit /d" | + sort >actual && + test_cmp expect actual +' + # b---bc # / \ / # a X diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh index 331b9b07d4..d15b313d4b 100755 --- a/t/t6021-merge-criss-cross.sh +++ b/t/t6021-merge-criss-cross.sh @@ -3,7 +3,7 @@ # Copyright (c) 2005 Fredrik Kuivinen # -# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a +# See http://marc.info/?l=git&m=111463358500362&w=2 for a # nice description of what this is about. diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh index 9d8584e957..a89dfbef08 100755 --- a/t/t6022-merge-rename.sh +++ b/t/t6022-merge-rename.sh @@ -242,10 +242,10 @@ test_expect_success 'merge of identical changes in a renamed file' ' rm -f A M N && git reset --hard && git checkout change+rename && - GIT_MERGE_VERBOSITY=3 git merge change | grep "^Skipped B" && + GIT_MERGE_VERBOSITY=3 git merge change | test_i18ngrep "^Skipped B" && git reset --hard HEAD^ && git checkout change && - GIT_MERGE_VERBOSITY=3 git merge change+rename | grep "^Skipped B" + GIT_MERGE_VERBOSITY=3 git merge change+rename | test_i18ngrep "^Skipped B" ' test_expect_success 'setup for rename + d/f conflicts' ' @@ -259,7 +259,7 @@ test_expect_success 'setup for rename + d/f conflicts' ' printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >sub/file && echo foo >dir/file-in-the-way && git add -A && - git commit -m "Common commmit" && + git commit -m "Common commit" && echo 11 >>sub/file && echo more >>dir/file-in-the-way && @@ -303,9 +303,9 @@ test_expect_success 'Rename+D/F conflict; renamed file merges but dir in way' ' git checkout -q renamed-file-has-no-conflicts^0 && test_must_fail git merge --strategy=recursive dir-in-way >output && - grep "CONFLICT (modify/delete): dir/file-in-the-way" output && - grep "Auto-merging dir" output && - grep "Adding as dir~HEAD instead" output && + test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output && + test_i18ngrep "Auto-merging dir" output && + test_i18ngrep "Adding as dir~HEAD instead" output && test 3 -eq "$(git ls-files -u | wc -l)" && test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" && @@ -325,9 +325,9 @@ test_expect_success 'Same as previous, but merged other way' ' test_must_fail git merge --strategy=recursive renamed-file-has-no-conflicts >output 2>errors && ! grep "error: refusing to lose untracked file at" errors && - grep "CONFLICT (modify/delete): dir/file-in-the-way" output && - grep "Auto-merging dir" output && - grep "Adding as dir~renamed-file-has-no-conflicts instead" output && + test_i18ngrep "CONFLICT (modify/delete): dir/file-in-the-way" output && + test_i18ngrep "Auto-merging dir" output && + test_i18ngrep "Adding as dir~renamed-file-has-no-conflicts instead" output && test 3 -eq "$(git ls-files -u | wc -l)" && test 2 -eq "$(git ls-files -u dir/file-in-the-way | wc -l)" && @@ -439,7 +439,7 @@ test_expect_success 'setup both rename source and destination involved in D/F co mkdir one && echo stuff >one/file && git add -A && - git commit -m "Common commmit" && + git commit -m "Common commit" && git mv one/file destdir && git commit -m "Renamed to destdir" && @@ -479,7 +479,7 @@ test_expect_success 'setup pair rename to parent of other (D/F conflicts)' ' echo stuff >one/file && echo other >two/file && git add -A && - git commit -m "Common commmit" && + git commit -m "Common commit" && git rm -rf one && git mv two/file one && @@ -539,7 +539,7 @@ test_expect_success 'setup rename of one file to two, with directories in the wa echo stuff >original && git add -A && - git commit -m "Common commmit" && + git commit -m "Common commit" && mkdir two && >two/file && @@ -583,7 +583,7 @@ test_expect_success 'setup rename one file to two; directories moving out of the mkdir one two && touch one/file two/file && git add -A && - git commit -m "Common commmit" && + git commit -m "Common commit" && git rm -rf one && git mv original one && @@ -618,7 +618,7 @@ test_expect_success 'setup avoid unnecessary update, normal rename' ' printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >original && git add -A && - git commit -m "Common commmit" && + git commit -m "Common commit" && git mv original rename && echo 11 >>rename && @@ -649,7 +649,7 @@ test_expect_success 'setup to test avoiding unnecessary update, with D/F conflic mkdir df && printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" >df/file && git add -A && - git commit -m "Common commmit" && + git commit -m "Common commit" && git mv df/file temp && rm -rf df && @@ -884,4 +884,20 @@ test_expect_success 'no spurious "refusing to lose untracked" message' ' ! grep "refusing to lose untracked file" errors.txt ' +test_expect_success 'do not follow renames for empty files' ' + git checkout -f -b empty-base && + >empty1 && + git add empty1 && + git commit -m base && + echo content >empty1 && + git add empty1 && + git commit -m fill && + git checkout -b empty-topic HEAD^ && + git mv empty1 empty2 && + git commit -m rename && + test_must_fail git merge empty-base && + >expect && + test_cmp expect empty2 +' + test_done diff --git a/t/t6028-merge-up-to-date.sh b/t/t6028-merge-up-to-date.sh index a91644e3b2..c518e9c30c 100755 --- a/t/t6028-merge-up-to-date.sh +++ b/t/t6028-merge-up-to-date.sh @@ -16,7 +16,12 @@ test_expect_success setup ' test_tick && git commit -m second && git tag c1 && - git branch test + git branch test && + echo third >file && + git add file && + test_tick && + git commit -m third && + git tag c2 ' test_expect_success 'merge -s recursive up-to-date' ' @@ -74,4 +79,14 @@ test_expect_success 'merge -s subtree up-to-date' ' ' +test_expect_success 'merge fast-forward octopus' ' + + git reset --hard c0 && + test_tick && + git merge c1 c2 + expect=$(git rev-parse c2) && + current=$(git rev-parse HEAD) && + test "$expect" = "$current" +' + test_done diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 691e4a4481..064f5cefeb 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -164,7 +164,7 @@ test_expect_success 'bisect start: existing ".git/BISECT_START" not modified if cp .git/BISECT_START saved && test_must_fail git bisect start $HASH4 foo -- && git branch > branch.output && - test_i18ngrep "* (no branch)" branch.output > /dev/null && + test_i18ngrep "* (no branch, bisect started on other)" branch.output > /dev/null && test_cmp saved .git/BISECT_START ' test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' ' @@ -190,7 +190,7 @@ test_expect_success 'bisect start: no ".git/BISECT_START" if checkout error' ' # $HASH1 is good, $HASH4 is bad, we skip $HASH3 # but $HASH2 is bad, # so we should find $HASH2 as the first bad commit -test_expect_success 'bisect skip: successfull result' ' +test_expect_success 'bisect skip: successful result' ' git bisect reset && git bisect start $HASH4 $HASH1 && git bisect skip && @@ -480,7 +480,7 @@ test_expect_success 'many merge bases creation' ' git merge -m "merge HASH7 and SIDE_HASH7" "$HASH7" && B_HASH=$(git rev-parse --verify HEAD) && git merge-base --all "$A_HASH" "$B_HASH" > merge_bases.txt && - test $(wc -l < merge_bases.txt) = "2" && + test_line_count = 2 merge_bases.txt && grep "$HASH5" merge_bases.txt && grep "$SIDE_HASH5" merge_bases.txt ' @@ -676,9 +676,7 @@ test_expect_success 'bisect fails if tree is broken on trial commit' ' check_same() { echo "Checking $1 is the same as $2" && - git rev-parse "$1" > expected.same && - git rev-parse "$2" > expected.actual && - test_cmp expected.same expected.actual + test_cmp_rev "$1" "$2" } test_expect_success 'bisect: --no-checkout - start commit bad' ' @@ -743,4 +741,42 @@ test_expect_success 'bisect: demonstrate identification of damage boundary' " git bisect reset " +cat > expected.bisect-log <<EOF +# bad: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>. +# good: [7b7f204a749c3125d5224ed61ea2ae1187ad046f] Add <2: A new day for git> into <hello>. +git bisect start '32a594a3fdac2d57cf6d02987e30eec68511498c' '7b7f204a749c3125d5224ed61ea2ae1187ad046f' +# good: [3de952f2416b6084f557ec417709eac740c6818c] Add <3: Another new day for git> into <hello>. +git bisect good 3de952f2416b6084f557ec417709eac740c6818c +# first bad commit: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>. +EOF + +test_expect_success 'bisect log: successfull result' ' + git bisect reset && + git bisect start $HASH4 $HASH2 && + git bisect good && + git bisect log >bisect-log.txt && + test_cmp expected.bisect-log bisect-log.txt && + git bisect reset +' + +cat > expected.bisect-skip-log <<EOF +# bad: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>. +# good: [7b7f204a749c3125d5224ed61ea2ae1187ad046f] Add <2: A new day for git> into <hello>. +git bisect start '32a594a3fdac2d57cf6d02987e30eec68511498c' '7b7f204a749c3125d5224ed61ea2ae1187ad046f' +# skip: [3de952f2416b6084f557ec417709eac740c6818c] Add <3: Another new day for git> into <hello>. +git bisect skip 3de952f2416b6084f557ec417709eac740c6818c +# only skipped commits left to test +# possible first bad commit: [32a594a3fdac2d57cf6d02987e30eec68511498c] Add <4: Ciao for now> into <hello>. +# possible first bad commit: [3de952f2416b6084f557ec417709eac740c6818c] Add <3: Another new day for git> into <hello>. +EOF + +test_expect_success 'bisect log: only skip commits left' ' + git bisect reset && + git bisect start $HASH4 $HASH2 && + test_must_fail git bisect skip && + git bisect log >bisect-skip-log.txt && + test_cmp expected.bisect-skip-log bisect-skip-log.txt && + git bisect reset +' + test_done diff --git a/t/t6032-merge-large-rename.sh b/t/t6032-merge-large-rename.sh index 94f010be8a..15beecc3c6 100755 --- a/t/t6032-merge-large-rename.sh +++ b/t/t6032-merge-large-rename.sh @@ -97,7 +97,7 @@ test_expect_success 'setup large simple rename' ' test_expect_success 'massive simple rename does not spam added files' ' sane_unset GIT_MERGE_VERBOSITY && git merge --no-stat simple-rename | grep -v Removing >output && - test 5 -gt "$(wc -l < output)" + test_line_count -lt 5 output ' test_done diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh index 2599ae50eb..9324ea4416 100755 --- a/t/t6035-merge-dir-to-symlink.sh +++ b/t/t6035-merge-dir-to-symlink.sh @@ -3,7 +3,7 @@ test_description='merging when a directory was replaced with a symlink' . ./test-lib.sh -test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink' ' +test_expect_success 'create a commit where dir a/b changed to symlink' ' mkdir -p a/b/c a/b-2/c && > a/b/c/d && > a/b-2/c/d && @@ -12,12 +12,12 @@ test_expect_success SYMLINKS 'create a commit where dir a/b changed to symlink' git commit -m base && git tag start && rm -rf a/b && - ln -s b-2 a/b && git add -A && + test_ln_s_add b-2 a/b && git commit -m "dir to symlink" ' -test_expect_success SYMLINKS 'checkout does not clobber untracked symlink' ' +test_expect_success 'checkout does not clobber untracked symlink' ' git checkout HEAD^0 && git reset --hard master && git rm --cached a/b && @@ -25,7 +25,7 @@ test_expect_success SYMLINKS 'checkout does not clobber untracked symlink' ' test_must_fail git checkout start^0 ' -test_expect_success SYMLINKS 'a/b-2/c/d is kept when clobbering symlink b' ' +test_expect_success 'a/b-2/c/d is kept when clobbering symlink b' ' git checkout HEAD^0 && git reset --hard master && git rm --cached a/b && @@ -34,14 +34,14 @@ test_expect_success SYMLINKS 'a/b-2/c/d is kept when clobbering symlink b' ' test -f a/b-2/c/d ' -test_expect_success SYMLINKS 'checkout should not have deleted a/b-2/c/d' ' +test_expect_success 'checkout should not have deleted a/b-2/c/d' ' git checkout HEAD^0 && git reset --hard master && git checkout start^0 && test -f a/b-2/c/d ' -test_expect_success SYMLINKS 'setup for merge test' ' +test_expect_success 'setup for merge test' ' git reset --hard && test -f a/b-2/c/d && echo x > a/x && @@ -50,39 +50,51 @@ test_expect_success SYMLINKS 'setup for merge test' ' git tag baseline ' -test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' ' +test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' ' git reset --hard && git checkout baseline^0 && git merge -s resolve master && - test -h a/b && test -f a/b-2/c/d ' -test_expect_success SYMLINKS 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' ' +test_expect_success SYMLINKS 'a/b was resolved as symlink' ' + test -h a/b +' + +test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' ' git reset --hard && git checkout baseline^0 && git merge -s recursive master && - test -h a/b && test -f a/b-2/c/d ' -test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' ' +test_expect_success SYMLINKS 'a/b was resolved as symlink' ' + test -h a/b +' + +test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' ' git reset --hard && git checkout master^0 && git merge -s resolve baseline^0 && - test -h a/b && test -f a/b-2/c/d ' -test_expect_success SYMLINKS 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' ' +test_expect_success SYMLINKS 'a/b was resolved as symlink' ' + test -h a/b +' + +test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' ' git reset --hard && git checkout master^0 && git merge -s recursive baseline^0 && - test -h a/b && test -f a/b-2/c/d ' -test_expect_failure SYMLINKS 'do not lose untracked in merge (resolve)' ' +test_expect_success SYMLINKS 'a/b was resolved as symlink' ' + test -h a/b +' + +test_expect_failure 'do not lose untracked in merge (resolve)' ' git reset --hard && git checkout baseline^0 && >a/b/c/e && @@ -91,7 +103,7 @@ test_expect_failure SYMLINKS 'do not lose untracked in merge (resolve)' ' test -f a/b-2/c/d ' -test_expect_success SYMLINKS 'do not lose untracked in merge (recursive)' ' +test_expect_success 'do not lose untracked in merge (recursive)' ' git reset --hard && git checkout baseline^0 && >a/b/c/e && @@ -100,52 +112,61 @@ test_expect_success SYMLINKS 'do not lose untracked in merge (recursive)' ' test -f a/b-2/c/d ' -test_expect_success SYMLINKS 'do not lose modifications in merge (resolve)' ' +test_expect_success 'do not lose modifications in merge (resolve)' ' git reset --hard && git checkout baseline^0 && echo more content >>a/b/c/d && test_must_fail git merge -s resolve master ' -test_expect_success SYMLINKS 'do not lose modifications in merge (recursive)' ' +test_expect_success 'do not lose modifications in merge (recursive)' ' git reset --hard && git checkout baseline^0 && echo more content >>a/b/c/d && test_must_fail git merge -s recursive master ' -test_expect_success SYMLINKS 'setup a merge where dir a/b-2 changed to symlink' ' +test_expect_success 'setup a merge where dir a/b-2 changed to symlink' ' git reset --hard && git checkout start^0 && rm -rf a/b-2 && - ln -s b a/b-2 && git add -A && + test_ln_s_add b a/b-2 && git commit -m "dir a/b-2 to symlink" && git tag test2 ' -test_expect_success SYMLINKS 'merge should not have D/F conflicts (resolve)' ' +test_expect_success 'merge should not have D/F conflicts (resolve)' ' git reset --hard && git checkout baseline^0 && git merge -s resolve test2 && - test -h a/b-2 && test -f a/b/c/d ' -test_expect_success SYMLINKS 'merge should not have D/F conflicts (recursive)' ' +test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' ' + test -h a/b-2 +' + +test_expect_success 'merge should not have D/F conflicts (recursive)' ' git reset --hard && git checkout baseline^0 && git merge -s recursive test2 && - test -h a/b-2 && test -f a/b/c/d ' -test_expect_success SYMLINKS 'merge should not have F/D conflicts (recursive)' ' +test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' ' + test -h a/b-2 +' + +test_expect_success 'merge should not have F/D conflicts (recursive)' ' git reset --hard && git checkout -b foo test2 && git merge -s recursive baseline^0 && - test -h a/b-2 && test -f a/b/c/d ' +test_expect_success SYMLINKS 'a/b-2 was resolved as symlink' ' + test -h a/b-2 +' + test_done diff --git a/t/t6037-merge-ours-theirs.sh b/t/t6037-merge-ours-theirs.sh index 2cf42c73f1..3889eca4ae 100755 --- a/t/t6037-merge-ours-theirs.sh +++ b/t/t6037-merge-ours-theirs.sh @@ -53,7 +53,19 @@ test_expect_success 'recursive favouring ours' ' ! grep 1 file ' -test_expect_success 'pull with -X' ' +test_expect_success 'binary file with -Xours/-Xtheirs' ' + echo file binary >.gitattributes && + + git reset --hard master && + git merge -s recursive -X theirs side && + git diff --exit-code side HEAD -- file && + + git reset --hard master && + git merge -s recursive -X ours side && + git diff --exit-code master HEAD -- file +' + +test_expect_success 'pull passes -X to underlying merge' ' git reset --hard master && git pull -s recursive -Xours . side && git reset --hard master && git pull -s recursive -X ours . side && git reset --hard master && git pull -s recursive -Xtheirs . side && diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 19272bc551..7ac8fd06c3 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -28,18 +28,25 @@ test_expect_success setup ' git reset --hard HEAD^ && git checkout -b b4 origin && advance e && - advance f + advance f && + git checkout -b brokenbase origin && + git checkout -b b5 --track brokenbase && + advance g && + git branch -d brokenbase && + git checkout -b b6 origin ) && git checkout -b follower --track master && - advance g + advance h ' -script='s/^..\(b.\)[ 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p' +script='s/^..\(b.\) *[0-9a-f]* \(.*\)$/\1 \2/p' cat >expect <<\EOF -b1 ahead 1, behind 1 -b2 ahead 1, behind 1 -b3 behind 1 -b4 ahead 2 +b1 [ahead 1, behind 1] d +b2 [ahead 1, behind 1] d +b3 [behind 1] b +b4 [ahead 2] f +b5 g +b6 c EOF test_expect_success 'branch -v' ' @@ -52,10 +59,12 @@ test_expect_success 'branch -v' ' ' cat >expect <<\EOF -b1 origin/master: ahead 1, behind 1 -b2 origin/master: ahead 1, behind 1 -b3 origin/master: behind 1 -b4 origin/master: ahead 2 +b1 [origin/master: ahead 1, behind 1] d +b2 [origin/master: ahead 1, behind 1] d +b3 [origin/master: behind 1] b +b4 [origin/master: ahead 2] f +b5 [brokenbase: gone] g +b6 [origin/master] c EOF test_expect_success 'branch -vv' ' @@ -67,34 +76,108 @@ test_expect_success 'branch -vv' ' test_i18ncmp expect actual ' -test_expect_success 'checkout' ' +test_expect_success 'checkout (diverged from upstream)' ' ( cd test && git checkout b1 ) >actual && - grep "have 1 and 1 different" actual + test_i18ngrep "have 1 and 1 different" actual ' test_expect_success 'checkout with local tracked branch' ' git checkout master && git checkout follower >actual && - grep "is ahead of" actual + test_i18ngrep "is ahead of" actual ' -test_expect_success 'status' ' +test_expect_success 'checkout (upstream is gone)' ' + ( + cd test && + git checkout b5 + ) >actual && + test_i18ngrep "is based on .*, but the upstream is gone." actual +' + +test_expect_success 'checkout (up-to-date with upstream)' ' + ( + cd test && git checkout b6 + ) >actual && + test_i18ngrep "Your branch is up-to-date with .origin/master" actual +' + +test_expect_success 'status (diverged from upstream)' ' ( cd test && git checkout b1 >/dev/null && # reports nothing to commit test_must_fail git commit --dry-run ) >actual && - grep "have 1 and 1 different" actual + test_i18ngrep "have 1 and 1 different" actual +' + +test_expect_success 'status (upstream is gone)' ' + ( + cd test && + git checkout b5 >/dev/null && + # reports nothing to commit + test_must_fail git commit --dry-run + ) >actual && + test_i18ngrep "is based on .*, but the upstream is gone." actual +' + +test_expect_success 'status (up-to-date with upstream)' ' + ( + cd test && + git checkout b6 >/dev/null && + # reports nothing to commit + test_must_fail git commit --dry-run + ) >actual && + test_i18ngrep "Your branch is up-to-date with .origin/master" actual +' + +cat >expect <<\EOF +## b1...origin/master [ahead 1, behind 1] +EOF + +test_expect_success 'status -s -b (diverged from upstream)' ' + ( + cd test && + git checkout b1 >/dev/null && + git status -s -b | head -1 + ) >actual && + test_i18ncmp expect actual +' + +cat >expect <<\EOF +## b5...brokenbase [gone] +EOF + +test_expect_success 'status -s -b (upstream is gone)' ' + ( + cd test && + git checkout b5 >/dev/null && + git status -s -b | head -1 + ) >actual && + test_i18ncmp expect actual +' + +cat >expect <<\EOF +## b6...origin/master +EOF + +test_expect_success 'status -s -b (up-to-date with upstream)' ' + ( + cd test && + git checkout b6 >/dev/null && + git status -s -b | head -1 + ) >actual && + test_i18ncmp expect actual ' test_expect_success 'fail to track lightweight tags' ' git checkout master && git tag light && test_must_fail git branch --track lighttrack light >actual && - test_must_fail grep "set up to track" actual && + test_i18ngrep ! "set up to track" actual && test_must_fail git checkout lighttrack ' @@ -102,7 +185,7 @@ test_expect_success 'fail to track annotated tags' ' git checkout master && git tag -m heavy heavy && test_must_fail git branch --track heavytrack heavy >actual && - test_must_fail grep "set up to track" actual && + test_i18ngrep ! "set up to track" actual && test_must_fail git checkout heavytrack ' diff --git a/t/t6042-merge-rename-corner-cases.sh b/t/t6042-merge-rename-corner-cases.sh index 32591f9413..411550d2b6 100755 --- a/t/t6042-merge-rename-corner-cases.sh +++ b/t/t6042-merge-rename-corner-cases.sh @@ -104,7 +104,7 @@ test_expect_failure 'conflict caused if rename not detected' ' test 0 -eq $(git ls-files -u | wc -l) && test 0 -eq $(git ls-files -o | wc -l) && - test 6 -eq $(wc -l < c) && + test_line_count = 6 c && test $(git rev-parse HEAD:a) = $(git rev-parse B:a) && test $(git rev-parse HEAD:b) = $(git rev-parse A:b) ' @@ -380,7 +380,7 @@ test_expect_success 'handle rename/rename (2to1) conflict correctly' ' git checkout B^0 && test_must_fail git merge -s recursive C^0 >out && - grep "CONFLICT (rename/rename)" out && + test_i18ngrep "CONFLICT (rename/rename)" out && test 2 -eq $(git ls-files -s | wc -l) && test 2 -eq $(git ls-files -u | wc -l) && diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh index 5c87f28e4e..7d47984352 100755 --- a/t/t6050-replace.sh +++ b/t/t6050-replace.sh @@ -122,9 +122,9 @@ test_expect_success '"git replace" listing and deleting' ' test "$HASH2" = "$(git replace -l)" && test "$HASH2" = "$(git replace)" && aa=${HASH2%??????????????????????????????????????} && - test "$HASH2" = "$(git replace -l "$aa*")" && + test "$HASH2" = "$(git replace --list "$aa*")" && test_must_fail git replace -d $R && - test_must_fail git replace -d && + test_must_fail git replace --delete && test_must_fail git replace -l -d $HASH2 && git replace -d $HASH2 && git show $HASH2 | grep "A U Thor" && @@ -140,6 +140,17 @@ test_expect_success '"git replace" replacing' ' test "$HASH2" = "$(git replace)" ' +test_expect_success '"git replace" resolves sha1' ' + SHORTHASH2=$(git rev-parse --short=8 $HASH2) && + git replace -d $SHORTHASH2 && + git replace $SHORTHASH2 $R && + git show $HASH2 | grep "O Thor" && + test_must_fail git replace $HASH2 $R && + git replace -f $HASH2 $R && + test_must_fail git replace --force && + test "$HASH2" = "$(git replace)" +' + # This creates a side branch where the bug in H2 # does not appear because P2 is created by applying # H2 and squashing H5 into it. @@ -252,4 +263,23 @@ test_expect_success 'not just commits' ' test_cmp file.replaced file ' +test_expect_success 'replaced and replacement objects must be of the same type' ' + test_must_fail git replace mytag $HASH1 && + test_must_fail git replace HEAD^{tree} HEAD~1 && + BLOB=$(git rev-parse :file) && + test_must_fail git replace HEAD^ $BLOB +' + +test_expect_success '-f option bypasses the type check' ' + git replace -f mytag $HASH1 && + git replace --force HEAD^{tree} HEAD~1 && + git replace -f HEAD^ $BLOB +' + +test_expect_success 'replace ref cleanup' ' + test -n "$(git replace)" && + git replace -d $(git replace) && + test -z "$(git replace)" +' + test_done diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh index e673c25e94..7ea14ced31 100755 --- a/t/t6101-rev-parse-parents.sh +++ b/t/t6101-rev-parse-parents.sh @@ -6,39 +6,86 @@ test_description='Test git rev-parse with different parent options' . ./test-lib.sh -. "$TEST_DIRECTORY"/lib-t6000.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_success '--verify start2^1' 'test_must_fail git rev-parse --verify start2^1' -test_expect_success '--verify start2^0' 'git rev-parse --verify start2^0' -test_expect_success 'final^1^@ = final^1^1 final^1^2' "test \"$(git rev-parse final^1^@)\" = \"$(git rev-parse final^1^1 final^1^2)\"" -test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' "test \"$(git rev-parse final^1^\!)\" = \"$(git rev-parse final^1 ^final^1^1 ^final^1^2)\"" - -test_expect_success 'repack for next test' 'git repack -a -d' + +test_cmp_rev_output () { + git rev-parse --verify "$1" >expect && + eval "$2" >actual && + test_cmp expect actual +} + +test_expect_success 'setup' ' + test_commit start && + test_commit second && + git checkout --orphan tmp && + test_commit start2 && + git checkout master && + git merge -m next start2 && + test_commit final +' + +test_expect_success 'start is valid' ' + git rev-parse start | grep "^[0-9a-f]\{40\}$" +' + +test_expect_success 'start^0' ' + test_cmp_rev_output tags/start "git rev-parse start^0" +' + +test_expect_success 'start^1 not valid' ' + test_must_fail git rev-parse --verify start^1 +' + +test_expect_success 'second^1 = second^' ' + test_cmp_rev_output second^ "git rev-parse second^1" +' + +test_expect_success 'final^1^1^1' ' + test_cmp_rev_output start "git rev-parse final^1^1^1" +' + +test_expect_success 'final^1^1^1 = final^^^' ' + test_cmp_rev_output final^^^ "git rev-parse final^1^1^1" +' + +test_expect_success 'final^1^2' ' + test_cmp_rev_output 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' ' + test_must_fail git rev-parse --verify final^1^3 +' + +test_expect_success '--verify start2^1' ' + test_must_fail git rev-parse --verify start2^1 +' + +test_expect_success '--verify start2^0' ' + git rev-parse --verify start2^0 +' + +test_expect_success 'final^1^@ = final^1^1 final^1^2' ' + git rev-parse final^1^1 final^1^2 >expect && + git rev-parse final^1^@ >actual && + test_cmp expect actual +' + +test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' ' + git rev-parse final^1 ^final^1^1 ^final^1^2 >expect && + git rev-parse final^1^! >actual && + test_cmp expect actual +' + +test_expect_success 'repack for next test' ' + git repack -a -d +' + test_expect_success 'short SHA-1 works' ' - start=`git rev-parse --verify start` && - echo $start && - abbrv=`echo $start | sed s/.\$//` && - echo $abbrv && - abbrv=`git rev-parse --verify $abbrv` && - echo $abbrv && - test $start = $abbrv' + start=$(git rev-parse --verify start) && + test_cmp_rev_output start "git rev-parse ${start%?}" +' test_done diff --git a/t/t6111-rev-list-treesame.sh b/t/t6111-rev-list-treesame.sh new file mode 100755 index 0000000000..88b84dfa73 --- /dev/null +++ b/t/t6111-rev-list-treesame.sh @@ -0,0 +1,196 @@ +#!/bin/sh +# +# ,---E--. *H----------. * marks !TREESAME parent paths +# / \ / \* +# *A--*B---D--*F-*G---------K-*L-*M +# \ /* \ / +# `-C-' `-*I-*J +# +# A creates "file", B and F change it. +# Odd merge G takes the old version from B. +# I changes it, but J reverts it, so K is TREESAME to both parents. +# H and L both change "file", and M merges those changes. + +test_description='TREESAME and limiting' + +. ./test-lib.sh + +note () { + git tag "$1" +} + +unnote () { + git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\))\([ ]\)|\1\2|g" +} + +test_expect_success setup ' + test_commit "Initial file" file "Hi there" A && + git branch other-branch && + + test_commit "file=Hello" file "Hello" B && + git branch third-branch && + + git checkout other-branch && + test_commit "Added other" other "Hello" C && + + git checkout master && + test_merge D other-branch && + + git checkout third-branch && + test_commit "Third file" third "Nothing" E && + + git checkout master && + test_commit "file=Blah" file "Blah" F && + + test_tick && git merge --no-commit third-branch && + git checkout third-branch file && + git commit && + note G && + git branch fiddler-branch && + + git checkout -b part2-branch && + test_commit "file=Part 2" file "Part 2" H && + + git checkout fiddler-branch && + test_commit "Bad commit" file "Silly" I && + + test_tick && git revert I && note J && + + git checkout master && + test_tick && git merge --no-ff fiddler-branch && + note K + + test_commit "file=Part 1" file "Part 1" L && + + test_tick && test_must_fail git merge part2-branch && + test_commit M file "Parts 1+2" +' + +check_outcome () { + outcome=$1 + shift + + case "$1" in + *"("*) + FMT="%P %H | %s" + munge_actual=" + s/^\([^ ]*\) \([^ ]*\) .*/(\1)\2/ + s/ //g + s/()// + " + ;; + *) + FMT="%H | %s" + munge_actual="s/^\([^ ]*\) .*/\1/" + ;; + esac && + printf "%s\n" $1 >expect && + shift + + param="$*" && + test_expect_$outcome "log $param" ' + git log --format="$FMT" $param | + unnote >actual && + sed -e "$munge_actual" <actual >check && + test_cmp expect check || { + cat actual + false + } + ' +} + +check_result () { + check_outcome success "$@" +} + +# Odd merge G drops a change in F. Important that G is listed in all +# except the most basic list. Achieving this means normal merge D will also be +# shown in normal full-history, as we can't distinguish unless we do a +# simplification pass. After simplification, D is dropped but G remains. +# Also, merge simplification of G should not drop the parent B that the default +# simple history follows. +check_result 'M L K J I H G F E D C B A' +check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FE)G (D)F (B)E (BC)D (A)C (A)B A' +check_result 'M H L K J I G E F D C B A' --topo-order +check_result 'M L H B A' -- file +check_result '(LH)M (B)L (B)H (A)B A' --parents -- file +check_result 'M L J I H G F D B A' --full-history -- file +check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FB)G (D)F (BA)D (A)B A' --full-history --parents -- file +check_result '(LH)M (G)H (J)L (I)J (G)I (FB)G (B)F (A)B A' --simplify-merges -- file +check_result 'M L K G F D B A' --first-parent +check_result 'M L G F B A' --first-parent -- file + +# Check that odd merge G remains shown when F is the bottom. +check_result 'M L K J I H G E' F..M +check_result 'M H L K J I G E' F..M --topo-order +check_result 'M L H' F..M -- file +check_result '(LH)M (B)L (B)H' --parents F..M -- file +check_result 'M L J I H G' F..M --full-history -- file +check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FB)G' F..M --full-history --parents -- file +check_result '(LH)M (G)H (J)L (I)J (G)I (FB)G' F..M --simplify-merges -- file +check_result 'M L K J I H G' F..M --ancestry-path +check_result 'M L J I H G' F..M --ancestry-path -- file +check_result '(LH)M (K)L (GJ)K (I)J (G)I (G)H (FE)G' F..M --ancestry-path --parents -- file +check_result '(LH)M (G)H (J)L (I)J (G)I (FE)G' F..M --ancestry-path --simplify-merges -- file +check_result 'M L K G' F..M --first-parent +check_result 'M L G' F..M --first-parent -- file + +# Note that G is pruned when E is the bottom, even if it's the same commit list +# If we want history since E, then we're quite happy to ignore G that took E. +check_result 'M L K J I H G' E..M --ancestry-path +check_result 'M L J I H' E..M --ancestry-path -- file +check_result '(LH)M (K)L (EJ)K (I)J (E)I (E)H' E..M --ancestry-path --parents -- file +check_result '(LH)M (E)H (J)L (I)J (E)I' E..M --ancestry-path --simplify-merges -- file + +# Should still be able to ignore I-J branch in simple log, despite limiting +# to G. +check_result 'M L K J I H' G..M +check_result 'M H L K J I' G..M --topo-order +check_result 'M L H' G..M -- file +check_result '(LH)M (G)L (G)H' G..M --parents -- file +check_result 'M L J I H' G..M --full-history -- file +check_result 'M L K J I H' G..M --full-history --parents -- file +check_result 'M H L J I' G..M --simplify-merges -- file +check_result 'M L K J I H' G..M --ancestry-path +check_result 'M L J I H' G..M --ancestry-path -- file +check_result 'M L K J I H' G..M --ancestry-path --parents -- file +check_result 'M H L J I' G..M --ancestry-path --simplify-merges -- file + +# B..F should be able to simplify the merge D from irrelevant side branch C. +# Default log should also be free to follow B-D, and ignore C. +# But --full-history shouldn't drop D on its own - without simplification, +# we can't decide if the merge from INTERESTING commit C was sensible. +check_result 'F D C' B..F +check_result 'F' B..F -- file +check_result '(B)F' B..F --parents -- file +check_result 'F D' B..F --full-history -- file +check_result '(D)F (BA)D' B..F --full-history --parents -- file +check_result '(B)F' B..F --simplify-merges -- file +check_result 'F D' B..F --ancestry-path +check_result 'F' B..F --ancestry-path -- file +check_result 'F' B..F --ancestry-path --parents -- file +check_result 'F' B..F --ancestry-path --simplify-merges -- file +check_result 'F D' B..F --first-parent +check_result 'F' B..F --first-parent -- file + +# E...F should be equivalent to E F ^B, and be able to drop D as above. +check_result 'F' E F ^B -- file # includes D +check_result 'F' E...F -- file # includes D + +# Any sort of full history of C..F should show D, as it's the connection to C, +# and it differs from it. +check_result 'F D B' C..F +check_result 'F B' C..F -- file +check_result '(B)F (A)B' C..F --parents -- file +check_result 'F D B' C..F --full-history -- file +check_result '(D)F (BC)D (A)B' C..F --full-history --parents -- file +check_result '(D)F (BC)D (A)B' C..F --simplify-merges -- file +check_result 'F D' C..F --ancestry-path +check_result 'F D' C..F --ancestry-path -- file +check_result 'F D' C..F --ancestry-path --parents -- file +check_result 'F D' C..F --ancestry-path --simplify-merges -- file +check_result 'F D B' C..F --first-parent +check_result 'F B' C..F --first-parent -- file + + +test_done diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index f67aa6ff6a..c0e5b2a627 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -110,6 +110,9 @@ check_describe tags/e --all HEAD^^^ check_describe B-0-* --long HEAD^^2^ check_describe A-3-* --long HEAD^^2 +check_describe c-7-* --tags +check_describe e-3-* --first-parent --tags + : >err.expect check_describe A --all A^0 test_expect_success 'no warning was displayed for A' ' @@ -171,4 +174,28 @@ check_describe "test2-lightweight-*" --tags --match="test2-*" check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^ +test_expect_success 'name-rev with exact tags' ' + echo A >expect && + tag_object=$(git rev-parse refs/tags/A) && + git name-rev --tags --name-only $tag_object >actual && + test_cmp expect actual && + + echo "A^0" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + git name-rev --tags --name-only $tagged_commit >actual && + test_cmp expect actual +' + +test_expect_success 'describe --contains with the exact tags' ' + echo "A^0" >expect && + tag_object=$(git rev-parse refs/tags/A) && + git describe --contains $tag_object >actual && + test_cmp expect actual && + + echo "A^0" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + git describe --contains $tagged_commit >actual && + test_cmp expect actual +' + test_done diff --git a/t/t6130-pathspec-noglob.sh b/t/t6130-pathspec-noglob.sh new file mode 100755 index 0000000000..658353277e --- /dev/null +++ b/t/t6130-pathspec-noglob.sh @@ -0,0 +1,162 @@ +#!/bin/sh + +test_description='test globbing (and noglob) of pathspec limiting' +. ./test-lib.sh + +test_expect_success 'create commits with glob characters' ' + test_commit unrelated bar && + test_commit vanilla foo && + # insert file "f*" in the commit, but in a way that avoids + # the name "f*" in the worktree, because it is not allowed + # on Windows (the tests below do not depend on the presence + # of the file in the worktree) + git update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:foo)" "f*" && + test_tick && + git commit -m star && + test_commit bracket "f[o][o]" +' + +test_expect_success 'vanilla pathspec matches literally' ' + echo vanilla >expect && + git log --format=%s -- foo >actual && + test_cmp expect actual +' + +test_expect_success 'star pathspec globs' ' + cat >expect <<-\EOF && + bracket + star + vanilla + EOF + git log --format=%s -- "f*" >actual && + test_cmp expect actual +' + +test_expect_success 'star pathspec globs' ' + cat >expect <<-\EOF && + bracket + star + vanilla + EOF + git log --format=%s -- ":(glob)f*" >actual && + test_cmp expect actual +' + +test_expect_success 'bracket pathspec globs and matches literal brackets' ' + cat >expect <<-\EOF && + bracket + vanilla + EOF + git log --format=%s -- "f[o][o]" >actual && + test_cmp expect actual +' + +test_expect_success 'bracket pathspec globs and matches literal brackets' ' + cat >expect <<-\EOF && + bracket + vanilla + EOF + git log --format=%s -- ":(glob)f[o][o]" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option matches literally (vanilla)' ' + echo vanilla >expect && + git --literal-pathspecs log --format=%s -- foo >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option matches literally (vanilla)' ' + echo vanilla >expect && + git log --format=%s -- ":(literal)foo" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option matches literally (star)' ' + echo star >expect && + git --literal-pathspecs log --format=%s -- "f*" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option matches literally (star)' ' + echo star >expect && + git log --format=%s -- ":(literal)f*" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option matches literally (bracket)' ' + echo bracket >expect && + git --literal-pathspecs log --format=%s -- "f[o][o]" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option matches literally (bracket)' ' + echo bracket >expect && + git log --format=%s -- ":(literal)f[o][o]" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob option disables :(literal)' ' + : >expect && + git --literal-pathspecs log --format=%s -- ":(literal)foo" >actual && + test_cmp expect actual +' + +test_expect_success 'no-glob environment variable works' ' + echo star >expect && + GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual && + test_cmp expect actual +' + +test_expect_success 'blame takes global pathspec flags' ' + git --literal-pathspecs blame -- foo && + git --icase-pathspecs blame -- foo && + git --glob-pathspecs blame -- foo && + git --noglob-pathspecs blame -- foo +' + +test_expect_success 'setup xxx/bar' ' + mkdir xxx && + test_commit xxx xxx/bar +' + +test_expect_success '**/ works with :(glob)' ' + cat >expect <<-\EOF && + xxx + unrelated + EOF + git log --format=%s -- ":(glob)**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ does not work with --noglob-pathspecs' ' + : >expect && + git --noglob-pathspecs log --format=%s -- "**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ works with :(glob) and --noglob-pathspecs' ' + cat >expect <<-\EOF && + xxx + unrelated + EOF + git --noglob-pathspecs log --format=%s -- ":(glob)**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ works with --glob-pathspecs' ' + cat >expect <<-\EOF && + xxx + unrelated + EOF + git --glob-pathspecs log --format=%s -- "**/bar" >actual && + test_cmp expect actual +' + +test_expect_success '**/ does not work with :(literal) and --glob-pathspecs' ' + : >expect && + git --glob-pathspecs log --format=%s -- ":(literal)**/bar" >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh new file mode 100755 index 0000000000..a7c7ff5f49 --- /dev/null +++ b/t/t6131-pathspec-icase.sh @@ -0,0 +1,109 @@ +#!/bin/sh + +test_description='test case insensitive pathspec limiting' +. ./test-lib.sh + +if test_have_prereq CASE_INSENSITIVE_FS +then + skip_all='skipping case sensitive tests - case insensitive file system' + test_done +fi + +test_expect_success 'create commits with glob characters' ' + test_commit bar bar && + test_commit bAr bAr && + test_commit BAR BAR && + mkdir foo && + test_commit foo/bar foo/bar && + test_commit foo/bAr foo/bAr && + test_commit foo/BAR foo/BAR && + mkdir fOo && + test_commit fOo/bar fOo/bar && + test_commit fOo/bAr fOo/bAr && + test_commit fOo/BAR fOo/BAR && + mkdir FOO && + test_commit FOO/bar FOO/bar && + test_commit FOO/bAr FOO/bAr && + test_commit FOO/BAR FOO/BAR +' + +test_expect_success 'tree_entry_interesting matches bar' ' + echo bar >expect && + git log --format=%s -- "bar" >actual && + test_cmp expect actual +' + +test_expect_success 'tree_entry_interesting matches :(icase)bar' ' + cat <<-EOF >expect && + BAR + bAr + bar + EOF + git log --format=%s -- ":(icase)bar" >actual && + test_cmp expect actual +' + +test_expect_success 'tree_entry_interesting matches :(icase)bar with prefix' ' + cat <<-EOF >expect && + fOo/BAR + fOo/bAr + fOo/bar + EOF + ( cd fOo && git log --format=%s -- ":(icase)bar" ) >actual && + test_cmp expect actual +' + +test_expect_success 'tree_entry_interesting matches :(icase)bar with empty prefix' ' + cat <<-EOF >expect && + FOO/BAR + FOO/bAr + FOO/bar + fOo/BAR + fOo/bAr + fOo/bar + foo/BAR + foo/bAr + foo/bar + EOF + ( cd fOo && git log --format=%s -- ":(icase)../foo/bar" ) >actual && + test_cmp expect actual +' + +test_expect_success 'match_pathspec_depth matches :(icase)bar' ' + cat <<-EOF >expect && + BAR + bAr + bar + EOF + git ls-files ":(icase)bar" >actual && + test_cmp expect actual +' + +test_expect_success 'match_pathspec_depth matches :(icase)bar with prefix' ' + cat <<-EOF >expect && + fOo/BAR + fOo/bAr + fOo/bar + EOF + ( cd fOo && git ls-files --full-name ":(icase)bar" ) >actual && + test_cmp expect actual +' + +test_expect_success 'match_pathspec_depth matches :(icase)bar with empty prefix' ' + cat <<-EOF >expect && + bar + fOo/BAR + fOo/bAr + fOo/bar + EOF + ( cd fOo && git ls-files --full-name ":(icase)bar" ../bar ) >actual && + test_cmp expect actual +' + +test_expect_success '"git diff" can take magic :(icase) pathspec' ' + echo FOO/BAR >expect && + git diff --name-only HEAD^ HEAD -- ":(icase)foo/bar" >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh index 9a16806921..54b5744cc5 100755 --- a/t/t6200-fmt-merge-msg.sh +++ b/t/t6200-fmt-merge-msg.sh @@ -35,15 +35,18 @@ test_expect_success setup ' echo "l3" >two && test_tick && - git commit -a -m "Left #3" && + GIT_COMMITTER_NAME="Another Committer" \ + GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #3" && echo "l4" >two && test_tick && - git commit -a -m "Left #4" && + GIT_COMMITTER_NAME="Another Committer" \ + GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #4" && echo "l5" >two && test_tick && - git commit -a -m "Left #5" && + GIT_COMMITTER_NAME="Another Committer" \ + GIT_AUTHOR_NAME="Another Author" git commit -a -m "Left #5" && git tag tag-l5 && git checkout right && @@ -99,6 +102,8 @@ test_expect_success '[merge] summary/log configuration' ' cat >expected <<-EOF && Merge branch ${apos}left${apos} + # By Another Author (3) and A U Thor (2) + # Via Another Committer * left: Left #5 Left #4 @@ -107,8 +112,8 @@ test_expect_success '[merge] summary/log configuration' ' Common #1 EOF - git config merge.log true && - test_might_fail git config --unset-all merge.summary && + test_config merge.log true && + test_unconfig merge.summary && git checkout master && test_tick && @@ -116,8 +121,8 @@ test_expect_success '[merge] summary/log configuration' ' git fmt-merge-msg <.git/FETCH_HEAD >actual1 && - test_might_fail git config --unset-all merge.log && - git config merge.summary true && + test_unconfig merge.log && + test_config merge.summary true && git checkout master && test_tick && @@ -129,11 +134,6 @@ test_expect_success '[merge] summary/log configuration' ' test_cmp expected actual2 ' -test_expect_success 'setup: clear [merge] configuration' ' - test_might_fail git config --unset-all merge.log && - test_might_fail git config --unset-all merge.summary -' - test_expect_success 'setup FETCH_HEAD' ' git checkout master && test_tick && @@ -144,6 +144,8 @@ test_expect_success 'merge.log=3 limits shortlog length' ' cat >expected <<-EOF && Merge branch ${apos}left${apos} + # By Another Author (3) and A U Thor (2) + # Via Another Committer * left: (5 commits) Left #5 Left #4 @@ -159,6 +161,8 @@ test_expect_success 'merge.log=5 shows all 5 commits' ' cat >expected <<-EOF && Merge branch ${apos}left${apos} + # By Another Author (3) and A U Thor (2) + # Via Another Committer * left: Left #5 Left #4 @@ -171,6 +175,24 @@ test_expect_success 'merge.log=5 shows all 5 commits' ' test_cmp expected actual ' +test_expect_success '--log=5 with custom comment character' ' + cat >expected <<-EOF && + Merge branch ${apos}left${apos} + + x By Another Author (3) and A U Thor (2) + x Via Another Committer + * left: + Left #5 + Left #4 + Left #3 + Common #2 + Common #1 + EOF + + git -c core.commentchar="x" fmt-merge-msg --log=5 <.git/FETCH_HEAD >actual && + test_cmp expected actual +' + test_expect_success 'merge.log=0 disables shortlog' ' echo "Merge branch ${apos}left${apos}" >expected git -c merge.log=0 fmt-merge-msg <.git/FETCH_HEAD >actual && @@ -181,6 +203,8 @@ test_expect_success '--log=3 limits shortlog length' ' cat >expected <<-EOF && Merge branch ${apos}left${apos} + # By Another Author (3) and A U Thor (2) + # Via Another Committer * left: (5 commits) Left #5 Left #4 @@ -196,6 +220,8 @@ test_expect_success '--log=5 shows all 5 commits' ' cat >expected <<-EOF && Merge branch ${apos}left${apos} + # By Another Author (3) and A U Thor (2) + # Via Another Committer * left: Left #5 Left #4 @@ -225,6 +251,8 @@ test_expect_success 'fmt-merge-msg -m' ' cat >expected.log <<-EOF && Sync with left + # By Another Author (3) and A U Thor (2) + # Via Another Committer * ${apos}left${apos} of $(pwd): Left #5 Left #4 @@ -233,14 +261,14 @@ test_expect_success 'fmt-merge-msg -m' ' Common #1 EOF - test_might_fail git config --unset merge.log && - test_might_fail git config --unset merge.summary && + test_unconfig merge.log && + test_unconfig merge.summary && git checkout master && git fetch "$(pwd)" left && git fmt-merge-msg -m "Sync with left" <.git/FETCH_HEAD >actual && git fmt-merge-msg --log -m "Sync with left" \ <.git/FETCH_HEAD >actual.log && - git config merge.log true && + test_config merge.log true && git fmt-merge-msg -m "Sync with left" \ <.git/FETCH_HEAD >actual.log-config && git fmt-merge-msg --no-log -m "Sync with left" \ @@ -256,6 +284,8 @@ test_expect_success 'setup: expected shortlog for two branches' ' cat >expected <<-EOF Merge branches ${apos}left${apos} and ${apos}right${apos} + # By Another Author (3) and A U Thor (2) + # Via Another Committer * left: Left #5 Left #4 @@ -273,29 +303,29 @@ test_expect_success 'setup: expected shortlog for two branches' ' ' test_expect_success 'shortlog for two branches' ' - git config merge.log true && - test_might_fail git config --unset-all merge.summary && + test_config merge.log true && + test_unconfig merge.summary && git checkout master && test_tick && git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual1 && - test_might_fail git config --unset-all merge.log && - git config merge.summary true && + test_unconfig merge.log && + test_config merge.summary true && git checkout master && test_tick && git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual2 && - git config merge.log yes && - test_might_fail git config --unset-all merge.summary && + test_config merge.log yes && + test_unconfig merge.summary && git checkout master && test_tick && git fetch . left right && git fmt-merge-msg <.git/FETCH_HEAD >actual3 && - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && git fetch . left right && @@ -308,8 +338,8 @@ test_expect_success 'shortlog for two branches' ' ' test_expect_success 'merge-msg -F' ' - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && git fetch . left right && @@ -318,8 +348,8 @@ test_expect_success 'merge-msg -F' ' ' test_expect_success 'merge-msg -F in subdirectory' ' - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && git fetch . left right && @@ -333,8 +363,8 @@ test_expect_success 'merge-msg -F in subdirectory' ' ' test_expect_success 'merge-msg with nothing to merge' ' - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && >empty && @@ -359,8 +389,8 @@ test_expect_success 'merge-msg tag' ' Common #1 EOF - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && @@ -379,6 +409,8 @@ test_expect_success 'merge-msg two tags' ' Common #2 Common #1 + # By Another Author (3) and A U Thor (2) + # Via Another Committer * tag ${apos}tag-l5${apos}: Left #5 Left #4 @@ -387,8 +419,8 @@ test_expect_success 'merge-msg two tags' ' Common #1 EOF - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && @@ -407,6 +439,8 @@ test_expect_success 'merge-msg tag and branch' ' Common #2 Common #1 + # By Another Author (3) and A U Thor (2) + # Via Another Committer * left: Left #5 Left #4 @@ -415,8 +449,8 @@ test_expect_success 'merge-msg tag and branch' ' Common #1 EOF - test_might_fail git config --unset-all merge.log && - git config merge.summary yes && + test_unconfig merge.log && + test_config merge.summary yes && git checkout master && test_tick && @@ -443,6 +477,8 @@ test_expect_success 'merge-msg lots of commits' ' echo " ..." } >expected && + test_config merge.summary yes && + git checkout master && test_tick && git fetch . long && @@ -451,4 +487,43 @@ test_expect_success 'merge-msg lots of commits' ' test_cmp expected actual ' +test_expect_success 'merge-msg with "merging" an annotated tag' ' + test_config merge.log true && + + git checkout master^0 && + git commit --allow-empty -m "One step ahead" && + git tag -a -m "An annotated one" annote HEAD && + + git checkout master && + git fetch . annote && + + git fmt-merge-msg <.git/FETCH_HEAD >actual && + { + cat <<-\EOF + Merge tag '\''annote'\'' + + An annotated one + + * tag '\''annote'\'': + One step ahead + EOF + } >expected && + test_cmp expected actual && + + test_when_finished "git reset --hard" && + annote=$(git rev-parse annote) && + git merge --no-commit $annote && + { + cat <<-EOF + Merge tag '\''$annote'\'' + + An annotated one + + * tag '\''$annote'\'': + One step ahead + EOF + } >expected && + test_cmp expected .git/MERGE_MSG +' + test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 172178490a..da5fb6c917 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -58,6 +58,8 @@ test_atom head parent '' test_atom head numparent 0 test_atom head object '' test_atom head type '' +test_atom head '*objectname' '' +test_atom head '*objecttype' '' test_atom head author 'A U Thor <author@example.com> 1151939924 +0200' test_atom head authorname 'A U Thor' test_atom head authoremail '<author@example.com>' @@ -91,6 +93,8 @@ test_atom tag parent '' test_atom tag numparent '' test_atom tag object '67a36f10722846e891fbada1ba48ed035de75581' test_atom tag type 'commit' +test_atom tag '*objectname' '67a36f10722846e891fbada1ba48ed035de75581' +test_atom tag '*objecttype' 'commit' test_atom tag author '' test_atom tag authorname '' test_atom tag authoremail '' @@ -456,4 +460,14 @@ test_atom refs/tags/signed-long contents "subject line body contents $sig" +cat >expected <<\EOF +408fe76d02a785a006c2e9c669b7be5589ede96d <committer@example.com> refs/tags/master +90b5ebede4899eda64893bc2a4c8f1d6fb6dfc40 <committer@example.com> refs/tags/bogo +EOF + +test_expect_success 'Verify sort with multiple keys' ' + git for-each-ref --format="%(objectname) %(taggeremail) %(refname)" --sort=objectname --sort=taggeremail \ + refs/tags/bogo refs/tags/master > actual && + test_cmp expected actual +' test_done diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index 82f3639937..63194d819e 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -9,9 +9,14 @@ test_expect_success 'gc empty repository' ' git gc ' +test_expect_success 'gc does not leave behind pid file' ' + git gc && + test_path_is_missing .git/gc.pid +' + test_expect_success 'gc --gobbledegook' ' test_expect_code 129 git gc --nonsense 2>err && - grep "[Uu]sage: git gc" err + test_i18ngrep "[Uu]sage: git gc" err ' test_expect_success 'gc -h with invalid configuration' ' @@ -22,7 +27,7 @@ test_expect_success 'gc -h with invalid configuration' ' echo "[gc] pruneexpire = CORRUPT" >>.git/config && test_expect_code 129 git gc -h >usage 2>&1 ) && - grep "[Uu]sage" broken/usage + test_i18ngrep "[Uu]sage" broken/usage ' test_done diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh index a845b154e4..b90e985a48 100755 --- a/t/t7001-mv.sh +++ b/t/t7001-mv.sh @@ -218,13 +218,13 @@ test_expect_success 'git mv should not change sha1 of moved cache entry' ' rm -f dirty dirty2 -test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' ' +test_expect_success 'git mv should overwrite symlink to a file' ' rm -fr .git && git init && echo 1 >moved && - ln -s moved symlink && - git add moved symlink && + test_ln_s_add moved symlink && + git add moved && test_must_fail git mv moved symlink && git mv -f moved symlink && ! test -e moved && @@ -237,22 +237,180 @@ test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' ' rm -f moved symlink -test_expect_success SYMLINKS 'git mv should overwrite file with a symlink' ' +test_expect_success 'git mv should overwrite file with a symlink' ' rm -fr .git && git init && echo 1 >moved && - ln -s moved symlink && - git add moved symlink && + test_ln_s_add moved symlink && + git add moved && test_must_fail git mv symlink moved && git mv -f symlink moved && ! test -e symlink && - test -h moved && git update-index --refresh && git diff-files --quiet ' +test_expect_success SYMLINKS 'check moved symlink' ' + + test -h moved +' + rm -f moved symlink +test_expect_success 'setup submodule' ' + git commit -m initial && + git reset --hard && + git submodule add ./. sub && + echo content >file && + git add file && + git commit -m "added sub and file" +' + +test_expect_success 'git mv cannot move a submodule in a file' ' + test_must_fail git mv sub file +' + +test_expect_success 'git mv moves a submodule with a .git directory and no .gitmodules' ' + entry="$(git ls-files --stage sub | cut -f 1)" && + git rm .gitmodules && + ( + cd sub && + rm -f .git && + cp -a ../.git/modules/sub .git && + GIT_WORK_TREE=. git config --unset core.worktree + ) && + mkdir mod && + git mv sub mod/sub && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'git mv moves a submodule with a .git directory and .gitmodules' ' + rm -rf mod && + git reset --hard && + git submodule update && + entry="$(git ls-files --stage sub | cut -f 1)" && + ( + cd sub && + rm -f .git && + cp -a ../.git/modules/sub .git && + GIT_WORK_TREE=. git config --unset core.worktree + ) && + mkdir mod && + git mv sub mod/sub && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + echo mod/sub >expected && + git config -f .gitmodules submodule.sub.path >actual && + test_cmp expected actual && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'git mv moves a submodule with gitfile' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + entry="$(git ls-files --stage sub | cut -f 1)" && + ( + cd mod && + git mv ../sub/ . + ) && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + echo mod/sub >expected && + git config -f .gitmodules submodule.sub.path >actual && + test_cmp expected actual && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv does not complain when no .gitmodules file is found' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git rm .gitmodules && + entry="$(git ls-files --stage sub | cut -f 1)" && + git mv sub mod/sub 2>actual.err && + ! test -s actual.err && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv will error out on a modified .gitmodules file unless staged' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git config -f .gitmodules foo.bar true && + entry="$(git ls-files --stage sub | cut -f 1)" && + test_must_fail git mv sub mod/sub 2>actual.err && + test -s actual.err && + test -e sub && + git diff-files --quiet -- sub && + git add .gitmodules && + git mv sub mod/sub 2>actual.err && + ! test -s actual.err && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv issues a warning when section is not found in .gitmodules' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git config -f .gitmodules --remove-section submodule.sub && + git add .gitmodules && + entry="$(git ls-files --stage sub | cut -f 1)" && + echo "warning: Could not find section in .gitmodules where path=sub" >expect.err && + git mv sub mod/sub 2>actual.err && + test_i18ncmp expect.err actual.err && + ! test -e sub && + [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] && + ( + cd mod/sub && + git status + ) && + git update-index --refresh && + git diff-files --quiet +' + +test_expect_success 'mv --dry-run does not touch the submodule or .gitmodules' ' + rm -rf mod/sub && + git reset --hard && + git submodule update && + git mv -n sub mod/sub 2>actual.err && + test -f sub/.git && + git diff-index --exit-code HEAD && + git update-index --refresh && + git diff-files --quiet -- sub .gitmodules +' + test_done diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh index e0227730de..9496736a89 100755 --- a/t/t7003-filter-branch.sh +++ b/t/t7003-filter-branch.sh @@ -5,7 +5,8 @@ test_description='git filter-branch' test_expect_success 'setup' ' test_commit A && - test_commit B && + GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" && + test_commit --notick B && git checkout -b branch B && test_commit D && mkdir dir && @@ -63,6 +64,20 @@ test_expect_success 'correct GIT_DIR while using -d' ' grep drepo "$TRASHDIR/backup-refs" ' +test_expect_success 'tree-filter works with -d' ' + git init drepo-tree && + ( + cd drepo-tree && + test_commit one && + git filter-branch -d "$TRASHDIR/dfoo" \ + --tree-filter "echo changed >one.t" && + echo changed >expect && + git cat-file blob HEAD:one.t >actual && + test_cmp expect actual && + test_cmp one.t actual + ) +' + test_expect_success 'Fail if commit filter fails' ' test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD ' @@ -166,10 +181,11 @@ test_expect_success 'author information is preserved' ' test_tick && GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips && git branch preserved-author && - git filter-branch -f --msg-filter "cat; \ + (sane_unset GIT_AUTHOR_NAME && + git filter-branch -f --msg-filter "cat; \ test \$GIT_COMMIT != $(git rev-parse master) || \ echo Hallo" \ - preserved-author && + preserved-author) && test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l) ' diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 4ef79aabc4..c8d6e9f88c 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -104,6 +104,18 @@ test_expect_success 'creating a tag using HEAD directly should succeed' ' tag_exists myhead ' +test_expect_success '--force can create a tag with the name of one existing' ' + tag_exists mytag && + git tag --force mytag && + tag_exists mytag' + +test_expect_success '--force is moot with a non-existing tag name' ' + git tag newtag >expect && + git tag --force forcetag >actual && + test_cmp expect actual +' +git tag -d newtag forcetag + # deleting tags: test_expect_success 'trying to delete an unknown tag should fail' ' @@ -263,6 +275,50 @@ test_expect_success 'tag -l can accept multiple patterns' ' test_cmp expect actual ' +test_expect_success 'listing tags in column' ' + COLUMNS=40 git tag -l --column=row >actual && + cat >expected <<\EOF && +a1 aa1 cba t210 t211 +v0.2.1 v1.0 v1.0.1 v1.1.3 +EOF + test_cmp expected actual +' + +test_expect_success 'listing tags in column with column.*' ' + git config column.tag row && + git config column.ui dense && + COLUMNS=40 git tag -l >actual && + git config --unset column.ui && + git config --unset column.tag && + cat >expected <<\EOF && +a1 aa1 cba t210 t211 +v0.2.1 v1.0 v1.0.1 v1.1.3 +EOF + test_cmp expected actual +' + +test_expect_success 'listing tag with -n --column should fail' ' + test_must_fail git tag --column -n +' + +test_expect_success 'listing tags -n in column with column.ui ignored' ' + git config column.ui "row dense" && + COLUMNS=40 git tag -l -n >actual && + git config --unset column.ui && + cat >expected <<\EOF && +a1 Foo +aa1 Foo +cba Foo +t210 Foo +t211 Foo +v0.2.1 Foo +v1.0 Foo +v1.0.1 Foo +v1.1.3 Foo +EOF + test_cmp expected actual +' + # creating and verifying lightweight tags: test_expect_success \ @@ -1022,12 +1078,12 @@ test_expect_success GPG \ ' # usage with rfc1991 signatures -echo "rfc1991" > gpghome/gpg.conf get_tag_header rfc1991-signed-tag $commit commit $time >expect echo "RFC1991 signed tag" >>expect echo '-----BEGIN PGP MESSAGE-----' >>expect test_expect_success GPG \ 'creating a signed tag with rfc1991' ' + echo "rfc1991" >gpghome/gpg.conf && git tag -s -m "RFC1991 signed tag" rfc1991-signed-tag $commit && get_tag_msg rfc1991-signed-tag >actual && test_cmp expect actual @@ -1041,6 +1097,7 @@ chmod +x fakeeditor test_expect_success GPG \ 'reediting a signed tag body omits signature' ' + echo "rfc1991" >gpghome/gpg.conf && echo "RFC1991 signed tag" >expect && GIT_EDITOR=./fakeeditor git tag -f -s rfc1991-signed-tag $commit && test_cmp expect actual @@ -1048,11 +1105,13 @@ test_expect_success GPG \ test_expect_success GPG \ 'verifying rfc1991 signature' ' + echo "rfc1991" >gpghome/gpg.conf && git tag -v rfc1991-signed-tag ' test_expect_success GPG \ 'list tag with rfc1991 signature' ' + echo "rfc1991" >gpghome/gpg.conf && echo "rfc1991-signed-tag RFC1991 signed tag" >expect && git tag -l -n1 rfc1991-signed-tag >actual && test_cmp expect actual && @@ -1282,4 +1341,43 @@ test_expect_success 'mixing incompatibles modes and options is forbidden' ' test_must_fail git tag -v -s ' +# check points-at + +test_expect_success '--points-at cannot be used in non-list mode' ' + test_must_fail git tag --points-at=v4.0 foo +' + +test_expect_success '--points-at finds lightweight tags' ' + echo v4.0 >expect && + git tag --points-at v4.0 >actual && + test_cmp expect actual +' + +test_expect_success '--points-at finds annotated tags of commits' ' + git tag -m "v4.0, annotated" annotated-v4.0 v4.0 && + echo annotated-v4.0 >expect && + git tag -l --points-at v4.0 "annotated*" >actual && + test_cmp expect actual +' + +test_expect_success '--points-at finds annotated tags of tags' ' + git tag -m "describing the v4.0 tag object" \ + annotated-again-v4.0 annotated-v4.0 && + cat >expect <<-\EOF && + annotated-again-v4.0 + annotated-v4.0 + EOF + git tag --points-at=annotated-v4.0 >actual && + test_cmp expect actual +' + +test_expect_success 'multiple --points-at are OR-ed together' ' + cat >expect <<-\EOF && + v2.0 + v3.0 + EOF + git tag --points-at=v2.0 --points-at=v3.0 >actual && + test_cmp expect actual +' + test_done diff --git a/t/t7007-show.sh b/t/t7007-show.sh index cce222f052..e41fa00b80 100755 --- a/t/t7007-show.sh +++ b/t/t7007-show.sh @@ -17,4 +17,107 @@ test_expect_success 'showing a tag that point at a missing object' ' test_must_fail git --no-pager show foo-tag ' +test_expect_success 'set up a bit of history' ' + test_commit main1 && + test_commit main2 && + test_commit main3 && + git tag -m "annotated tag" annotated && + git checkout -b side HEAD^^ && + test_commit side2 && + test_commit side3 +' + +test_expect_success 'showing two commits' ' + cat >expect <<-EOF && + commit $(git rev-parse main2) + commit $(git rev-parse main3) + EOF + git show main2 main3 >actual && + grep ^commit actual >actual.filtered && + test_cmp expect actual.filtered +' + +test_expect_success 'showing a range walks (linear)' ' + cat >expect <<-EOF && + commit $(git rev-parse main3) + commit $(git rev-parse main2) + EOF + git show main1..main3 >actual && + grep ^commit actual >actual.filtered && + test_cmp expect actual.filtered +' + +test_expect_success 'showing a range walks (Y shape, ^ first)' ' + cat >expect <<-EOF && + commit $(git rev-parse main3) + commit $(git rev-parse main2) + EOF + git show ^side3 main3 >actual && + grep ^commit actual >actual.filtered && + test_cmp expect actual.filtered +' + +test_expect_success 'showing a range walks (Y shape, ^ last)' ' + cat >expect <<-EOF && + commit $(git rev-parse main3) + commit $(git rev-parse main2) + EOF + git show main3 ^side3 >actual && + grep ^commit actual >actual.filtered && + test_cmp expect actual.filtered +' + +test_expect_success 'showing with -N walks' ' + cat >expect <<-EOF && + commit $(git rev-parse main3) + commit $(git rev-parse main2) + EOF + git show -2 main3 >actual && + grep ^commit actual >actual.filtered && + test_cmp expect actual.filtered +' + +test_expect_success 'showing annotated tag' ' + cat >expect <<-EOF && + tag annotated + commit $(git rev-parse annotated^{commit}) + EOF + git show annotated >actual && + grep -E "^(commit|tag)" actual >actual.filtered && + test_cmp expect actual.filtered +' + +test_expect_success 'showing annotated tag plus commit' ' + cat >expect <<-EOF && + tag annotated + commit $(git rev-parse annotated^{commit}) + commit $(git rev-parse side3) + EOF + git show annotated side3 >actual && + grep -E "^(commit|tag)" actual >actual.filtered && + test_cmp expect actual.filtered +' + +test_expect_success 'showing range' ' + cat >expect <<-EOF && + commit $(git rev-parse main3) + commit $(git rev-parse main2) + EOF + git show ^side3 annotated >actual && + grep -E "^(commit|tag)" actual >actual.filtered && + test_cmp expect actual.filtered +' + +test_expect_success '-s suppresses diff' ' + echo main3 >expect && + git show -s --format=%s main3 >actual && + test_cmp expect actual +' + +test_expect_success '--quiet suppresses diff' ' + echo main3 >expect && + git show --quiet --format=%s main3 >actual && + test_cmp expect actual +' + test_done diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh index fd6410fc71..b146406e9c 100755 --- a/t/t7008-grep-binary.sh +++ b/t/t7008-grep-binary.sh @@ -111,6 +111,28 @@ test_expect_success 'grep respects binary diff attribute' ' test_cmp expect actual ' +test_expect_success 'grep --cached respects binary diff attribute' ' + git grep --cached text t >actual && + test_cmp expect actual +' + +test_expect_success 'grep --cached respects binary diff attribute (2)' ' + git add .gitattributes && + rm .gitattributes && + git grep --cached text t >actual && + test_when_finished "git rm --cached .gitattributes" && + test_when_finished "git checkout .gitattributes" && + test_cmp expect actual +' + +test_expect_success 'grep revision respects binary diff attribute' ' + git commit -m new && + echo "Binary file HEAD:t matches" >expect && + git grep text HEAD -- t >actual && + test_when_finished "git reset HEAD^" && + test_cmp expect actual +' + test_expect_success 'grep respects not-binary diff attribute' ' echo binQary | q_to_nul >b && git add b && @@ -123,4 +145,35 @@ test_expect_success 'grep respects not-binary diff attribute' ' test_cmp expect actual ' +cat >nul_to_q_textconv <<'EOF' +#!/bin/sh +"$PERL_PATH" -pe 'y/\000/Q/' < "$1" +EOF +chmod +x nul_to_q_textconv + +test_expect_success 'setup textconv filters' ' + echo a diff=foo >.gitattributes && + git config diff.foo.textconv "\"$(pwd)\""/nul_to_q_textconv +' + +test_expect_success 'grep does not honor textconv' ' + test_must_fail git grep Qfile +' + +test_expect_success 'grep --textconv honors textconv' ' + echo "a:binaryQfile" >expect && + git grep --textconv Qfile >actual && + test_cmp expect actual +' + +test_expect_success 'grep --no-textconv does not honor textconv' ' + test_must_fail git grep --no-textconv Qfile +' + +test_expect_success 'grep --textconv blob honors textconv' ' + echo "HEAD:a:binaryQfile" >expect && + git grep --textconv Qfile HEAD:a >actual && + test_cmp expect actual +' + test_done diff --git a/t/t7009-filter-branch-null-sha1.sh b/t/t7009-filter-branch-null-sha1.sh new file mode 100755 index 0000000000..a997f7ac3a --- /dev/null +++ b/t/t7009-filter-branch-null-sha1.sh @@ -0,0 +1,49 @@ +#!/bin/sh + +test_description='filter-branch removal of trees with null sha1' +. ./test-lib.sh + +test_expect_success 'setup: base commits' ' + test_commit one && + test_commit two && + test_commit three +' + +test_expect_success 'setup: a commit with a bogus null sha1 in the tree' ' + { + git ls-tree HEAD && + printf "160000 commit $_z40\\tbroken\\n" + } >broken-tree + echo "add broken entry" >msg && + + tree=$(git mktree <broken-tree) && + test_tick && + commit=$(git commit-tree $tree -p HEAD <msg) && + git update-ref HEAD "$commit" +' + +# we have to make one more commit on top removing the broken +# entry, since otherwise our index does not match HEAD (and filter-branch will +# complain). We could make the index match HEAD, but doing so would involve +# writing a null sha1 into the index. +test_expect_success 'setup: bring HEAD and index in sync' ' + test_tick && + git commit -a -m "back to normal" +' + +test_expect_success 'filter commands are still checked' ' + test_must_fail git filter-branch \ + --force --prune-empty \ + --index-filter "git rm --cached --ignore-unmatch three.t" +' + +test_expect_success 'removing the broken entry works' ' + echo three >expect && + git filter-branch \ + --force --prune-empty \ + --index-filter "git rm --cached --ignore-unmatch broken" && + git log -1 --format=%s >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh index 8f3b54d826..88d60c1ce2 100755 --- a/t/t7011-skip-worktree-reading.sh +++ b/t/t7011-skip-worktree-reading.sh @@ -91,12 +91,12 @@ test_expect_success 'update-index --remove' ' test_cmp expected 1 ' -test_expect_success 'ls-files --delete' ' +test_expect_success 'ls-files --deleted' ' setup_absent && test -z "$(git ls-files -d)" ' -test_expect_success 'ls-files --delete' ' +test_expect_success 'ls-files --deleted' ' setup_dirty && test -z "$(git ls-files -d)" ' diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh index b8cb4906aa..7d467c034a 100755 --- a/t/t7060-wtstatus.sh +++ b/t/t7060-wtstatus.sh @@ -5,6 +5,7 @@ test_description='basic work tree status reporting' . ./test-lib.sh test_expect_success setup ' + git config --global advice.statusuoption false && test_commit A && test_commit B oneside added && git checkout A^0 && @@ -28,17 +29,19 @@ test_expect_success 'Report new path with conflict' ' test_cmp expect actual ' -cat >expect <<EOF -# On branch side -# Unmerged paths: -# (use "git add/rm <file>..." as appropriate to mark resolution) -# -# deleted by us: foo -# +test_expect_success 'M/D conflict does not segfault' ' + cat >expect <<EOF && +On branch side +You have unmerged paths. + (fix conflicts and run "git commit") + +Unmerged paths: + (use "git add/rm <file>..." as appropriate to mark resolution) + + deleted by us: foo + no changes added to commit (use "git add" and/or "git commit -a") EOF - -test_expect_success 'M/D conflict does not segfault' ' mkdir mdconflict && ( cd mdconflict && @@ -118,4 +121,97 @@ test_expect_success 'git diff-index --cached -C shows 2 copies + 1 unmerged' ' test_cmp expected actual ' + +test_expect_success 'status when conflicts with add and rm advice (deleted by them)' ' + git reset --hard && + git checkout master && + test_commit init main.txt init && + git checkout -b second_branch && + git rm main.txt && + git commit -m "main.txt deleted on second_branch" && + test_commit second conflict.txt second && + git checkout master && + test_commit on_second main.txt on_second && + test_commit master conflict.txt master && + test_must_fail git merge second_branch && + cat >expected <<\EOF && +On branch master +You have unmerged paths. + (fix conflicts and run "git commit") + +Unmerged paths: + (use "git add/rm <file>..." as appropriate to mark resolution) + + both added: conflict.txt + deleted by them: main.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'prepare for conflicts' ' + git reset --hard && + git checkout -b conflict && + test_commit one main.txt one && + git branch conflict_second && + git mv main.txt sub_master.txt && + git commit -m "main.txt renamed in sub_master.txt" && + git checkout conflict_second && + git mv main.txt sub_second.txt && + git commit -m "main.txt renamed in sub_second.txt" +' + + +test_expect_success 'status when conflicts with add and rm advice (both deleted)' ' + test_must_fail git merge conflict && + cat >expected <<\EOF && +On branch conflict_second +You have unmerged paths. + (fix conflicts and run "git commit") + +Unmerged paths: + (use "git add/rm <file>..." as appropriate to mark resolution) + + both deleted: main.txt + added by them: sub_master.txt + added by us: sub_second.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status when conflicts with only rm advice (both deleted)' ' + git reset --hard conflict_second && + test_must_fail git merge conflict && + git add sub_master.txt && + git add sub_second.txt && + cat >expected <<\EOF && +On branch conflict_second +You have unmerged paths. + (fix conflicts and run "git commit") + +Changes to be committed: + + new file: sub_master.txt + +Unmerged paths: + (use "git rm <file>..." to mark resolution) + + both deleted: main.txt + +Untracked files not listed (use -u option to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual && + git reset --hard && + git checkout master +' + + test_done diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh new file mode 100755 index 0000000000..460789b4d8 --- /dev/null +++ b/t/t7061-wtstatus-ignore.sh @@ -0,0 +1,265 @@ +#!/bin/sh + +test_description='git-status ignored files' + +. ./test-lib.sh + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +?? untracked/ +EOF + +test_expect_success 'status untracked directory with --ignored' ' + echo "ignored" >.gitignore && + mkdir untracked && + : >untracked/ignored && + : >untracked/uncommitted && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +?? untracked/uncommitted +!! untracked/ignored +EOF + +test_expect_success 'status untracked directory with --ignored -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' +cat >expected <<\EOF +?? untracked/uncommitted +!! untracked/ignored +EOF + +test_expect_success 'status prefixed untracked directory with --ignored' ' + git status --porcelain --ignored untracked/ >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? untracked/uncommitted +!! untracked/ignored +EOF + +test_expect_success 'status prefixed untracked sub-directory with --ignored -u' ' + git status --porcelain --ignored -u untracked/ >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! ignored/ +EOF + +test_expect_success 'status ignored directory with --ignore' ' + rm -rf untracked && + mkdir ignored && + : >ignored/uncommitted && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! ignored/uncommitted +EOF + +test_expect_success 'status ignored directory with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +EOF + +test_expect_success 'status empty untracked directory with --ignore' ' + rm -rf ignored && + mkdir untracked-ignored && + mkdir untracked-ignored/test && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +EOF + +test_expect_success 'status empty untracked directory with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! untracked-ignored/ +EOF + +test_expect_success 'status untracked directory with ignored files with --ignore' ' + : >untracked-ignored/ignored && + : >untracked-ignored/test/ignored && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! untracked-ignored/ignored +!! untracked-ignored/test/ignored +EOF + +test_expect_success 'status untracked directory with ignored files with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +EOF + +test_expect_success 'status ignored tracked directory with --ignore' ' + rm -rf untracked-ignored && + mkdir tracked && + : >tracked/committed && + git add tracked/committed && + git commit -m. && + echo "tracked" >.gitignore && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +EOF + +test_expect_success 'status ignored tracked directory with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +EOF + +test_expect_success 'status ignored tracked directory and ignored file with --ignore' ' + echo "committed" >>.gitignore && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +EOF + +test_expect_success 'status ignored tracked directory and ignored file with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! tracked/uncommitted +EOF + +test_expect_success 'status ignored tracked directory and uncommitted file with --ignore' ' + echo "tracked" >.gitignore && + : >tracked/uncommitted && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! tracked/uncommitted +EOF + +test_expect_success 'status ignored tracked directory and uncommitted file with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! tracked/ignored/ +EOF + +test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore' ' + rm -rf tracked/uncommitted && + mkdir tracked/ignored && + : >tracked/ignored/uncommitted && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! tracked/ignored/uncommitted +EOF + +test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! tracked/ignored/uncommitted +EOF + +test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore' ' + : >tracked/ignored/committed && + git add -f tracked/ignored/committed && + git commit -m. && + git status --porcelain --ignored >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +?? .gitignore +?? actual +?? expected +!! tracked/ignored/uncommitted +EOF + +test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore -u' ' + git status --porcelain --ignored -u >actual && + test_cmp expected actual +' + +test_done diff --git a/t/t7062-wtstatus-ignorecase.sh b/t/t7062-wtstatus-ignorecase.sh new file mode 100755 index 0000000000..73709dbeee --- /dev/null +++ b/t/t7062-wtstatus-ignorecase.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +test_description='git-status with core.ignorecase=true' + +. ./test-lib.sh + +test_expect_success 'status with hash collisions' ' + # note: "V/", "V/XQANY/" and "WURZAUP/" produce the same hash code + # in name-hash.c::hash_name + mkdir V && + mkdir V/XQANY && + mkdir WURZAUP && + touch V/XQANY/test && + git config core.ignorecase true && + git add . && + # test is successful if git status completes (no endless loop) + git status +' + +test_done diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh index b096dc88c2..8d4b50d1b5 100755 --- a/t/t7102-reset.sh +++ b/t/t7102-reset.sh @@ -9,6 +9,19 @@ Documented tests for git reset' . ./test-lib.sh +commit_msg () { + # String "modify 2nd file (changed)" partly in German + # (translated with Google Translate), + # encoded in UTF-8, used as a commit log message below. + msg="modify 2nd file (ge\303\244ndert)\n" + if test -n "$1" + then + printf "$msg" | iconv -f utf-8 -t "$1" + else + printf "$msg" + fi +} + test_expect_success 'creating initial files and commits' ' test_tick && echo "1st file" >first && @@ -28,7 +41,8 @@ test_expect_success 'creating initial files and commits' ' echo "1st line 2nd file" >secondfile && echo "2nd line 2nd file" >>secondfile && - git commit -a -m "modify 2nd file" + git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" && + head5=$(git rev-parse --verify HEAD) ' # git log --pretty=oneline # to see those SHA1 involved @@ -43,6 +57,20 @@ check_changes () { done | test_cmp .cat_expect - } +test_expect_success 'reset --hard message' ' + hex=$(git log -1 --format="%h") && + git reset --hard > .actual && + echo HEAD is now at $hex $(commit_msg) > .expected && + test_cmp .expected .actual +' + +test_expect_success 'reset --hard message (iso8859-1 logoutputencoding)' ' + hex=$(git log -1 --format="%h") && + git -c "i18n.logOutputEncoding=iso8859-1" reset --hard > .actual && + echo HEAD is now at $hex $(commit_msg iso8859-1) > .expected && + test_cmp .expected .actual +' + >.diff_expect >.cached_expect cat >.cat_expect <<EOF @@ -56,7 +84,7 @@ test_expect_success 'giving a non existing revision should fail' ' test_must_fail git reset --mixed aaaaaa && test_must_fail git reset --soft aaaaaa && test_must_fail git reset --hard aaaaaa && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success 'reset --soft with unmerged index should fail' ' @@ -74,7 +102,7 @@ test_expect_success \ test_must_fail git reset --hard -- first && test_must_fail git reset --soft HEAD^ -- first && test_must_fail git reset --hard HEAD^ -- first && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success 'giving unrecognized options should fail' ' @@ -86,7 +114,7 @@ test_expect_success 'giving unrecognized options should fail' ' test_must_fail git reset --soft -o && test_must_fail git reset --hard --other && test_must_fail git reset --hard -o && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success \ @@ -110,7 +138,7 @@ test_expect_success \ git checkout master && git branch -D branch1 branch2 && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success \ @@ -133,27 +161,27 @@ test_expect_success \ git checkout master && git branch -D branch3 branch4 && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' test_expect_success \ 'resetting to HEAD with no changes should succeed and do nothing' ' git reset --hard && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --hard HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --soft && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --soft HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --mixed && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset --mixed HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git reset HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' >.diff_expect @@ -176,7 +204,7 @@ test_expect_success '--soft reset only should show changes in diff --cached' ' git reset --soft HEAD^ && check_changes d1a4bc3abce4829628ae2dcb0d60ef3d1a78b1c4 && test "$(git rev-parse ORIG_HEAD)" = \ - 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + $head5 ' >.diff_expect @@ -191,9 +219,10 @@ test_expect_success \ 'changing files and redo the last commit should succeed' ' echo "3rd line 2nd file" >>secondfile && git commit -a -C ORIG_HEAD && - check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d && + head4=$(git rev-parse --verify HEAD) && + check_changes $head4 && test "$(git rev-parse ORIG_HEAD)" = \ - 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + $head5 ' >.diff_expect @@ -210,7 +239,7 @@ test_expect_success \ git reset --hard HEAD~2 && check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e && test "$(git rev-parse ORIG_HEAD)" = \ - 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d + $head4 ' >.diff_expect @@ -302,8 +331,8 @@ test_expect_success 'redoing the last two commits should succeed' ' echo "1st line 2nd file" >secondfile && echo "2nd line 2nd file" >>secondfile && - git commit -a -m "modify 2nd file" && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + git -c "i18n.commitEncoding=iso8859-1" commit -a -m "$(commit_msg iso8859-1)" && + check_changes $head5 ' >.diff_expect @@ -325,10 +354,11 @@ test_expect_success '--hard reset to HEAD should clear a failed merge' ' git checkout branch2 && echo "3rd line in branch2" >>secondfile && git commit -a -m "change in branch2" && + head3=$(git rev-parse --verify HEAD) && test_must_fail git pull . branch1 && git reset --hard && - check_changes 77abb337073fb4369a7ad69ff6f5ec0e4d6b54bb + check_changes $head3 ' >.diff_expect @@ -341,15 +371,15 @@ EOF test_expect_success \ '--hard reset to ORIG_HEAD should clear a fast-forward merge' ' git reset --hard HEAD^ && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git pull . branch1 && git reset --hard ORIG_HEAD && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc && + check_changes $head5 && git checkout master && git branch -D branch1 branch2 && - check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc + check_changes $head5 ' cat > expect << EOF @@ -388,7 +418,8 @@ test_expect_success 'test --mixed <paths>' ' echo 4 > file4 && echo 5 > file1 && git add file1 file3 file4 && - test_must_fail git reset HEAD -- file1 file2 file3 && + git reset HEAD -- file1 file2 file3 && + test_must_fail git diff --quiet && git diff > output && test_cmp output expect && git diff --cached > output && @@ -402,7 +433,8 @@ test_expect_success 'test resetting the index at give paths' ' >sub/file2 && git update-index --add sub/file1 sub/file2 && T=$(git write-tree) && - test_must_fail git reset HEAD sub/file2 && + git reset HEAD sub/file2 && + test_must_fail git diff --quiet && U=$(git write-tree) && echo "$T" && echo "$U" && @@ -440,7 +472,8 @@ test_expect_success 'resetting specific path that is unmerged' ' echo "100644 $F3 3 file2" } | git update-index --index-info && git ls-files -u && - test_must_fail git reset HEAD file2 && + git reset HEAD file2 && + test_must_fail git diff --quiet && git diff-index --exit-code --cached HEAD ' @@ -449,10 +482,11 @@ test_expect_success 'disambiguation (1)' ' git reset --hard && >secondfile && git add secondfile && - test_must_fail git reset secondfile && + git reset secondfile && + test_must_fail git diff --quiet -- secondfile && test -z "$(git diff --cached --name-only)" && test -f secondfile && - test ! -s secondfile + test_must_be_empty secondfile ' @@ -474,7 +508,8 @@ test_expect_success 'disambiguation (3)' ' >secondfile && git add secondfile && rm -f secondfile && - test_must_fail git reset HEAD secondfile && + git reset HEAD secondfile && + test_must_fail git diff --quiet && test -z "$(git diff --cached --name-only)" && test ! -f secondfile @@ -486,9 +521,18 @@ test_expect_success 'disambiguation (4)' ' >secondfile && git add secondfile && rm -f secondfile && - test_must_fail git reset -- secondfile && + git reset -- secondfile && + test_must_fail git diff --quiet && test -z "$(git diff --cached --name-only)" && test ! -f secondfile ' +test_expect_success 'reset with paths accepts tree' ' + # for simpler tests, drop last commit containing added files + git reset --hard HEAD^ && + git reset HEAD^^{tree} -- . && + git diff --cached HEAD^ --exit-code && + git diff HEAD --exit-code +' + test_done diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh index 95fab20361..98b7d7b969 100755 --- a/t/t7105-reset-patch.sh +++ b/t/t7105-reset-patch.sh @@ -25,15 +25,17 @@ test_expect_success PERL 'saying "n" does nothing' ' ' test_expect_success PERL 'git reset -p' ' - (echo n; echo y) | git reset -p && + (echo n; echo y) | git reset -p >output && verify_state dir/foo work head && - verify_saved_state bar + verify_saved_state bar && + test_i18ngrep "Unstage" output ' test_expect_success PERL 'git reset -p HEAD^' ' - (echo n; echo y) | git reset -p HEAD^ && + (echo n; echo y) | git reset -p HEAD^ >output && verify_state dir/foo work parent && - verify_saved_state bar + verify_saved_state bar && + test_i18ngrep "Apply" output ' # The idea in the rest is that bar sorts first, so we always say 'y' diff --git a/t/t7106-reset-unborn-branch.sh b/t/t7106-reset-unborn-branch.sh new file mode 100755 index 0000000000..0f95f00477 --- /dev/null +++ b/t/t7106-reset-unborn-branch.sh @@ -0,0 +1,70 @@ +#!/bin/sh + +test_description='git reset should work on unborn branch' +. ./test-lib.sh + +test_expect_success 'setup' ' + echo a >a && + echo b >b +' + +test_expect_success 'reset' ' + git add a b && + git reset && + + >expect && + git ls-files >actual && + test_cmp expect actual +' + +test_expect_success 'reset HEAD' ' + rm .git/index && + git add a b && + test_must_fail git reset HEAD +' + +test_expect_success 'reset $file' ' + rm .git/index && + git add a b && + git reset a && + + echo b >expect && + git ls-files >actual && + test_cmp expect actual +' + +test_expect_success PERL 'reset -p' ' + rm .git/index && + git add a && + echo y >yes && + git reset -p <yes >output && + + >expect && + git ls-files >actual && + test_cmp expect actual && + test_i18ngrep "Unstage" output +' + +test_expect_success 'reset --soft is a no-op' ' + rm .git/index && + git add a && + git reset --soft && + + echo a >expect && + git ls-files >actual && + test_cmp expect actual +' + +test_expect_success 'reset --hard' ' + rm .git/index && + git add a && + test_when_finished "echo a >a" && + git reset --hard && + + >expect && + git ls-files >actual && + test_cmp expect actual && + test_path_is_missing a +' + +test_done diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 07fb53adcb..0c9ec0ad44 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -229,7 +229,7 @@ test_expect_success 'checkout to detach HEAD (with advice declined)' ' git checkout -f renamer && git clean -f && git checkout renamer^ 2>messages && test_i18ngrep "HEAD is now at 7329388" messages && - test 1 -eq $(wc -l <messages) && + test_line_count = 1 messages && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && test "z$H" = "z$M" && @@ -247,7 +247,7 @@ test_expect_success 'checkout to detach HEAD' ' git checkout -f renamer && git clean -f && git checkout renamer^ 2>messages && test_i18ngrep "HEAD is now at 7329388" messages && - test 1 -lt $(wc -l <messages) && + test_line_count -gt 1 messages && H=$(git rev-parse --verify HEAD) && M=$(git show-ref -s --verify refs/heads/master) && test "z$H" = "z$M" && @@ -431,6 +431,7 @@ test_expect_success 'detach a symbolic link HEAD' ' test_expect_success \ 'checkout with --track fakes a sensible -b <name>' ' + git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" && git update-ref refs/remotes/origin/koala/bear renamer && git checkout --track origin/koala/bear && diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh index 800b5368a5..710be90489 100755 --- a/t/t7300-clean.sh +++ b/t/t7300-clean.sh @@ -298,6 +298,23 @@ test_expect_success 'git clean -d -x' ' ' +test_expect_success 'git clean -d -x with ignored tracked directory' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + git clean -d -x -e src && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f src/part3.c && + test ! -d docs && + test ! -f obj.o && + test ! -d build + +' + test_expect_success 'git clean -X' ' mkdir -p build docs && @@ -332,6 +349,23 @@ test_expect_success 'git clean -d -X' ' ' +test_expect_success 'git clean -d -X with ignored tracked directory' ' + + mkdir -p build docs && + touch a.out src/part3.c docs/manual.txt obj.o build/lib.so && + git clean -d -X -e src && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f src/part3.c && + test -f docs/manual.txt && + test ! -f obj.o && + test ! -d build + +' + test_expect_success 'clean.requireForce defaults to true' ' git config --unset clean.requireForce && @@ -399,8 +433,8 @@ test_expect_success SANITY 'removal failure' ' ' test_expect_success 'nested git work tree' ' - rm -fr foo bar && - mkdir foo bar && + rm -fr foo bar baz && + mkdir -p foo bar baz/boo && ( cd foo && git init && @@ -412,15 +446,24 @@ test_expect_success 'nested git work tree' ' cd bar && >goodbye.people ) && + ( + cd baz/boo && + git init && + >deeper.world + git add . && + git commit -a -m deeply.nested + ) && git clean -f -d && test -f foo/.git/index && test -f foo/hello.world && + test -f baz/boo/.git/index && + test -f baz/boo/deeper.world && ! test -d bar ' test_expect_success 'force removal of nested git work tree' ' - rm -fr foo bar && - mkdir foo bar && + rm -fr foo bar baz && + mkdir -p foo bar baz/boo && ( cd foo && git init && @@ -432,9 +475,17 @@ test_expect_success 'force removal of nested git work tree' ' cd bar && >goodbye.people ) && + ( + cd baz/boo && + git init && + >deeper.world + git add . && + git commit -a -m deeply.nested + ) && git clean -f -f -d && ! test -d foo && - ! test -d bar + ! test -d bar && + ! test -d baz ' test_expect_success 'git clean -e' ' diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh new file mode 100755 index 0000000000..3ae394e934 --- /dev/null +++ b/t/t7301-clean-interactive.sh @@ -0,0 +1,475 @@ +#!/bin/sh + +test_description='git clean -i basic tests' + +. ./test-lib.sh + +test_expect_success 'setup' ' + + mkdir -p src && + touch src/part1.c Makefile && + echo build >.gitignore && + echo \*.o >>.gitignore && + git add . && + git commit -m setup && + touch src/part2.c README && + git add . + +' + +test_expect_success 'git clean -i (c: clean hotkey)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + echo c | git clean -i && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test ! -f src/part3.h && + test ! -f src/part4.c && + test ! -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -i (cl: clean prefix)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + echo cl | git clean -i && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test ! -f src/part3.h && + test ! -f src/part4.c && + test ! -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -i (quit)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + echo quit | git clean -i && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -i (Ctrl+D)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + echo "\04" | git clean -i && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (filter all)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo f; echo "*"; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (filter patterns)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo f; echo "part3.* *.out"; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test ! -f src/part4.c && + test ! -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (filter patterns 2)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo f; echo "* !*.out"; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - all)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo "*"; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f docs/manual.txt && + test ! -f src/part3.c && + test ! -f src/part3.h && + test ! -f src/part4.c && + test ! -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - none)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - number)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 3; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - number 2)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 2 3; echo 5; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test ! -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - number 3)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 3,4 5; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test ! -f src/part3.h && + test ! -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - filenames)' ' + + mkdir -p build docs && + touch a.out foo.txt bar.txt baz.txt && + (echo s; echo a.out fo ba bar; echo; echo c) | \ + git clean -id && + test -f Makefile && + test ! -f a.out && + test ! -f foo.txt && + test ! -f bar.txt && + test -f baz.txt && + rm baz.txt + +' + +test_expect_success 'git clean -id (select - range)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 1,3-4; echo 2; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f src/part3.c && + test ! -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test ! -f docs/manual.txt && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (select - range 2)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo 4- 1; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test -f src/part3.c && + test ! -f src/part3.h && + test ! -f src/part4.c && + test ! -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (inverse select)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo s; echo "*"; echo -5- 1 -2; echo; echo c) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test ! -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (ask)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f docs/manual.txt && + test -f src/part3.c && + test ! -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id (ask - Ctrl+D)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (echo a; echo Y; echo no; echo yes; echo "\04") | \ + git clean -id && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id with prefix and path (filter)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (cd build/ && \ + (echo f; echo "docs"; echo "*.h"; echo ; echo c) | \ + git clean -id ..) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test ! -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id with prefix and path (select by name)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (cd build/ && \ + (echo s; echo "../docs/"; echo "../src/part3.c"; \ + echo "../src/part4.c"; echo; echo c) | \ + git clean -id ..) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test -f a.out && + test ! -f docs/manual.txt && + test ! -f src/part3.c && + test -f src/part3.h && + test ! -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_expect_success 'git clean -id with prefix and path (ask)' ' + + mkdir -p build docs && + touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \ + docs/manual.txt obj.o build/lib.so && + (cd build/ && \ + (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \ + git clean -id ..) && + test -f Makefile && + test -f README && + test -f src/part1.c && + test -f src/part2.c && + test ! -f a.out && + test ! -f docs/manual.txt && + test -f src/part3.c && + test ! -f src/part3.h && + test -f src/part4.c && + test -f src/part4.h && + test -f obj.o && + test -f build/lib.so + +' + +test_done diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 695f7afdf3..c28e8d8ada 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -18,6 +18,16 @@ test_expect_success 'setup - initial commit' ' git branch initial ' +test_expect_success 'configuration parsing' ' + test_when_finished "rm -f .gitmodules" && + cat >.gitmodules <<-\EOF && + [submodule "s"] + path + ignore + EOF + test_must_fail git status +' + test_expect_success 'setup - repository in init subdirectory' ' mkdir init && ( @@ -78,7 +88,16 @@ test_expect_success 'submodule add' ' ( cd addtest && git submodule add -q "$submodurl" submod >actual && - test ! -s actual && + test_must_be_empty actual && + echo "gitdir: ../.git/modules/submod" >expect && + test_cmp expect submod/.git && + ( + cd submod && + git config core.worktree >actual && + echo "../../../submod" >expect && + test_cmp expect actual && + rm -f actual expect + ) && git submodule init ) && @@ -124,6 +143,7 @@ test_expect_success 'submodule add --branch' ' ( cd addtest && git submodule add -b initial "$submodurl" submod-branch && + test "initial" = "$(git config -f .gitmodules submodule.submod-branch.branch)" && git submodule init ) && @@ -202,6 +222,32 @@ test_expect_success 'submodule add with ./, /.. and // in path' ' test_cmp empty untracked ' +test_expect_success 'submodule add in subdirectory' ' + echo "refs/heads/master" >expect && + >empty && + + mkdir addtest/sub && + ( + cd addtest/sub && + git submodule add "$submodurl" ../realsubmod3 && + git submodule init + ) && + + rm -f heads head untracked && + inspect addtest/realsubmod3 ../.. && + test_cmp expect heads && + test_cmp expect head && + test_cmp empty untracked +' + +test_expect_success 'submodule add in subdirectory with relative path should fail' ' + ( + cd addtest/sub && + test_must_fail git submodule add ../../ submod3 2>../../output.err + ) && + test_i18ngrep toplevel output.err +' + test_expect_success 'setup - add an example entry to .gitmodules' ' GIT_CONFIG=.gitmodules \ git config submodule.example.url git://example.com/init.git @@ -225,7 +271,7 @@ EOF test_expect_success 'status should only print one line' ' git submodule status >lines && - test $(wc -l <lines) = 1 + test_line_count = 1 lines ' test_expect_success 'setup - fetch commit name from submodule' ' @@ -249,6 +295,27 @@ test_expect_success 'init should register submodule url in .git/config' ' test_cmp expect url ' +test_failure_with_unknown_submodule () { + test_must_fail git submodule $1 no-such-submodule 2>output.err && + grep "^error: .*no-such-submodule" output.err +} + +test_expect_success 'init should fail with unknown submodule' ' + test_failure_with_unknown_submodule init +' + +test_expect_success 'update should fail with unknown submodule' ' + test_failure_with_unknown_submodule update +' + +test_expect_success 'status should fail with unknown submodule' ' + test_failure_with_unknown_submodule status +' + +test_expect_success 'sync should fail with unknown submodule' ' + test_failure_with_unknown_submodule sync +' + test_expect_success 'update should fail when path is used by a file' ' echo hello >expect && @@ -277,7 +344,7 @@ test_expect_success 'update should work when path is an empty dir' ' mkdir init && git submodule update -q >update.out && - test ! -s update.out && + test_must_be_empty update.out && inspect init && test_cmp expect head-sha1 @@ -288,6 +355,26 @@ test_expect_success 'status should be "up-to-date" after update' ' grep "^ $rev1" list ' +test_expect_success 'status "up-to-date" from subdirectory' ' + mkdir -p sub && + ( + cd sub && + git submodule status >../list + ) && + grep "^ $rev1" list && + grep "\\.\\./init" list +' + +test_expect_success 'status "up-to-date" from subdirectory with path' ' + mkdir -p sub && + ( + cd sub && + git submodule status ../init >../list + ) && + grep "^ $rev1" list && + grep "\\.\\./init" list +' + test_expect_success 'status should be "modified" after submodule commit' ' ( cd init && @@ -368,6 +455,25 @@ test_expect_success 'update --init' ' git rev-parse --resolve-git-dir init/.git ' +test_expect_success 'update --init from subdirectory' ' + mv init init2 && + git config -f .gitmodules submodule.example.url "$(pwd)/init2" && + git config --remove-section submodule.example && + test_must_fail git config submodule.example.url && + + mkdir -p sub && + ( + cd sub && + git submodule update ../init >update.out && + cat update.out && + test_i18ngrep "not initialized" update.out && + test_must_fail git rev-parse --resolve-git-dir ../init/.git && + + git submodule update --init ../init + ) && + git rev-parse --resolve-git-dir init/.git +' + test_expect_success 'do not add files from a submodule' ' git reset --hard && @@ -375,7 +481,7 @@ test_expect_success 'do not add files from a submodule' ' ' -test_expect_success 'gracefully add submodule with a trailing slash' ' +test_expect_success 'gracefully add/reset submodule with a trailing slash' ' git reset --hard && git commit -m "commit subproject" init && @@ -389,7 +495,9 @@ test_expect_success 'gracefully add submodule with a trailing slash' ' git add init/ && test_must_fail git diff --exit-code --cached init && test $commit = $(git ls-files --stage | - sed -n "s/^160000 \([^ ]*\).*/\1/p") + sed -n "s/^160000 \([^ ]*\).*/\1/p") && + git reset init/ && + git diff --exit-code --cached init ' @@ -408,11 +516,8 @@ test_expect_success 'moving to a commit without submodule does not leave empty d git checkout second ' -test_expect_success 'submodule <invalid-path> warns' ' - - git submodule no-such-submodule 2> output.err && - grep "^error: .*no-such-submodule" output.err - +test_expect_success 'submodule <invalid-subcommand> fails' ' + test_must_fail git submodule no-such-subcommand ' test_expect_success 'add submodules without specifying an explicit path' ' @@ -474,21 +579,72 @@ test_expect_success 'set up for relative path tests' ' git add sub && git config -f .gitmodules submodule.sub.path sub && git config -f .gitmodules submodule.sub.url ../subrepo && - cp .git/config pristine-.git-config + cp .git/config pristine-.git-config && + cp .gitmodules pristine-.gitmodules ) ' -test_expect_success 'relative path works with URL' ' +test_expect_success '../subrepo works with URL - ssh://hostname/repo' ' ( cd reltest && cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && git config remote.origin.url ssh://hostname/repo && git submodule init && test "$(git config submodule.sub.url)" = ssh://hostname/subrepo ) ' -test_expect_success 'relative path works with user@host:path' ' +test_expect_success '../subrepo works with port-qualified URL - ssh://hostname:22/repo' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url ssh://hostname:22/repo && + git submodule init && + test "$(git config submodule.sub.url)" = ssh://hostname:22/subrepo + ) +' + +# About the choice of the path in the next test: +# - double-slash side-steps path mangling issues on Windows +# - it is still an absolute local path +# - there cannot be a server with a blank in its name just in case the +# path is used erroneously to access a //server/share style path +test_expect_success '../subrepo path works with local path - //somewhere else/repo' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url "//somewhere else/repo" && + git submodule init && + test "$(git config submodule.sub.url)" = "//somewhere else/subrepo" + ) +' + +test_expect_success '../subrepo works with file URL - file:///tmp/repo' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url file:///tmp/repo && + git submodule init && + test "$(git config submodule.sub.url)" = file:///tmp/subrepo + ) +' + +test_expect_success '../subrepo works with helper URL- helper:://hostname/repo' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url helper:://hostname/repo && + git submodule init && + test "$(git config submodule.sub.url)" = helper:://hostname/subrepo + ) +' + +test_expect_success '../subrepo works with scp-style URL - user@host:repo' ' ( cd reltest && cp pristine-.git-config .git/config && @@ -498,4 +654,334 @@ test_expect_success 'relative path works with user@host:path' ' ) ' +test_expect_success '../subrepo works with scp-style URL - user@host:path/to/repo' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url user@host:path/to/repo && + git submodule init && + test "$(git config submodule.sub.url)" = user@host:path/to/subrepo + ) +' + +test_expect_success '../subrepo works with relative local path - foo' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url foo && + # actual: fails with an error + git submodule init && + test "$(git config submodule.sub.url)" = subrepo + ) +' + +test_expect_success '../subrepo works with relative local path - foo/bar' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url foo/bar && + git submodule init && + test "$(git config submodule.sub.url)" = foo/subrepo + ) +' + +test_expect_success '../subrepo works with relative local path - ./foo' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url ./foo && + git submodule init && + test "$(git config submodule.sub.url)" = subrepo + ) +' + +test_expect_success '../subrepo works with relative local path - ./foo/bar' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url ./foo/bar && + git submodule init && + test "$(git config submodule.sub.url)" = foo/subrepo + ) +' + +test_expect_success '../subrepo works with relative local path - ../foo' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url ../foo && + git submodule init && + test "$(git config submodule.sub.url)" = ../subrepo + ) +' + +test_expect_success '../subrepo works with relative local path - ../foo/bar' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + git config remote.origin.url ../foo/bar && + git submodule init && + test "$(git config submodule.sub.url)" = ../foo/subrepo + ) +' + +test_expect_success '../bar/a/b/c works with relative local path - ../foo/bar.git' ' + ( + cd reltest && + cp pristine-.git-config .git/config && + cp pristine-.gitmodules .gitmodules && + mkdir -p a/b/c && + (cd a/b/c; git init) && + git config remote.origin.url ../foo/bar.git && + git submodule add ../bar/a/b/c ./a/b/c && + git submodule init && + test "$(git config submodule.a/b/c.url)" = ../foo/bar/a/b/c + ) +' + +test_expect_success 'moving the superproject does not break submodules' ' + ( + cd addtest && + git submodule status >expect + ) + mv addtest addtest2 && + ( + cd addtest2 && + git submodule status >actual && + test_cmp expect actual + ) +' + +test_expect_success 'submodule add --name allows to replace a submodule with another at the same path' ' + ( + cd addtest2 && + ( + cd repo && + echo "$submodurl/repo" >expect && + git config remote.origin.url >actual && + test_cmp expect actual && + echo "gitdir: ../.git/modules/repo" >expect && + test_cmp expect .git + ) && + rm -rf repo && + git rm repo && + git submodule add -q --name repo_new "$submodurl/bare.git" repo >actual && + test_must_be_empty actual && + echo "gitdir: ../.git/modules/submod" >expect && + test_cmp expect submod/.git && + ( + cd repo && + echo "$submodurl/bare.git" >expect && + git config remote.origin.url >actual && + test_cmp expect actual && + echo "gitdir: ../.git/modules/repo_new" >expect && + test_cmp expect .git + ) && + echo "repo" >expect && + test_must_fail git config -f .gitmodules submodule.repo.path && + git config -f .gitmodules submodule.repo_new.path >actual && + test_cmp expect actual&& + echo "$submodurl/repo" >expect && + test_must_fail git config -f .gitmodules submodule.repo.url && + echo "$submodurl/bare.git" >expect && + git config -f .gitmodules submodule.repo_new.url >actual && + test_cmp expect actual && + echo "$submodurl/repo" >expect && + git config submodule.repo.url >actual && + test_cmp expect actual && + echo "$submodurl/bare.git" >expect && + git config submodule.repo_new.url >actual && + test_cmp expect actual + ) +' + +test_expect_success 'submodule add with an existing name fails unless forced' ' + ( + cd addtest2 && + rm -rf repo && + git rm repo && + test_must_fail git submodule add -q --name repo_new "$submodurl/repo.git" repo && + test ! -d repo && + test_must_fail git config -f .gitmodules submodule.repo_new.path && + test_must_fail git config -f .gitmodules submodule.repo_new.url && + echo "$submodurl/bare.git" >expect && + git config submodule.repo_new.url >actual && + test_cmp expect actual && + git submodule add -f -q --name repo_new "$submodurl/repo.git" repo && + test -d repo && + echo "repo" >expect && + git config -f .gitmodules submodule.repo_new.path >actual && + test_cmp expect actual&& + echo "$submodurl/repo.git" >expect && + git config -f .gitmodules submodule.repo_new.url >actual && + test_cmp expect actual && + echo "$submodurl/repo.git" >expect && + git config submodule.repo_new.url >actual && + test_cmp expect actual + ) +' + +test_expect_success 'set up a second submodule' ' + git submodule add ./init2 example2 && + git commit -m "submodule example2 added" +' + +test_expect_success 'submodule deinit should remove the whole submodule section from .git/config' ' + git config submodule.example.foo bar && + git config submodule.example2.frotz nitfol && + git submodule deinit init && + test -z "$(git config --get-regexp "submodule\.example\.")" && + test -n "$(git config --get-regexp "submodule\.example2\.")" && + test -f example2/.git && + rmdir init +' + +test_expect_success 'submodule deinit from subdirectory' ' + git submodule update --init && + git config submodule.example.foo bar && + mkdir -p sub && + ( + cd sub && + git submodule deinit ../init >../output + ) && + grep "\\.\\./init" output && + test -z "$(git config --get-regexp "submodule\.example\.")" && + test -n "$(git config --get-regexp "submodule\.example2\.")" && + test -f example2/.git && + rmdir init +' + +test_expect_success 'submodule deinit . deinits all initialized submodules' ' + git submodule update --init && + git config submodule.example.foo bar && + git config submodule.example2.frotz nitfol && + test_must_fail git submodule deinit && + git submodule deinit . >actual && + test -z "$(git config --get-regexp "submodule\.example\.")" && + test -z "$(git config --get-regexp "submodule\.example2\.")" && + test_i18ngrep "Cleared directory .init" actual && + test_i18ngrep "Cleared directory .example2" actual && + rmdir init example2 +' + +test_expect_success 'submodule deinit deinits a submodule when its work tree is missing or empty' ' + git submodule update --init && + rm -rf init example2/* example2/.git && + git submodule deinit init example2 >actual && + test -z "$(git config --get-regexp "submodule\.example\.")" && + test -z "$(git config --get-regexp "submodule\.example2\.")" && + test_i18ngrep ! "Cleared directory .init" actual && + test_i18ngrep "Cleared directory .example2" actual && + rmdir init +' + +test_expect_success 'submodule deinit fails when the submodule contains modifications unless forced' ' + git submodule update --init && + echo X >>init/s && + test_must_fail git submodule deinit init && + test -n "$(git config --get-regexp "submodule\.example\.")" && + test -f example2/.git && + git submodule deinit -f init >actual && + test -z "$(git config --get-regexp "submodule\.example\.")" && + test_i18ngrep "Cleared directory .init" actual && + rmdir init +' + +test_expect_success 'submodule deinit fails when the submodule contains untracked files unless forced' ' + git submodule update --init && + echo X >>init/untracked && + test_must_fail git submodule deinit init && + test -n "$(git config --get-regexp "submodule\.example\.")" && + test -f example2/.git && + git submodule deinit -f init >actual && + test -z "$(git config --get-regexp "submodule\.example\.")" && + test_i18ngrep "Cleared directory .init" actual && + rmdir init +' + +test_expect_success 'submodule deinit fails when the submodule HEAD does not match unless forced' ' + git submodule update --init && + ( + cd init && + git checkout HEAD^ + ) && + test_must_fail git submodule deinit init && + test -n "$(git config --get-regexp "submodule\.example\.")" && + test -f example2/.git && + git submodule deinit -f init >actual && + test -z "$(git config --get-regexp "submodule\.example\.")" && + test_i18ngrep "Cleared directory .init" actual && + rmdir init +' + +test_expect_success 'submodule deinit is silent when used on an uninitialized submodule' ' + git submodule update --init && + git submodule deinit init >actual && + test_i18ngrep "Submodule .example. (.*) unregistered for path .init" actual && + test_i18ngrep "Cleared directory .init" actual && + git submodule deinit init >actual && + test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual && + test_i18ngrep "Cleared directory .init" actual && + git submodule deinit . >actual && + test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual && + test_i18ngrep "Submodule .example2. (.*) unregistered for path .example2" actual && + test_i18ngrep "Cleared directory .init" actual && + git submodule deinit . >actual && + test_i18ngrep ! "Submodule .example. (.*) unregistered for path .init" actual && + test_i18ngrep ! "Submodule .example2. (.*) unregistered for path .example2" actual && + test_i18ngrep "Cleared directory .init" actual && + rmdir init example2 +' + +test_expect_success 'submodule deinit fails when submodule has a .git directory even when forced' ' + git submodule update --init && + ( + cd init && + rm .git && + cp -R ../.git/modules/example .git && + GIT_WORK_TREE=. git config --unset core.worktree + ) && + test_must_fail git submodule deinit init && + test_must_fail git submodule deinit -f init && + test -d init/.git && + test -n "$(git config --get-regexp "submodule\.example\.")" +' + +test_expect_success 'submodule with UTF-8 name' ' + svname=$(printf "\303\245 \303\244\303\266") && + mkdir "$svname" && + ( + cd "$svname" && + git init && + >sub && + git add sub && + git commit -m "init sub" + ) && + git submodule add ./"$svname" && + git submodule >&2 && + test -n "$(git submodule | grep "$svname")" +' + +test_expect_success 'submodule add clone shallow submodule' ' + mkdir super && + pwd=$(pwd) + ( + cd super && + git init && + git submodule add --depth=1 file://"$pwd"/example2 submodule && + ( + cd submodule && + test 1 = $(git log --oneline | wc -l) + ) + ) +' + + test_done diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh index 30b429e7dc..366746f0d4 100755 --- a/t/t7401-submodule-summary.sh +++ b/t/t7401-submodule-summary.sh @@ -45,6 +45,42 @@ EOF test_cmp expected actual " +test_expect_success 'added submodule (subdirectory)' " + mkdir sub && + ( + cd sub && + git submodule summary >../actual + ) && + cat >expected <<-EOF && +* ../sm1 0000000...$head1 (2): + > Add foo2 + +EOF + test_cmp expected actual +" + +test_expect_success 'added submodule (subdirectory only)' " + ( + cd sub && + git submodule summary . >../actual + ) && + >expected && + test_cmp expected actual +" + +test_expect_success 'added submodule (subdirectory with explicit path)' " + ( + cd sub && + git submodule summary ../sm1 >../actual + ) && + cat >expected <<-EOF && +* ../sm1 0000000...$head1 (2): + > Add foo2 + +EOF + test_cmp expected actual +" + commit_file sm1 && head2=$(add_file sm1 foo3) @@ -68,6 +104,24 @@ EOF test_cmp expected actual " +test_expect_success 'no ignore=all setting has any effect' " + git config -f .gitmodules submodule.sm1.path sm1 && + git config -f .gitmodules submodule.sm1.ignore all && + git config submodule.sm1.ignore all && + git config diff.ignoreSubmodules all && + git submodule summary >actual && + cat >expected <<-EOF && +* sm1 $head1...$head2 (1): + > Add foo3 + +EOF + test_cmp expected actual && + git config --unset diff.ignoreSubmodules && + git config --remove-section submodule.sm1 && + git config -f .gitmodules --remove-section submodule.sm1 +" + + commit_file sm1 && head3=$( cd sm1 && @@ -76,8 +130,8 @@ head3=$( ) test_expect_success 'modified submodule(backward)' " - git submodule summary >actual && - cat >expected <<-EOF && + git submodule summary >actual && + cat >expected <<-EOF && * sm1 $head2...$head3 (2): < Add foo3 < Add foo2 @@ -89,8 +143,8 @@ EOF head4=$(add_file sm1 foo4 foo5) && head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD) test_expect_success 'modified submodule(backward and forward)' " - git submodule summary >actual && - cat >expected <<-EOF && + git submodule summary >actual && + cat >expected <<-EOF && * sm1 $head2...$head4 (4): > Add foo5 > Add foo4 @@ -102,15 +156,15 @@ EOF " test_expect_success '--summary-limit' " - git submodule summary -n 3 >actual && - cat >expected <<-EOF && + git submodule summary -n 3 >actual && + cat >expected <<-EOF && * sm1 $head2...$head4 (4): > Add foo5 > Add foo4 < Add foo3 EOF - test_cmp expected actual + test_cmp expected actual " commit_file sm1 && @@ -122,8 +176,8 @@ rm -f sm1 && mv sm1-bak sm1 test_expect_success 'typechanged submodule(submodule->blob), --cached' " - git submodule summary --cached >actual && - cat >expected <<-EOF && + git submodule summary --cached >actual && + cat >expected <<-EOF && * sm1 $head4(submodule)->$head5(blob) (3): < Add foo5 @@ -132,59 +186,59 @@ EOF " test_expect_success 'typechanged submodule(submodule->blob), --files' " - git submodule summary --files >actual && - cat >expected <<-EOF && + git submodule summary --files >actual && + cat >expected <<-EOF && * sm1 $head5(blob)->$head4(submodule) (3): > Add foo5 EOF - test_i18ncmp actual expected + test_i18ncmp actual expected " rm -rf sm1 && git checkout-index sm1 test_expect_success 'typechanged submodule(submodule->blob)' " - git submodule summary >actual && - cat >expected <<-EOF && + git submodule summary >actual && + cat >expected <<-EOF && * sm1 $head4(submodule)->$head5(blob): EOF - test_i18ncmp actual expected + test_i18ncmp actual expected " rm -f sm1 && test_create_repo sm1 && head6=$(add_file sm1 foo6 foo7) test_expect_success 'nonexistent commit' " - git submodule summary >actual && - cat >expected <<-EOF && + git submodule summary >actual && + cat >expected <<-EOF && * sm1 $head4...$head6: Warn: sm1 doesn't contain commit $head4_full EOF - test_i18ncmp actual expected + test_i18ncmp actual expected " commit_file test_expect_success 'typechanged submodule(blob->submodule)' " - git submodule summary >actual && - cat >expected <<-EOF && + git submodule summary >actual && + cat >expected <<-EOF && * sm1 $head5(blob)->$head6(submodule) (2): > Add foo7 EOF - test_i18ncmp expected actual + test_i18ncmp expected actual " commit_file sm1 && rm -rf sm1 test_expect_success 'deleted submodule' " - git submodule summary >actual && - cat >expected <<-EOF && + git submodule summary >actual && + cat >expected <<-EOF && * sm1 $head6...0000000: EOF - test_cmp expected actual + test_cmp expected actual " test_create_repo sm2 && @@ -192,62 +246,60 @@ head7=$(add_file sm2 foo8 foo9) && git add sm2 test_expect_success 'multiple submodules' " - git submodule summary >actual && - cat >expected <<-EOF && + git submodule summary >actual && + cat >expected <<-EOF && * sm1 $head6...0000000: * sm2 0000000...$head7 (2): > Add foo9 EOF - test_cmp expected actual + test_cmp expected actual " test_expect_success 'path filter' " - git submodule summary sm2 >actual && - cat >expected <<-EOF && + git submodule summary sm2 >actual && + cat >expected <<-EOF && * sm2 0000000...$head7 (2): > Add foo9 EOF - test_cmp expected actual + test_cmp expected actual " commit_file sm2 test_expect_success 'given commit' " - git submodule summary HEAD^ >actual && - cat >expected <<-EOF && + git submodule summary HEAD^ >actual && + cat >expected <<-EOF && * sm1 $head6...0000000: * sm2 0000000...$head7 (2): > Add foo9 EOF - test_cmp expected actual + test_cmp expected actual " test_expect_success '--for-status' " - git submodule summary --for-status HEAD^ >actual && - test_i18ncmp actual - <<EOF -# Submodule changes to be committed: -# -# * sm1 $head6...0000000: -# -# * sm2 0000000...$head7 (2): -# > Add foo9 -# + git submodule summary --for-status HEAD^ >actual && + test_i18ncmp actual - <<EOF +* sm1 $head6...0000000: + +* sm2 0000000...$head7 (2): + > Add foo9 + EOF " test_expect_success 'fail when using --files together with --cached' " - test_must_fail git submodule summary --files --cached + test_must_fail git submodule summary --files --cached " test_expect_success 'should not fail in an empty repo' " - git init xyzzy && - cd xyzzy && - git submodule summary >output 2>&1 && - test_cmp output /dev/null + git init xyzzy && + cd xyzzy && + git submodule summary >output 2>&1 && + test_cmp output /dev/null " test_done diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh index f919c8d34d..8e32f19007 100755 --- a/t/t7402-submodule-rebase.sh +++ b/t/t7402-submodule-rebase.sh @@ -3,7 +3,7 @@ # Copyright (c) 2008 Johannes Schindelin # -test_description='Test rebasing and stashing with dirty submodules' +test_description='Test rebasing, stashing, etc. with submodules' . ./test-lib.sh @@ -20,7 +20,8 @@ test_expect_success setup ' echo second line >> file && (cd submodule && git pull) && test_tick && - git commit -m file-and-submodule -a + git commit -m file-and-submodule -a && + git branch added-submodule ' @@ -89,4 +90,29 @@ test_expect_success 'stash with a dirty submodule' ' ' +test_expect_success 'rebasing submodule that should conflict' ' + git reset --hard && + git checkout added-submodule && + git add submodule && + test_tick && + git commit -m third && + ( + cd submodule && + git commit --allow-empty -m extra + ) && + git add submodule && + test_tick && + git commit -m fourth && + + test_must_fail git rebase --onto HEAD^^ HEAD^ HEAD^0 && + git ls-files -s submodule >actual && + ( + cd submodule && + echo "160000 $(git rev-parse HEAD^) 1 submodule" && + echo "160000 $(git rev-parse HEAD^^) 2 submodule" && + echo "160000 $(git rev-parse HEAD) 3 submodule" + ) >expect && + test_cmp expect actual +' + test_done diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh index 3620215c1f..79bc135bf6 100755 --- a/t/t7403-submodule-sync.sh +++ b/t/t7403-submodule-sync.sh @@ -11,79 +11,340 @@ These tests exercise the "git submodule sync" subcommand. . ./test-lib.sh test_expect_success setup ' - echo file > file && + echo file >file && git add file && test_tick && git commit -m upstream && git clone . super && git clone super submodule && - (cd super && - git submodule add ../submodule submodule && - test_tick && - git commit -m "submodule" + ( + cd submodule && + git submodule add ../submodule sub-submodule && + test_tick && + git commit -m "sub-submodule" + ) && + ( + cd super && + git submodule add ../submodule submodule && + test_tick && + git commit -m "submodule" ) && git clone super super-clone && - (cd super-clone && git submodule update --init) && + ( + cd super-clone && + git submodule update --init --recursive + ) && git clone super empty-clone && - (cd empty-clone && git submodule init) && - git clone super top-only-clone + ( + cd empty-clone && + git submodule init + ) && + git clone super top-only-clone && + git clone super relative-clone && + ( + cd relative-clone && + git submodule update --init --recursive + ) && + git clone super recursive-clone && + ( + cd recursive-clone && + git submodule update --init --recursive + ) ' test_expect_success 'change submodule' ' - (cd submodule && - echo second line >> file && - test_tick && - git commit -a -m "change submodule" + ( + cd submodule && + echo second line >>file && + test_tick && + git commit -a -m "change submodule" ) ' +reset_submodule_urls () { + local root + root=$(pwd) && + ( + cd super-clone/submodule && + git config remote.origin.url "$root/submodule" + ) && + ( + cd super-clone/submodule/sub-submodule && + git config remote.origin.url "$root/submodule" + ) +} + test_expect_success 'change submodule url' ' - (cd super && - cd submodule && - git checkout master && - git pull + ( + cd super && + cd submodule && + git checkout master && + git pull ) && mv submodule moved-submodule && - (cd super && - git config -f .gitmodules submodule.submodule.url ../moved-submodule && - test_tick && - git commit -a -m moved-submodule + ( + cd moved-submodule && + git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule && + test_tick && + git commit -a -m moved-sub-submodule + ) && + ( + cd super && + git config -f .gitmodules submodule.submodule.url ../moved-submodule && + test_tick && + git commit -a -m moved-submodule ) ' test_expect_success '"git submodule sync" should update submodule URLs' ' - (cd super-clone && - git pull --no-recurse-submodules && - git submodule sync + ( + cd super-clone && + git pull --no-recurse-submodules && + git submodule sync + ) && + test -d "$( + cd super-clone/submodule && + git config remote.origin.url + )" && + test ! -d "$( + cd super-clone/submodule/sub-submodule && + git config remote.origin.url + )" && + ( + cd super-clone/submodule && + git checkout master && + git pull + ) && + ( + cd super-clone && + test -d "$(git config submodule.submodule.url)" + ) +' + +test_expect_success '"git submodule sync --recursive" should update all submodule URLs' ' + ( + cd super-clone && + ( + cd submodule && + git pull --no-recurse-submodules + ) && + git submodule sync --recursive + ) && + test -d "$( + cd super-clone/submodule && + git config remote.origin.url + )" && + test -d "$( + cd super-clone/submodule/sub-submodule && + git config remote.origin.url + )" && + ( + cd super-clone/submodule/sub-submodule && + git checkout master && + git pull + ) +' + +test_expect_success 'reset submodule URLs' ' + reset_submodule_urls super-clone +' + +test_expect_success '"git submodule sync" should update submodule URLs - subdirectory' ' + ( + cd super-clone && + git pull --no-recurse-submodules && + mkdir -p sub && + cd sub && + git submodule sync >../../output ) && - test -d "$(cd super-clone/submodule && - git config remote.origin.url + grep "\\.\\./submodule" output && + test -d "$( + cd super-clone/submodule && + git config remote.origin.url + )" && + test ! -d "$( + cd super-clone/submodule/sub-submodule && + git config remote.origin.url )" && - (cd super-clone/submodule && - git checkout master && - git pull + ( + cd super-clone/submodule && + git checkout master && + git pull + ) && + ( + cd super-clone && + test -d "$(git config submodule.submodule.url)" + ) +' + +test_expect_success '"git submodule sync --recursive" should update all submodule URLs - subdirectory' ' + ( + cd super-clone && + ( + cd submodule && + git pull --no-recurse-submodules + ) && + mkdir -p sub && + cd sub && + git submodule sync --recursive >../../output ) && - (cd super-clone && - test -d "$(git config submodule.submodule.url)" + grep "\\.\\./submodule/sub-submodule" output && + test -d "$( + cd super-clone/submodule && + git config remote.origin.url + )" && + test -d "$( + cd super-clone/submodule/sub-submodule && + git config remote.origin.url + )" && + ( + cd super-clone/submodule/sub-submodule && + git checkout master && + git pull ) ' test_expect_success '"git submodule sync" should update known submodule URLs' ' - (cd empty-clone && - git pull && - git submodule sync && - test -d "$(git config submodule.submodule.url)" + ( + cd empty-clone && + git pull && + git submodule sync && + test -d "$(git config submodule.submodule.url)" ) ' test_expect_success '"git submodule sync" should not vivify uninteresting submodule' ' - (cd top-only-clone && - git pull && - git submodule sync && - test -z "$(git config submodule.submodule.url)" && - git submodule sync submodule && - test -z "$(git config submodule.submodule.url)" + ( + cd top-only-clone && + git pull && + git submodule sync && + test -z "$(git config submodule.submodule.url)" && + git submodule sync submodule && + test -z "$(git config submodule.submodule.url)" + ) +' + +test_expect_success '"git submodule sync" handles origin URL of the form foo' ' + ( + cd relative-clone && + git remote set-url origin foo && + git submodule sync && + ( + cd submodule && + #actual fails with: "cannot strip off url foo + test "$(git config remote.origin.url)" = "../submodule" + ) + ) +' + +test_expect_success '"git submodule sync" handles origin URL of the form foo/bar' ' + ( + cd relative-clone && + git remote set-url origin foo/bar && + git submodule sync && + ( + cd submodule && + #actual foo/submodule + test "$(git config remote.origin.url)" = "../foo/submodule" + ) && + ( + cd submodule/sub-submodule && + test "$(git config remote.origin.url)" != "../../foo/submodule" + ) + ) +' + +test_expect_success '"git submodule sync --recursive" propagates changes in origin' ' + ( + cd recursive-clone && + git remote set-url origin foo/bar && + git submodule sync --recursive && + ( + cd submodule && + #actual foo/submodule + test "$(git config remote.origin.url)" = "../foo/submodule" + ) && + ( + cd submodule/sub-submodule && + test "$(git config remote.origin.url)" = "../../foo/submodule" + ) + ) +' + +test_expect_success '"git submodule sync" handles origin URL of the form ./foo' ' + ( + cd relative-clone && + git remote set-url origin ./foo && + git submodule sync && + ( + cd submodule && + #actual ./submodule + test "$(git config remote.origin.url)" = "../submodule" + ) + ) +' + +test_expect_success '"git submodule sync" handles origin URL of the form ./foo/bar' ' + ( + cd relative-clone && + git remote set-url origin ./foo/bar && + git submodule sync && + ( + cd submodule && + #actual ./foo/submodule + test "$(git config remote.origin.url)" = "../foo/submodule" + ) + ) +' + +test_expect_success '"git submodule sync" handles origin URL of the form ../foo' ' + ( + cd relative-clone && + git remote set-url origin ../foo && + git submodule sync && + ( + cd submodule && + #actual ../submodule + test "$(git config remote.origin.url)" = "../../submodule" + ) ) ' +test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar' ' + ( + cd relative-clone && + git remote set-url origin ../foo/bar && + git submodule sync && + ( + cd submodule && + #actual ../foo/submodule + test "$(git config remote.origin.url)" = "../../foo/submodule" + ) + ) +' + +test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar with deeply nested submodule' ' + ( + cd relative-clone && + git remote set-url origin ../foo/bar && + mkdir -p a/b/c && + ( + cd a/b/c && + git init && + >.gitignore && + git add .gitignore && + test_tick && + git commit -m "initial commit" + ) && + git submodule add ../bar/a/b/c ./a/b/c && + git submodule sync && + ( + cd a/b/c && + #actual ../foo/bar/a/b/c + test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c" + ) + ) +' + + test_done diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 5b97222c48..0825a928df 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -58,7 +58,7 @@ test_expect_success 'setup a submodule tree' ' git submodule add ../merging merging && test_tick && git commit -m "rebasing" - ) + ) && (cd super && git submodule add ../none none && test_tick && @@ -80,6 +80,21 @@ test_expect_success 'submodule update detaching the HEAD ' ' ) ' +test_expect_success 'submodule update from subdirectory' ' + (cd super/submodule && + git reset --hard HEAD~1 + ) && + mkdir super/sub && + (cd super/sub && + (cd ../submodule && + compare_head + ) && + git submodule update ../submodule && + cd ../submodule && + ! compare_head + ) +' + apos="'"; test_expect_success 'submodule update does not fetch already present commits' ' (cd submodule && @@ -123,6 +138,49 @@ test_expect_success 'submodule update should throw away changes with --force ' ' ) ' +test_expect_success 'submodule update --force forcibly checks out submodules' ' + (cd super && + (cd submodule && + rm -f file + ) && + git submodule update --force submodule && + (cd submodule && + test "$(git status -s file)" = "" + ) + ) +' + +test_expect_success 'submodule update --remote should fetch upstream changes' ' + (cd submodule && + echo line4 >> file && + git add file && + test_tick && + git commit -m "upstream line4" + ) && + (cd super && + git submodule update --remote --force submodule && + cd submodule && + test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline)" + ) +' + +test_expect_success 'local config should override .gitmodules branch' ' + (cd submodule && + git checkout -b test-branch && + echo line5 >> file && + git add file && + test_tick && + git commit -m "upstream line5" && + git checkout master + ) && + (cd super && + git config submodule.submodule.branch test-branch && + git submodule update --remote --force submodule && + cd submodule && + test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline test-branch)" + ) +' + test_expect_success 'submodule update --rebase staying on master' ' (cd super/submodule && git checkout master @@ -236,6 +294,50 @@ test_expect_success 'submodule update - checkout in .git/config' ' ) ' +test_expect_success 'submodule update - command in .git/config' ' + (cd super && + git config submodule.submodule.update "!git checkout" + ) && + (cd super/submodule && + git reset --hard HEAD^ + ) && + (cd super && + (cd submodule && + compare_head + ) && + git submodule update submodule && + cd submodule && + ! compare_head + ) +' + +test_expect_success 'submodule update - command in .git/config catches failure' ' + (cd super && + git config submodule.submodule.update "!false" + ) && + (cd super/submodule && + git reset --hard HEAD^ + ) && + (cd super && + test_must_fail git submodule update submodule + ) +' + +test_expect_success 'submodule init does not copy command into .git/config' ' + (cd super && + H=$(git ls-files -s submodule | cut -d" " -f2) && + mkdir submodule1 && + git update-index --add --cacheinfo 160000 $H submodule1 && + git config -f .gitmodules submodule.submodule1.path submodule1 && + git config -f .gitmodules submodule.submodule1.url ../submodule && + git config -f .gitmodules submodule.submodule1.update !false && + git submodule init submodule1 && + echo "none" >expect && + git config submodule.submodule1.update >actual && + test_cmp expect actual + ) +' + test_expect_success 'submodule init picks up rebase' ' (cd super && git config -f .gitmodules submodule.rebasing.update rebase && @@ -367,7 +469,7 @@ test_expect_success 'submodule update continues after checkout error' ' git submodule init && git commit -am "new_submodule" && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../expect + git rev-parse --verify HEAD >../expect ) && (cd submodule && test_commit "update_submodule" file @@ -384,7 +486,7 @@ test_expect_success 'submodule update continues after checkout error' ' git checkout HEAD^ && test_must_fail git submodule update && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../actual + git rev-parse --verify HEAD >../actual ) && test_cmp expect actual ) @@ -413,7 +515,7 @@ test_expect_success 'submodule update continues after recursive checkout error' test_commit "update_submodule_again_again" file ) && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../expect && + git rev-parse --verify HEAD >../expect && test_commit "update_submodule2_again" file ) && git add submodule && @@ -428,7 +530,7 @@ test_expect_success 'submodule update continues after recursive checkout error' ) && test_must_fail git submodule update --recursive && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../actual + git rev-parse --verify HEAD >../actual ) && test_cmp expect actual ) @@ -460,12 +562,12 @@ test_expect_success 'submodule update exit immediately in case of merge conflict ) && git checkout HEAD^ && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../expect + git rev-parse --verify HEAD >../expect ) && git config submodule.submodule.update merge && test_must_fail git submodule update && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../actual + git rev-parse --verify HEAD >../actual ) && test_cmp expect actual ) @@ -495,12 +597,12 @@ test_expect_success 'submodule update exit immediately after recursive rebase er ) && git checkout HEAD^ && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../expect + git rev-parse --verify HEAD >../expect ) && git config submodule.submodule.update rebase && test_must_fail git submodule update && (cd submodule2 && - git rev-parse --max-count=1 HEAD > ../actual + git rev-parse --verify HEAD >../actual ) && test_cmp expect actual ) @@ -553,14 +655,14 @@ test_expect_success 'submodule add places git-dir in superprojects git-dir recur git log > ../../../expected ) && git commit -m "added subsubmodule" && - git push + git push origin : ) && (cd .git/modules/deeper/submodule/modules/subsubmodule && git log > ../../../../../actual ) && git add deeper/submodule && git commit -m "update submodule" && - git push && + git push origin : && test_cmp actual expected ) ' @@ -600,7 +702,8 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re rm -rf super_update_r2 && git clone super_update_r super_update_r2 && (cd super_update_r2 && - git submodule update --init --recursive && + git submodule update --init --recursive >actual && + test_i18ngrep "Submodule path .submodule/subsubmodule.: checked out" actual && (cd submodule/subsubmodule && git log > ../../expected ) && @@ -615,8 +718,50 @@ test_expect_success 'submodule add properly re-creates deeper level submodules' (cd super && git reset --hard master && rm -rf deeper/ && - git submodule add ../submodule deeper/submodule + git submodule add --force ../submodule deeper/submodule + ) +' + +test_expect_success 'submodule update properly revives a moved submodule' ' + (cd super && + H=$(git rev-parse --short HEAD) && + git commit -am "pre move" && + H2=$(git rev-parse --short HEAD) && + git status | sed "s/$H/XXX/" >expect && + H=$(cd submodule2; git rev-parse HEAD) && + git rm --cached submodule2 && + rm -rf submodule2 && + mkdir -p "moved/sub module" && + git update-index --add --cacheinfo 160000 $H "moved/sub module" && + git config -f .gitmodules submodule.submodule2.path "moved/sub module" + git commit -am "post move" && + git submodule update && + git status | sed "s/$H2/XXX/" >actual && + test_cmp expect actual + ) +' + +test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd' ' + mkdir -p linked/dir && + ln -s linked/dir linkto && + (cd linkto && + git clone "$TRASH_DIRECTORY"/super_update_r2 super && + (cd super && + git submodule update --init --recursive + ) ) ' +test_expect_success 'submodule update clone shallow submodule' ' + git clone cloned super3 && + pwd=$(pwd) + (cd super3 && + sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp && + mv -f .gitmodules.tmp .gitmodules && + git submodule update --init --depth=3 + (cd submodule && + test 1 = $(git log --oneline | wc -l) + ) + ) +' test_done diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh index 9b69fe2e14..b64c9ed8e7 100755 --- a/t/t7407-submodule-foreach.sh +++ b/t/t7407-submodule-foreach.sh @@ -80,6 +80,22 @@ test_expect_success 'test basic "submodule foreach" usage' ' test_i18ncmp expect actual ' +cat >expect <<EOF +Entering '../sub1' +$pwd/clone-foo1-../sub1-$sub1sha1 +Entering '../sub3' +$pwd/clone-foo3-../sub3-$sub3sha1 +EOF + +test_expect_success 'test "submodule foreach" from subdirectory' ' + mkdir clone/sub && + ( + cd clone/sub && + git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$sha1" >../../actual + ) && + test_i18ncmp expect actual +' + test_expect_success 'setup nested submodules' ' git clone submodule nested1 && git clone submodule nested2 && @@ -129,7 +145,7 @@ test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' ' git rev-parse --resolve-git-dir nested1/.git && test_must_fail git rev-parse --resolve-git-dir nested1/nested2/.git && git submodule foreach "git submodule update --init" && - git rev-parse --resolve-git-dir nested1/nested1/nested2/.git + git rev-parse --resolve-git-dir nested1/nested2/.git && test_must_fail git rev-parse --resolve-git-dir nested1/nested2/nested3/.git ) ' @@ -226,22 +242,18 @@ test_expect_success 'test "status --recursive"' ' test_cmp expect actual ' -sed -e "/nested1 /s/.*/+$nested1sha1 nested1 (file2~1)/;/sub[1-3]/d" < expect > expect2 +sed -e "/nested2 /s/.*/+$nested2sha1 nested1\/nested2 (file2~1)/;/sub[1-3]/d" < expect > expect2 mv -f expect2 expect test_expect_success 'ensure "status --cached --recursive" preserves the --cached flag' ' ( cd clone3 && ( - cd nested1 && + cd nested1/nested2 && test_commit file2 ) && git submodule status --cached --recursive -- nested1 > ../actual ) && - if test_have_prereq MINGW - then - dos2unix actual - fi && test_cmp expect actual ' diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh index ab37c368d0..b770b2f04d 100755 --- a/t/t7408-submodule-reference.sh +++ b/t/t7408-submodule-reference.sh @@ -28,7 +28,7 @@ git prune' cd "$base_dir" -test_expect_success 'preparing supermodule' \ +test_expect_success 'preparing superproject' \ 'test_create_repo super && cd super && echo file > file && git add file && @@ -43,7 +43,7 @@ git commit -m B-super-added' cd "$base_dir" test_expect_success 'after add: existence of info/alternates' \ -'test `wc -l <super/.git/modules/sub/objects/info/alternates` = 1' +'test_line_count = 1 super/.git/modules/sub/objects/info/alternates' cd "$base_dir" @@ -55,7 +55,7 @@ diff expected current' cd "$base_dir" -test_expect_success 'cloning supermodule' \ +test_expect_success 'cloning superproject' \ 'git clone super super-clone' cd "$base_dir" @@ -66,7 +66,7 @@ test_expect_success 'update with reference' \ cd "$base_dir" test_expect_success 'after update: existence of info/alternates' \ -'test `wc -l <super-clone/.git/modules/sub/objects/info/alternates` = 1' +'test_line_count = 1 super-clone/.git/modules/sub/objects/info/alternates' cd "$base_dir" diff --git a/t/t7409-submodule-detached-worktree.sh b/t/t7409-submodule-detached-worktree.sh new file mode 100755 index 0000000000..c20717181e --- /dev/null +++ b/t/t7409-submodule-detached-worktree.sh @@ -0,0 +1,84 @@ +#!/bin/sh +# +# Copyright (c) 2012 Daniel Graña +# + +test_description='Test submodules on detached working tree + +This test verifies that "git submodule" initialization, update and addition works +on detahced working trees +' + +TEST_NO_CREATE_REPO=1 +. ./test-lib.sh + +test_expect_success 'submodule on detached working tree' ' + git init --bare remote && + test_create_repo bundle1 && + ( + cd bundle1 && + test_commit "shoot" && + git rev-parse --verify HEAD >../expect + ) && + mkdir home && + ( + cd home && + GIT_WORK_TREE="$(pwd)" && + GIT_DIR="$(pwd)/.dotfiles" && + export GIT_WORK_TREE GIT_DIR && + git clone --bare ../remote .dotfiles && + git submodule add ../bundle1 .vim/bundle/sogood && + test_commit "sogood" && + ( + unset GIT_WORK_TREE GIT_DIR && + cd .vim/bundle/sogood && + git rev-parse --verify HEAD >actual && + test_cmp ../../../../expect actual + ) && + git push origin master + ) && + mkdir home2 && + ( + cd home2 && + git clone --bare ../remote .dotfiles && + GIT_WORK_TREE="$(pwd)" && + GIT_DIR="$(pwd)/.dotfiles" && + export GIT_WORK_TREE GIT_DIR && + git checkout master && + git submodule update --init && + ( + unset GIT_WORK_TREE GIT_DIR && + cd .vim/bundle/sogood && + git rev-parse --verify HEAD >actual && + test_cmp ../../../../expect actual + ) + ) +' + +test_expect_success 'submodule on detached working pointed by core.worktree' ' + mkdir home3 && + ( + cd home3 && + GIT_DIR="$(pwd)/.dotfiles" && + export GIT_DIR && + git clone --bare ../remote "$GIT_DIR" && + git config core.bare false && + git config core.worktree .. && + git checkout master && + git submodule add ../bundle1 .vim/bundle/dupe && + test_commit "dupe" && + git push origin master + ) && + ( + cd home && + GIT_DIR="$(pwd)/.dotfiles" && + export GIT_DIR && + git config core.bare false && + git config core.worktree .. && + git pull && + git submodule update --init && + test -f .vim/bundle/dupe/shoot.t + ) +' + +test_done diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh index 1c908f4d39..bdc1f29503 100755 --- a/t/t7500-commit.sh +++ b/t/t7500-commit.sh @@ -13,9 +13,9 @@ commit_msg_is () { expect=commit_msg_is.expect actual=commit_msg_is.actual - printf "%s" "$(git log --pretty=format:%s%b -1)" >$expect && - printf "%s" "$1" >$actual && - test_i18ncmp $expect $actual + printf "%s" "$(git log --pretty=format:%s%b -1)" >"$actual" && + printf "%s" "$1" >"$expect" && + test_i18ncmp "$expect" "$actual" } # A sanity check to see if commit is working at all. @@ -36,8 +36,7 @@ test_expect_success 'nonexistent template file should return error' ' ' test_expect_success 'nonexistent template file in config should return error' ' - git config commit.template "$PWD"/notexist && - test_when_finished "git config --unset commit.template" && + test_config commit.template "$PWD"/notexist && ( GIT_EDITOR="echo hello >\"\$1\"" && export GIT_EDITOR && @@ -93,14 +92,13 @@ test_expect_success '-t option should be short for --template' ' test_expect_success 'config-specified template should commit' ' echo "new template" > "$TEMPLATE" && - git config commit.template "$TEMPLATE" && + test_config commit.template "$TEMPLATE" && echo "more content" >> foo && git add foo && ( test_set_editor "$TEST_DIRECTORY"/t7500/add-content && git commit ) && - git config --unset commit.template && commit_msg_is "new templatecommit message" ' diff --git a/t/t7500/add-content-and-comment b/t/t7500/add-content-and-comment new file mode 100755 index 0000000000..c4dccff13a --- /dev/null +++ b/t/t7500/add-content-and-comment @@ -0,0 +1,5 @@ +#!/bin/sh +echo "commit message" >> "$1" +echo "# comment" >> "$1" +exit 0 + diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index 8bb38337a9..f04798f872 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -30,10 +30,12 @@ test_expect_success 'setup: initial commit' ' ' test_expect_success '-m and -F do not mix' ' + git checkout HEAD file && echo >>file && git add file && test_must_fail git commit -m foo -m bar -F file ' test_expect_success '-m and -C do not mix' ' + git checkout HEAD file && echo >>file && git add file && test_must_fail git commit -C HEAD -m illegal ' @@ -51,7 +53,7 @@ test_expect_success PERL 'can use paths with --interactive' ' ' test_expect_success 'using invalid commit with -C' ' - test_must_fail git commit -C bogus + test_must_fail git commit --allow-empty -C bogus ' test_expect_success 'nothing to commit' ' @@ -79,7 +81,19 @@ test_expect_success 'empty commit message' ' test_must_fail git commit -F msg -a ' +test_expect_success 'template "emptyness" check does not kick in with -F' ' + git checkout HEAD file && echo >>file && git add file && + git commit -t file -F file +' + +test_expect_success 'template "emptyness" check' ' + git checkout HEAD file && echo >>file && git add file && + test_must_fail git commit -t file 2>err && + test_i18ngrep "did not edit" err +' + test_expect_success 'setup: commit message from file' ' + git checkout HEAD file && echo >>file && git add file && echo this is the commit message, coming from a file >msg && git commit -F msg -a ' @@ -94,6 +108,16 @@ test_expect_success 'amend commit' ' EDITOR=./editor git commit --amend ' +test_expect_success 'amend --only ignores staged contents' ' + cp file file.expect && + echo changed >file && + git add file && + git commit --no-edit --amend --only && + git cat-file blob HEAD:file >file.actual && + test_cmp file.expect file.actual && + git diff --exit-code +' + test_expect_success 'set up editor' ' cat >editor <<-\EOF && #!/bin/sh @@ -124,6 +148,21 @@ test_expect_success '--amend --edit' ' test_cmp expect msg ' +test_expect_success '--amend --edit of empty message' ' + cat >replace <<-\EOF && + #!/bin/sh + echo "amended" >"$1" + EOF + chmod 755 replace && + git commit --allow-empty --allow-empty-message -m "" && + echo more bongo >file && + git add file && + EDITOR=./replace git commit --edit --amend && + git diff-tree -s --format=%s HEAD >msg && + ./replace expect && + test_cmp expect msg +' + test_expect_success '-m --edit' ' echo amended >expect && git commit --allow-empty -m buffer && @@ -473,4 +512,29 @@ test_expect_success 'amend can copy notes' ' ' +test_expect_success 'commit a file whose name is a dash' ' + git reset --hard && + for i in 1 2 3 4 5 + do + echo $i + done >./- && + git add ./- && + test_tick && + git commit -m "add dash" >output </dev/null && + test_i18ngrep " changed, 5 insertions" output +' + +test_expect_success '--only works on to-be-born branch' ' + # This test relies on having something in the index, as it + # would not otherwise actually prove much. So check this. + test -n "$(git ls-files)" && + git checkout --orphan orphan && + echo foo >newfile && + git add newfile && + git commit --only newfile -m"--only on unborn branch" && + echo newfile >expected && + git ls-tree -r --name-only HEAD >actual && + test_cmp expected actual +' + test_done diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index 3f3adc31b9..6313da2cdd 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -4,6 +4,15 @@ test_description='git commit porcelain-ish' . ./test-lib.sh +commit_msg_is () { + expect=commit_msg_is.expect + actual=commit_msg_is.actual + + printf "%s" "$(git log --pretty=format:%s%b -1)" >$actual && + printf "%s" "$1" >$expect && + test_i18ncmp $expect $actual +} + # Arguments: [<prefix] [<commit message>] [<commit options>] check_summary_oneline() { test_tick && @@ -162,23 +171,30 @@ test_expect_success 'verbose' ' test_expect_success 'verbose respects diff config' ' - git config color.diff always && + test_config color.diff always && git status -v >actual && - grep "\[1mdiff --git" actual && - git config --unset color.diff + grep "\[1mdiff --git" actual +' + +mesg_with_comment_and_newlines=' +# text + +' + +test_expect_success 'prepare file with comment line and trailing newlines' ' + printf "%s" "$mesg_with_comment_and_newlines" >expect ' -test_expect_success 'cleanup commit messages (verbatim,-t)' ' +test_expect_success 'cleanup commit messages (verbatim option,-t)' ' echo >>negative && - { echo;echo "# text";echo; } >expect && - git commit --cleanup=verbatim -t expect -a && - git cat-file -p HEAD |sed -e "1,/^\$/d" |head -n 3 >actual && + git commit --cleanup=verbatim --no-status -t expect -a && + git cat-file -p HEAD |sed -e "1,/^\$/d" >actual && test_cmp expect actual ' -test_expect_success 'cleanup commit messages (verbatim,-F)' ' +test_expect_success 'cleanup commit messages (verbatim option,-F)' ' echo >>negative && git commit --cleanup=verbatim -F expect -a && @@ -187,16 +203,16 @@ test_expect_success 'cleanup commit messages (verbatim,-F)' ' ' -test_expect_success 'cleanup commit messages (verbatim,-m)' ' +test_expect_success 'cleanup commit messages (verbatim option,-m)' ' echo >>negative && - git commit --cleanup=verbatim -m "$(cat expect)" -a && + git commit --cleanup=verbatim -m "$mesg_with_comment_and_newlines" -a && git cat-file -p HEAD |sed -e "1,/^\$/d">actual && test_cmp expect actual ' -test_expect_success 'cleanup commit messages (whitespace,-F)' ' +test_expect_success 'cleanup commit messages (whitespace option,-F)' ' echo >>negative && { echo;echo "# text";echo; } >text && @@ -207,7 +223,7 @@ test_expect_success 'cleanup commit messages (whitespace,-F)' ' ' -test_expect_success 'cleanup commit messages (strip,-F)' ' +test_expect_success 'cleanup commit messages (strip option,-F)' ' echo >>negative && { echo;echo "# text";echo sample;echo; } >text && @@ -218,7 +234,7 @@ test_expect_success 'cleanup commit messages (strip,-F)' ' ' -test_expect_success 'cleanup commit messages (strip,-F,-e)' ' +test_expect_success 'cleanup commit messages (strip option,-F,-e)' ' echo >>negative && { echo;echo sample;echo; } >text && @@ -231,48 +247,136 @@ echo "sample # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit." >expect -test_expect_success 'cleanup commit messages (strip,-F,-e): output' ' +test_expect_success 'cleanup commit messages (strip option,-F,-e): output' ' test_i18ncmp expect actual ' -echo "# -# Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> -#" >> expect +test_expect_success 'cleanup commit message (fail on invalid cleanup mode option)' ' + test_must_fail git commit --cleanup=non-existent +' -test_expect_success 'author different from committer' ' - echo >>negative && - test_might_fail git commit -e -m "sample" && - head -n 7 .git/COMMIT_EDITMSG >actual && - test_i18ncmp expect actual +test_expect_success 'cleanup commit message (fail on invalid cleanup mode configuration)' ' + test_must_fail git -c commit.cleanup=non-existent commit +' + +test_expect_success 'cleanup commit message (no config and no option uses default)' ' + echo content >>file && + git add file && + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment && + git commit --no-status + ) && + commit_msg_is "commit message" +' + +test_expect_success 'cleanup commit message (option overrides default)' ' + echo content >>file && + git add file && + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment && + git commit --cleanup=whitespace --no-status + ) && + commit_msg_is "commit message # comment" +' + +test_expect_success 'cleanup commit message (config overrides default)' ' + echo content >>file && + git add file && + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment && + git -c commit.cleanup=whitespace commit --no-status + ) && + commit_msg_is "commit message # comment" +' + +test_expect_success 'cleanup commit message (option overrides config)' ' + echo content >>file && + git add file && + ( + test_set_editor "$TEST_DIRECTORY"/t7500/add-content-and-comment && + git -c commit.cleanup=whitespace commit --cleanup=default + ) && + commit_msg_is "commit message" +' + +test_expect_success 'cleanup commit message (default, -m)' ' + echo content >>file && + git add file && + git commit -m "message #comment " && + commit_msg_is "message #comment" +' + +test_expect_success 'cleanup commit message (whitespace option, -m)' ' + echo content >>file && + git add file && + git commit --cleanup=whitespace --no-status -m "message #comment " && + commit_msg_is "message #comment" +' + +test_expect_success 'cleanup commit message (whitespace config, -m)' ' + echo content >>file && + git add file && + git -c commit.cleanup=whitespace commit --no-status -m "message #comment " && + commit_msg_is "message #comment" ' -mv expect expect.tmp -sed '$d' < expect.tmp > expect -rm -f expect.tmp -echo "# Committer: -#" >> expect +test_expect_success 'message shows author when it is not equal to committer' ' + echo >>negative && + git commit -e -m "sample" -a && + test_i18ngrep \ + "^# Author: *A U Thor <author@example.com>\$" \ + .git/COMMIT_EDITMSG +' -test_expect_success 'committer is automatic' ' +test_expect_success AUTOIDENT 'message shows committer when it is automatic' ' echo >>negative && ( sane_unset GIT_COMMITTER_EMAIL && sane_unset GIT_COMMITTER_NAME && - # must fail because there is no change - test_must_fail git commit -e -m "sample" + git commit -e -m "sample" -a ) && - head -n 8 .git/COMMIT_EDITMSG | \ - sed "s/^# Committer: .*/# Committer:/" >actual - test_i18ncmp expect actual + # the ident is calculated from the system, so we cannot + # check the actual value, only that it is there + test_i18ngrep "^# Committer: " .git/COMMIT_EDITMSG ' -pwd=`pwd` -cat >> .git/FAKE_EDITOR << EOF -#! /bin/sh -echo editor started > "$pwd/.git/result" +write_script .git/FAKE_EDITOR <<EOF +echo editor started > "$(pwd)/.git/result" exit 0 EOF -chmod +x .git/FAKE_EDITOR + +test_expect_success !AUTOIDENT 'do not fire editor when committer is bogus' ' + >.git/result + >expect && + + echo >>negative && + ( + sane_unset GIT_COMMITTER_EMAIL && + sane_unset GIT_COMMITTER_NAME && + GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" && + export GIT_EDITOR && + test_must_fail git commit -e -m sample -a + ) && + test_cmp expect .git/result +' + +test_expect_success 'do not fire editor if -m <msg> was given' ' + echo tick >file && + git add file && + echo "editor not started" >.git/result && + (GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" git commit -m tick) && + test "$(cat .git/result)" = "editor not started" +' + +test_expect_success 'do not fire editor if -m "" was given' ' + echo tock >file && + git add file && + echo "editor not started" >.git/result && + (GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" \ + git commit -m "" --allow-empty-message) && + test "$(cat .git/result)" = "editor not started" +' test_expect_success 'do not fire editor in the presence of conflicts' ' @@ -293,16 +397,14 @@ test_expect_success 'do not fire editor in the presence of conflicts' ' test_must_fail git cherry-pick -n master && echo "editor not started" >.git/result && ( - GIT_EDITOR="$(pwd)/.git/FAKE_EDITOR" && + GIT_EDITOR="\"$(pwd)/.git/FAKE_EDITOR\"" && export GIT_EDITOR && test_must_fail git commit ) && test "$(cat .git/result)" = "editor not started" ' -pwd=`pwd` -cat >.git/FAKE_EDITOR <<EOF -#! $SHELL_PATH +write_script .git/FAKE_EDITOR <<EOF # kill -TERM command added below. EOF @@ -335,31 +437,32 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo git reset --hard && git commit -s -m "hello: kitty" --allow-empty && git cat-file commit HEAD | sed -e "1,/^$/d" >actual && - test $(wc -l <actual) = 3 + test_line_count = 3 actual + +' + +test_expect_success 'commit -s places sob on third line after two empty lines' ' + git commit -s --allow-empty --allow-empty-message && + cat <<-EOF >expect && + + Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> + + EOF + sed -e "/^#/d" -e "s/^:.*//" .git/COMMIT_EDITMSG >actual && + test_cmp expect actual ' -cat >.git/FAKE_EDITOR <<EOF -#!$SHELL_PATH -mv "\$1" "\$1.orig" +write_script .git/FAKE_EDITOR <<\EOF +mv "$1" "$1.orig" ( echo message - cat "\$1.orig" -) >"\$1" + cat "$1.orig" +) >"$1" EOF echo '## Custom template' >template -clear_config () { - ( - git config --unset-all "$1" - case $? in - 0|5) exit 0 ;; - *) exit 1 ;; - esac - ) -} - try_commit () { git reset --hard && echo >>negative && @@ -375,67 +478,57 @@ try_commit () { try_commit_status_combo () { test_expect_success 'commit' ' - clear_config commit.status && try_commit "" && test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' test_expect_success 'commit' ' - clear_config commit.status && try_commit "" && test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' test_expect_success 'commit --status' ' - clear_config commit.status && try_commit --status && test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' test_expect_success 'commit --no-status' ' - clear_config commit.status && try_commit --no-status && test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG ' test_expect_success 'commit with commit.status = yes' ' - clear_config commit.status && - git config commit.status yes && + test_config commit.status yes && try_commit "" && test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' test_expect_success 'commit with commit.status = no' ' - clear_config commit.status && - git config commit.status no && + test_config commit.status no && try_commit "" && test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG ' test_expect_success 'commit --status with commit.status = yes' ' - clear_config commit.status && - git config commit.status yes && + test_config commit.status yes && try_commit --status && test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' test_expect_success 'commit --no-status with commit.status = yes' ' - clear_config commit.status && - git config commit.status yes && + test_config commit.status yes && try_commit --no-status && test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG ' test_expect_success 'commit --status with commit.status = no' ' - clear_config commit.status && - git config commit.status no && + test_config commit.status no && try_commit --status && test_i18ngrep "^# Changes to be committed:" .git/COMMIT_EDITMSG ' test_expect_success 'commit --no-status with commit.status = no' ' - clear_config commit.status && - git config commit.status no && + test_config commit.status no && try_commit --no-status && test_i18ngrep ! "^# Changes to be committed:" .git/COMMIT_EDITMSG ' @@ -448,4 +541,10 @@ use_template="-t template" try_commit_status_combo +test_expect_success 'commit --status with custom comment character' ' + test_config core.commentchar ";" && + try_commit --status && + test_i18ngrep "^; Changes to be committed:" .git/COMMIT_EDITMSG +' + test_done diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh index ee7f0cd459..984889b39d 100755 --- a/t/t7503-pre-commit-hook.sh +++ b/t/t7503-pre-commit-hook.sh @@ -118,4 +118,22 @@ test_expect_success 'with failing hook requiring GIT_PREFIX' ' git checkout -- file ' +test_expect_success 'check the author in hook' ' + write_script "$HOOK" <<-\EOF && + test "$GIT_AUTHOR_NAME" = "New Author" && + test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com" + EOF + test_must_fail git commit --allow-empty -m "by a.u.thor" && + ( + GIT_AUTHOR_NAME="New Author" && + GIT_AUTHOR_EMAIL="newauthor@example.com" && + export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL && + git commit --allow-empty -m "by new.author via env" && + git show -s + ) && + git commit --author="New Author <newauthor@example.com>" \ + --allow-empty -m "by new.author via command line" && + git show -s +' + test_done diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh index 5b4b694f18..357375151d 100755 --- a/t/t7505-prepare-commit-msg-hook.sh +++ b/t/t7505-prepare-commit-msg-hook.sh @@ -167,5 +167,19 @@ test_expect_success 'with failing hook (--no-verify)' ' ' +test_expect_success 'with failing hook (merge)' ' + + git checkout -B other HEAD@{1} && + echo "more" >> file && + git add file && + rm -f "$HOOK" && + git commit -m other && + write_script "$HOOK" <<-EOF + exit 1 + EOF + git checkout - && + test_must_fail git merge other + +' test_done diff --git a/t/t7508-status.sh b/t/t7508-status.sh index fc57b135c5..c987b5ed65 100755 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@ -8,6 +8,7 @@ test_description='git status' . ./test-lib.sh test_expect_success 'status -h in broken repository' ' + git config --global advice.statusuoption false && mkdir broken && test_when_finished "rm -fr broken" && ( @@ -59,7 +60,14 @@ test_expect_success 'status (1)' ' test_i18ngrep "use \"git rm --cached <file>\.\.\.\" to unstage" output ' -cat >expect <<\EOF +strip_comments () { + tab=' ' + sed "s/^\# //; s/^\#$//; s/^#$tab/$tab/" <"$1" >"$1".tmp && + rm "$1" && mv "$1".tmp "$1" +} + +test_expect_success 'status --column' ' + cat >expect <<\EOF && # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) @@ -75,39 +83,98 @@ cat >expect <<\EOF # Untracked files: # (use "git add <file>..." to include in what will be committed) # -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked +# dir1/untracked dir2/untracked output +# dir2/modified expect untracked +# EOF + COLUMNS=50 git -c status.displayCommentPrefix=true status --column="column dense" >output && + test_i18ncmp expect output +' -test_expect_success 'status (2)' ' - git status >output && +test_expect_success 'status --column status.displayCommentPrefix=false' ' + strip_comments expect && + COLUMNS=49 git -c status.displayCommentPrefix=false status --column="column dense" >output && test_i18ncmp expect output ' cat >expect <<\EOF # On branch master # Changes to be committed: +# (use "git reset HEAD <file>..." to unstage) +# # new file: dir2/added # # Changes not staged for commit: +# (use "git add <file>..." to update what will be committed) +# (use "git checkout -- <file>..." to discard changes in working directory) +# # modified: dir1/modified # # Untracked files: +# (use "git add <file>..." to include in what will be committed) +# # dir1/untracked # dir2/modified # dir2/untracked # expect # output # untracked +# +EOF + +test_expect_success 'status with status.displayCommentPrefix=true' ' + git -c status.displayCommentPrefix=true status >output && + test_i18ncmp expect output +' + +test_expect_success 'status with status.displayCommentPrefix=false' ' + strip_comments expect && + git -c status.displayCommentPrefix=false status >output && + test_i18ncmp expect output +' + +test_expect_success 'setup fake editor' ' + cat >.git/editor <<-\EOF && + #! /bin/sh + cp "$1" output +EOF + chmod 755 .git/editor +' + +commit_template_commented () { + ( + EDITOR=.git/editor && + export EDITOR && + # Fails due to empty message + test_must_fail git commit + ) && + ! grep '^[^#]' output +} + +test_expect_success 'commit ignores status.displayCommentPrefix=false in COMMIT_EDITMSG' ' + commit_template_commented +' + +cat >expect <<\EOF +On branch master +Changes to be committed: + new file: dir2/added + +Changes not staged for commit: + modified: dir1/modified + +Untracked files: + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + EOF test_expect_success 'status (advice.statusHints false)' ' - test_when_finished "git config --unset advice.statusHints" && - git config advice.statusHints false && + test_config advice.statusHints false && git status >output && test_i18ncmp expect output @@ -161,33 +228,35 @@ test_expect_success 'status with gitignore' ' git status -s --ignored >output && test_cmp expect output && - cat >expect <<-\EOF && - # On branch master - # Changes to be committed: - # (use "git reset HEAD <file>..." to unstage) - # - # new file: dir2/added - # - # Changes not staged for commit: - # (use "git add <file>..." to update what will be committed) - # (use "git checkout -- <file>..." to discard changes in working directory) - # - # modified: dir1/modified - # - # Untracked files: - # (use "git add <file>..." to include in what will be committed) - # - # dir2/modified - # Ignored files: - # (use "git add -f <file>..." to include in what will be committed) - # - # .gitignore - # dir1/untracked - # dir2/untracked - # expect - # output - # untracked - EOF + cat >expect <<\EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + new file: dir2/added + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + dir2/modified + +Ignored files: + (use "git add -f <file>..." to include in what will be committed) + + .gitignore + dir1/untracked + dir2/untracked + expect + output + untracked + +EOF git status --ignored >output && test_i18ncmp expect output ' @@ -222,30 +291,31 @@ test_expect_success 'status with gitignore (nothing untracked)' ' git status -s --ignored >output && test_cmp expect output && - cat >expect <<-\EOF && - # On branch master - # Changes to be committed: - # (use "git reset HEAD <file>..." to unstage) - # - # new file: dir2/added - # - # Changes not staged for commit: - # (use "git add <file>..." to update what will be committed) - # (use "git checkout -- <file>..." to discard changes in working directory) - # - # modified: dir1/modified - # - # Ignored files: - # (use "git add -f <file>..." to include in what will be committed) - # - # .gitignore - # dir1/untracked - # dir2/modified - # dir2/untracked - # expect - # output - # untracked - EOF + cat >expect <<\EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + new file: dir2/added + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Ignored files: + (use "git add -f <file>..." to include in what will be committed) + + .gitignore + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +EOF git status --ignored >output && test_i18ncmp expect output ' @@ -271,55 +341,62 @@ test_expect_success 'status -s -b' ' ' +test_expect_success 'status -s -z -b' ' + tr "\\n" Q <expect >expect.q && + mv expect.q expect && + git status -s -z -b >output && + nul_to_q <output >output.q && + mv output.q output && + test_cmp expect output +' + test_expect_success 'setup dir3' ' mkdir dir3 && : >dir3/untracked1 && : >dir3/untracked2 ' -cat >expect <<EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# new file: dir2/added -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Untracked files not listed (use -u option to show untracked files) -EOF test_expect_success 'status -uno' ' + cat >expect <<EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + new file: dir2/added + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files not listed (use -u option to show untracked files) +EOF git status -uno >output && test_i18ncmp expect output ' test_expect_success 'status (status.showUntrackedFiles no)' ' - git config status.showuntrackedfiles no - test_when_finished "git config --unset status.showuntrackedfiles" && + test_config status.showuntrackedfiles no && git status >output && test_i18ncmp expect output ' -cat >expect <<EOF -# On branch master -# Changes to be committed: -# new file: dir2/added -# -# Changes not staged for commit: -# modified: dir1/modified -# -# Untracked files not listed -EOF -git config advice.statusHints false test_expect_success 'status -uno (advice.statusHints false)' ' + cat >expect <<EOF && +On branch master +Changes to be committed: + new file: dir2/added + +Changes not staged for commit: + modified: dir1/modified + +Untracked files not listed +EOF + test_config advice.statusHints false && git status -uno >output && test_i18ncmp expect output ' -git config --unset advice.statusHints cat >expect << EOF M dir1/modified @@ -336,38 +413,38 @@ test_expect_success 'status -s (status.showUntrackedFiles no)' ' test_cmp expect output ' -cat >expect <<EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# new file: dir2/added -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# dir1/untracked -# dir2/modified -# dir2/untracked -# dir3/ -# expect -# output -# untracked -EOF test_expect_success 'status -unormal' ' + cat >expect <<EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + new file: dir2/added + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + dir1/untracked + dir2/modified + dir2/untracked + dir3/ + expect + output + untracked + +EOF git status -unormal >output && test_i18ncmp expect output ' test_expect_success 'status (status.showUntrackedFiles normal)' ' - git config status.showuntrackedfiles normal - test_when_finished "git config --unset status.showuntrackedfiles" && + test_config status.showuntrackedfiles normal git status >output && test_i18ncmp expect output ' @@ -394,39 +471,39 @@ test_expect_success 'status -s (status.showUntrackedFiles normal)' ' test_cmp expect output ' -cat >expect <<EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# new file: dir2/added -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# dir1/untracked -# dir2/modified -# dir2/untracked -# dir3/untracked1 -# dir3/untracked2 -# expect -# output -# untracked -EOF test_expect_success 'status -uall' ' + cat >expect <<EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + new file: dir2/added + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + dir1/untracked + dir2/modified + dir2/untracked + dir3/untracked1 + dir3/untracked2 + expect + output + untracked + +EOF git status -uall >output && test_i18ncmp expect output ' test_expect_success 'status (status.showUntrackedFiles all)' ' - git config status.showuntrackedfiles all - test_when_finished "git config --unset status.showuntrackedfiles" && + test_config status.showuntrackedfiles all git status >output && test_i18ncmp expect output ' @@ -451,38 +528,37 @@ test_expect_success 'status -s -uall' ' test_cmp expect output ' test_expect_success 'status -s (status.showUntrackedFiles all)' ' - git config status.showuntrackedfiles all + test_config status.showuntrackedfiles all && git status -s >output && rm -rf dir3 && - git config --unset status.showuntrackedfiles && test_cmp expect output ' -cat >expect <<\EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# new file: ../dir2/added -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: modified -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# untracked -# ../dir2/modified -# ../dir2/untracked -# ../expect -# ../output -# ../untracked -EOF - test_expect_success 'status with relative paths' ' + cat >expect <<\EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + new file: ../dir2/added + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + untracked + ../dir2/modified + ../dir2/untracked + ../expect + ../output + ../untracked + +EOF (cd dir1 && git status) >output && test_i18ncmp expect output ' @@ -529,40 +605,38 @@ test_expect_success 'setup unique colors' ' ' -cat >expect <<\EOF -# On branch <GREEN>master<RESET> -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# <GREEN>new file: dir2/added<RESET> -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# <RED>modified: dir1/modified<RESET> -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# <BLUE>dir1/untracked<RESET> -# <BLUE>dir2/modified<RESET> -# <BLUE>dir2/untracked<RESET> -# <BLUE>expect<RESET> -# <BLUE>output<RESET> -# <BLUE>untracked<RESET> -EOF - test_expect_success 'status with color.ui' ' - git config color.ui always && - test_when_finished "git config --unset color.ui" && + cat >expect <<\EOF && +On branch <GREEN>master<RESET> +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + <GREEN>new file: dir2/added<RESET> + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + <RED>modified: dir1/modified<RESET> + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + <BLUE>dir1/untracked<RESET> + <BLUE>dir2/modified<RESET> + <BLUE>dir2/untracked<RESET> + <BLUE>expect<RESET> + <BLUE>output<RESET> + <BLUE>untracked<RESET> + +EOF + test_config color.ui always && git status | test_decode_color >output && test_i18ncmp expect output ' test_expect_success 'status with color.status' ' - git config color.status always && - test_when_finished "git config --unset color.status" && + test_config color.status always && git status | test_decode_color >output && test_i18ncmp expect output ' @@ -647,42 +721,46 @@ test_expect_success 'status --porcelain ignores color.status' ' git config --unset color.status git config --unset color.ui -test_expect_success 'status --porcelain ignores -b' ' +test_expect_success 'status --porcelain respects -b' ' git status --porcelain -b >output && + { + echo "## master" && + cat expect + } >tmp && + mv tmp expect && test_cmp expect output ' -cat >expect <<\EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# new file: dir2/added -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked -EOF test_expect_success 'status without relative paths' ' + cat >expect <<\EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + new file: dir2/added - git config status.relativePaths false && - test_when_finished "git config --unset status.relativePaths" && +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +EOF + test_config status.relativePaths false && (cd dir1 && git status) >output && test_i18ncmp expect output @@ -701,30 +779,30 @@ EOF test_expect_success 'status -s without relative paths' ' - git config status.relativePaths false && - test_when_finished "git config --unset status.relativePaths" && + test_config status.relativePaths false && (cd dir1 && git status -s) >output && test_cmp expect output ' -cat <<EOF >expect -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# modified: dir1/modified -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# dir1/untracked -# dir2/ -# expect -# output -# untracked -EOF test_expect_success 'dry-run of partial commit excluding new file in index' ' + cat >expect <<EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + modified: dir1/modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + dir1/untracked + dir2/ + expect + output + untracked + +EOF git commit --dry-run dir1/modified >output && test_i18ncmp expect output ' @@ -749,31 +827,32 @@ test_expect_success 'setup status submodule summary' ' git add sm ' -cat >expect <<EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# new file: dir2/added -# new file: sm -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked -EOF test_expect_success 'status submodule summary is disabled by default' ' + cat >expect <<EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + new file: dir2/added + new file: sm + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +EOF git status >output && test_i18ncmp expect output ' @@ -808,41 +887,52 @@ test_expect_success 'status -s --untracked-files=all does not show submodule' ' head=$(cd sm && git rev-parse --short=7 --verify HEAD) -cat >expect <<EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# new file: dir2/added -# new file: sm -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Submodule changes to be committed: -# -# * sm 0000000...$head (1): -# > Add foo -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked -EOF test_expect_success 'status submodule summary' ' + cat >expect <<EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + new file: dir2/added + new file: sm + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Submodule changes to be committed: + +* sm 0000000...$head (1): + > Add foo + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +EOF git config status.submodulesummary 10 && git status >output && test_i18ncmp expect output ' +test_expect_success 'status submodule summary with status.displayCommentPrefix=false' ' + strip_comments expect && + git -c status.displayCommentPrefix=false status >output && + test_i18ncmp expect output +' + +test_expect_success 'commit with submodule summary ignores status.displayCommentPrefix' ' + commit_template_commented +' + cat >expect <<EOF M dir1/modified A dir2/added @@ -859,26 +949,27 @@ test_expect_success 'status -s submodule summary' ' test_cmp expect output ' -cat >expect <<EOF -# On branch master -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked +test_expect_success 'status submodule summary (clean submodule): commit' ' + cat >expect <<EOF && +On branch master +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + no changes added to commit (use "git add" and/or "git commit -a") EOF -test_expect_success 'status submodule summary (clean submodule): commit' ' git commit -m "commit submodule" && git config status.submodulesummary 10 && test_must_fail git commit --dry-run >output && @@ -908,36 +999,37 @@ test_expect_success 'status -z implies porcelain' ' test_cmp expect output ' -cat >expect <<EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD^1 <file>..." to unstage) -# -# new file: dir2/added -# new file: sm -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Submodule changes to be committed: -# -# * sm 0000000...$head (1): -# > Add foo -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked -EOF test_expect_success 'commit --dry-run submodule summary (--amend)' ' + cat >expect <<EOF && +On branch master +Changes to be committed: + (use "git reset HEAD^1 <file>..." to unstage) + + new file: dir2/added + new file: sm + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Submodule changes to be committed: + +* sm 0000000...$head (1): + > Add foo + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +EOF git config status.submodulesummary 10 && git commit --dry-run --amend >output && test_i18ncmp expect output @@ -962,52 +1054,51 @@ test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' new_head=$(cd sm && git rev-parse --short=7 --verify HEAD) touch .gitmodules -cat > expect << EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# modified: sm -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Submodule changes to be committed: -# -# * sm $head...$new_head (1): -# > Add bar -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# .gitmodules -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked -EOF - test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' ' + cat > expect << EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + modified: sm + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Submodule changes to be committed: + +* sm $head...$new_head (1): + > Add bar + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + .gitmodules + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +EOF echo modified sm/untracked && git status --ignore-submodules=untracked >output && test_i18ncmp expect output ' test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' ' - git config diff.ignoreSubmodules dirty && + test_config diff.ignoreSubmodules dirty && git status >output && test_i18ncmp expect output && git config --add -f .gitmodules submodule.subname.ignore untracked && git config --add -f .gitmodules submodule.subname.path sm && git status >output && test_i18ncmp expect output && - git config -f .gitmodules --remove-section submodule.subname && - git config --unset diff.ignoreSubmodules + git config -f .gitmodules --remove-section submodule.subname ' test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' ' @@ -1027,15 +1118,14 @@ test_expect_success '--ignore-submodules=dirty suppresses submodules with untrac ' test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' ' - git config diff.ignoreSubmodules dirty && + test_config diff.ignoreSubmodules dirty && git status >output && ! test -s actual && git config --add -f .gitmodules submodule.subname.ignore dirty && git config --add -f .gitmodules submodule.subname.path sm && git status >output && test_i18ncmp expect output && - git config -f .gitmodules --remove-section submodule.subname && - git config --unset diff.ignoreSubmodules + git config -f .gitmodules --remove-section submodule.subname ' test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' ' @@ -1074,39 +1164,39 @@ test_expect_success '.git/config ignore=dirty suppresses submodules with modifie git config -f .gitmodules --remove-section submodule.subname ' -cat > expect << EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# modified: sm -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# (commit or discard the untracked or modified content in submodules) -# -# modified: dir1/modified -# modified: sm (modified content) -# -# Submodule changes to be committed: -# -# * sm $head...$new_head (1): -# > Add bar -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# .gitmodules -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked -EOF - test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" ' + cat > expect << EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + modified: sm + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + (commit or discard the untracked or modified content in submodules) + + modified: dir1/modified + modified: sm (modified content) + +Submodule changes to be committed: + +* sm $head...$new_head (1): + > Add bar + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + .gitmodules + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +EOF git status --ignore-submodules=untracked > output && test_i18ncmp expect output ' @@ -1132,43 +1222,43 @@ test_expect_success ".git/config ignore=untracked doesn't suppress submodules wi head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD) -cat > expect << EOF -# On branch master -# Changes to be committed: -# (use "git reset HEAD <file>..." to unstage) -# -# modified: sm -# -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# modified: sm (new commits) -# -# Submodule changes to be committed: -# -# * sm $head...$new_head (1): -# > Add bar -# -# Submodules changed but not updated: -# -# * sm $new_head...$head2 (1): -# > 2nd commit -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# .gitmodules -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked -EOF - test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" ' + cat > expect << EOF && +On branch master +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + modified: sm + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + modified: sm (new commits) + +Submodule changes to be committed: + +* sm $head...$new_head (1): + > Add bar + +Submodules changed but not updated: + +* sm $new_head...$head2 (1): + > 2nd commit + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + .gitmodules + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +EOF git status --ignore-submodules=untracked > output && test_i18ncmp expect output ' @@ -1216,32 +1306,81 @@ test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary ' cat > expect << EOF -# On branch master -# Changes not staged for commit: -# (use "git add <file>..." to update what will be committed) -# (use "git checkout -- <file>..." to discard changes in working directory) -# -# modified: dir1/modified -# -# Untracked files: -# (use "git add <file>..." to include in what will be committed) -# -# .gitmodules -# dir1/untracked -# dir2/modified -# dir2/untracked -# expect -# output -# untracked -no changes added to commit (use "git add" and/or "git commit -a") +; On branch master +; Changes to be committed: +; (use "git reset HEAD <file>..." to unstage) +; +; modified: sm +; +; Changes not staged for commit: +; (use "git add <file>..." to update what will be committed) +; (use "git checkout -- <file>..." to discard changes in working directory) +; +; modified: dir1/modified +; modified: sm (new commits) +; +; Submodule changes to be committed: +; +; * sm $head...$new_head (1): +; > Add bar +; +; Submodules changed but not updated: +; +; * sm $new_head...$head2 (1): +; > 2nd commit +; +; Untracked files: +; (use "git add <file>..." to include in what will be committed) +; +; .gitmodules +; dir1/untracked +; dir2/modified +; dir2/untracked +; expect +; output +; untracked +; EOF +test_expect_success "status (core.commentchar with submodule summary)" ' + test_config core.commentchar ";" && + git -c status.displayCommentPrefix=true status >output && + test_i18ncmp expect output +' + +test_expect_success "status (core.commentchar with two chars with submodule summary)" ' + test_config core.commentchar ";;" && + git -c status.displayCommentPrefix=true status >output && + test_i18ncmp expect output +' + test_expect_success "--ignore-submodules=all suppresses submodule summary" ' + cat > expect << EOF && +On branch master +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: dir1/modified + +Untracked files: + (use "git add <file>..." to include in what will be committed) + + .gitmodules + dir1/untracked + dir2/modified + dir2/untracked + expect + output + untracked + +no changes added to commit (use "git add" and/or "git commit -a") +EOF git status --ignore-submodules=all > output && test_i18ncmp expect output ' -test_expect_failure '.gitmodules ignore=all suppresses submodule summary' ' +test_expect_success '.gitmodules ignore=all suppresses submodule summary' ' git config --add -f .gitmodules submodule.subname.ignore all && git config --add -f .gitmodules submodule.subname.path sm && git status > output && @@ -1249,7 +1388,7 @@ test_expect_failure '.gitmodules ignore=all suppresses submodule summary' ' git config -f .gitmodules --remove-section submodule.subname ' -test_expect_failure '.git/config ignore=all suppresses submodule summary' ' +test_expect_success '.git/config ignore=all suppresses submodule summary' ' git config --add -f .gitmodules submodule.subname.ignore none && git config --add -f .gitmodules submodule.subname.path sm && git config --add submodule.subname.ignore all && @@ -1260,4 +1399,66 @@ test_expect_failure '.git/config ignore=all suppresses submodule summary' ' git config -f .gitmodules --remove-section submodule.subname ' +test_expect_success 'setup of test environment' ' + git config status.showUntrackedFiles no && + git status -s >expected_short && + git status --no-short >expected_noshort +' + +test_expect_success '"status.short=true" same as "-s"' ' + git -c status.short=true status >actual && + test_cmp expected_short actual +' + +test_expect_success '"status.short=true" weaker than "--no-short"' ' + git -c status.short=true status --no-short >actual && + test_cmp expected_noshort actual +' + +test_expect_success '"status.short=false" same as "--no-short"' ' + git -c status.short=false status >actual && + test_cmp expected_noshort actual +' + +test_expect_success '"status.short=false" weaker than "-s"' ' + git -c status.short=false status -s >actual && + test_cmp expected_short actual +' + +test_expect_success '"status.branch=true" same as "-b"' ' + git status -sb >expected_branch && + git -c status.branch=true status -s >actual && + test_cmp expected_branch actual +' + +test_expect_success '"status.branch=true" different from "--no-branch"' ' + git status -s --no-branch >expected_nobranch && + git -c status.branch=true status -s >actual && + test_must_fail test_cmp expected_nobranch actual +' + +test_expect_success '"status.branch=true" weaker than "--no-branch"' ' + git -c status.branch=true status -s --no-branch >actual && + test_cmp expected_nobranch actual +' + +test_expect_success '"status.branch=true" weaker than "--porcelain"' ' + git -c status.branch=true status --porcelain >actual && + test_cmp expected_nobranch actual +' + +test_expect_success '"status.branch=false" same as "--no-branch"' ' + git -c status.branch=false status -s >actual && + test_cmp expected_nobranch actual +' + +test_expect_success '"status.branch=false" weaker than "-b"' ' + git -c status.branch=false status -sb >actual && + test_cmp expected_branch actual +' + +test_expect_success 'Restore default test environment' ' + git config --unset status.showUntrackedFiles +' + test_done diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh new file mode 100755 index 0000000000..3cec57af1e --- /dev/null +++ b/t/t7512-status-help.sh @@ -0,0 +1,748 @@ +#!/bin/sh +# +# Copyright (c) 2012 Valentin Duperray, Lucien Kong, Franck Jonas, +# Thomas Nguy, Khoi Nguyen +# Grenoble INP Ensimag +# + +test_description='git status advice' + +. ./test-lib.sh + +. "$TEST_DIRECTORY"/lib-rebase.sh + +set_fake_editor + +test_expect_success 'prepare for conflicts' ' + git config --global advice.statusuoption false && + test_commit init main.txt init && + git branch conflicts && + test_commit on_master main.txt on_master && + git checkout conflicts && + test_commit on_conflicts main.txt on_conflicts +' + + +test_expect_success 'status when conflicts unresolved' ' + test_must_fail git merge master && + cat >expected <<\EOF && +On branch conflicts +You have unmerged paths. + (fix conflicts and run "git commit") + +Unmerged paths: + (use "git add <file>..." to mark resolution) + + both modified: main.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status when conflicts resolved before commit' ' + git reset --hard conflicts && + test_must_fail git merge master && + echo one >main.txt && + git add main.txt && + cat >expected <<\EOF && +On branch conflicts +All conflicts fixed but you are still merging. + (use "git commit" to conclude merge) + +Changes to be committed: + + modified: main.txt + +Untracked files not listed (use -u option to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'prepare for rebase conflicts' ' + git reset --hard master && + git checkout -b rebase_conflicts && + test_commit one_rebase main.txt one && + test_commit two_rebase main.txt two && + test_commit three_rebase main.txt three +' + + +test_expect_success 'status when rebase in progress before resolving conflicts' ' + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD^^) && + test_must_fail git rebase HEAD^ --onto HEAD^^ && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''. + (fix conflicts and then run "git rebase --continue") + (use "git rebase --skip" to skip this patch) + (use "git rebase --abort" to check out the original branch) + +Unmerged paths: + (use "git reset HEAD <file>..." to unstage) + (use "git add <file>..." to mark resolution) + + both modified: main.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status when rebase in progress before rebase --continue' ' + git reset --hard rebase_conflicts && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD^^) && + test_must_fail git rebase HEAD^ --onto HEAD^^ && + echo three >main.txt && + git add main.txt && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''. + (all conflicts fixed: run "git rebase --continue") + +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + modified: main.txt + +Untracked files not listed (use -u option to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'prepare for rebase_i_conflicts' ' + git reset --hard master && + git checkout -b rebase_i_conflicts && + test_commit one_unmerge main.txt one_unmerge && + git branch rebase_i_conflicts_second && + test_commit one_master main.txt one_master && + git checkout rebase_i_conflicts_second && + test_commit one_second main.txt one_second +' + + +test_expect_success 'status during rebase -i when conflicts unresolved' ' + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short rebase_i_conflicts) && + test_must_fail git rebase -i rebase_i_conflicts && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''. + (fix conflicts and then run "git rebase --continue") + (use "git rebase --skip" to skip this patch) + (use "git rebase --abort" to check out the original branch) + +Unmerged paths: + (use "git reset HEAD <file>..." to unstage) + (use "git add <file>..." to mark resolution) + + both modified: main.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status during rebase -i after resolving conflicts' ' + git reset --hard rebase_i_conflicts_second && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short rebase_i_conflicts) && + test_must_fail git rebase -i rebase_i_conflicts && + git add main.txt && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''. + (all conflicts fixed: run "git rebase --continue") + +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + modified: main.txt + +Untracked files not listed (use -u option to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status when rebasing -i in edit mode' ' + git reset --hard master && + git checkout -b rebase_i_edit && + test_commit one_rebase_i main.txt one && + test_commit two_rebase_i main.txt two && + test_commit three_rebase_i main.txt three && + FAKE_LINES="1 edit 2" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~2) && + git rebase -i HEAD~2 && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''. + (use "git commit --amend" to amend the current commit) + (use "git rebase --continue" once you are satisfied with your changes) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status when splitting a commit' ' + git reset --hard master && + git checkout -b split_commit && + test_commit one_split main.txt one && + test_commit two_split main.txt two && + test_commit three_split main.txt three && + test_commit four_split main.txt four && + FAKE_LINES="1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git reset HEAD^ && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''. + (Once your working directory is clean, run "git rebase --continue") + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: main.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status after editing the last commit with --amend during a rebase -i' ' + git reset --hard master && + git checkout -b amend_last && + test_commit one_amend main.txt one && + test_commit two_amend main.txt two && + test_commit three_amend main.txt three && + test_commit four_amend main.txt four && + FAKE_LINES="1 2 edit 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git commit --amend -m "foo" && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''. + (use "git commit --amend" to amend the current commit) + (use "git rebase --continue" once you are satisfied with your changes) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'prepare for several edits' ' + git reset --hard master && + git checkout -b several_edits && + test_commit one_edits main.txt one && + test_commit two_edits main.txt two && + test_commit three_edits main.txt three && + test_commit four_edits main.txt four +' + + +test_expect_success 'status: (continue first edit) second edit' ' + FAKE_LINES="edit 1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git rebase --continue && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. + (use "git commit --amend" to amend the current commit) + (use "git rebase --continue" once you are satisfied with your changes) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status: (continue first edit) second edit and split' ' + git reset --hard several_edits && + FAKE_LINES="edit 1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git rebase --continue && + git reset HEAD^ && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. + (Once your working directory is clean, run "git rebase --continue") + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: main.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status: (continue first edit) second edit and amend' ' + git reset --hard several_edits && + FAKE_LINES="edit 1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git rebase --continue && + git commit --amend -m "foo" && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. + (use "git commit --amend" to amend the current commit) + (use "git rebase --continue" once you are satisfied with your changes) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status: (amend first edit) second edit' ' + git reset --hard several_edits && + FAKE_LINES="edit 1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git commit --amend -m "a" && + git rebase --continue && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. + (use "git commit --amend" to amend the current commit) + (use "git rebase --continue" once you are satisfied with your changes) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status: (amend first edit) second edit and split' ' + git reset --hard several_edits && + FAKE_LINES="edit 1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git commit --amend -m "b" && + git rebase --continue && + git reset HEAD^ && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. + (Once your working directory is clean, run "git rebase --continue") + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: main.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status: (amend first edit) second edit and amend' ' + git reset --hard several_edits && + FAKE_LINES="edit 1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git commit --amend -m "c" && + git rebase --continue && + git commit --amend -m "d" && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. + (use "git commit --amend" to amend the current commit) + (use "git rebase --continue" once you are satisfied with your changes) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status: (split first edit) second edit' ' + git reset --hard several_edits && + FAKE_LINES="edit 1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git reset HEAD^ && + git add main.txt && + git commit -m "e" && + git rebase --continue && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. + (use "git commit --amend" to amend the current commit) + (use "git rebase --continue" once you are satisfied with your changes) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status: (split first edit) second edit and split' ' + git reset --hard several_edits && + FAKE_LINES="edit 1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git reset HEAD^ && + git add main.txt && + git commit --amend -m "f" && + git rebase --continue && + git reset HEAD^ && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. + (Once your working directory is clean, run "git rebase --continue") + +Changes not staged for commit: + (use "git add <file>..." to update what will be committed) + (use "git checkout -- <file>..." to discard changes in working directory) + + modified: main.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status: (split first edit) second edit and amend' ' + git reset --hard several_edits && + FAKE_LINES="edit 1 edit 2 3" && + export FAKE_LINES && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD~3) && + git rebase -i HEAD~3 && + git reset HEAD^ && + git add main.txt && + git commit --amend -m "g" && + git rebase --continue && + git commit --amend -m "h" && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''. + (use "git commit --amend" to amend the current commit) + (use "git rebase --continue" once you are satisfied with your changes) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'prepare am_session' ' + git reset --hard master && + git checkout -b am_session && + test_commit one_am one.txt "one" && + test_commit two_am two.txt "two" && + test_commit three_am three.txt "three" +' + + +test_expect_success 'status in an am session: file already exists' ' + git checkout -b am_already_exists && + test_when_finished "rm Maildir/* && git am --abort" && + git format-patch -1 -oMaildir && + test_must_fail git am Maildir/*.patch && + cat >expected <<\EOF && +On branch am_already_exists +You are in the middle of an am session. + (fix conflicts and then run "git am --continue") + (use "git am --skip" to skip this patch) + (use "git am --abort" to restore the original branch) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status in an am session: file does not exist' ' + git reset --hard am_session && + git checkout -b am_not_exists && + git rm three.txt && + git commit -m "delete three.txt" && + test_when_finished "rm Maildir/* && git am --abort" && + git format-patch -1 -oMaildir && + test_must_fail git am Maildir/*.patch && + cat >expected <<\EOF && +On branch am_not_exists +You are in the middle of an am session. + (fix conflicts and then run "git am --continue") + (use "git am --skip" to skip this patch) + (use "git am --abort" to restore the original branch) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status in an am session: empty patch' ' + git reset --hard am_session && + git checkout -b am_empty && + test_when_finished "rm Maildir/* && git am --abort" && + git format-patch -3 -oMaildir && + git rm one.txt two.txt three.txt && + git commit -m "delete all am_empty" && + echo error >Maildir/0002-two_am.patch && + test_must_fail git am Maildir/*.patch && + cat >expected <<\EOF && +On branch am_empty +You are in the middle of an am session. +The current patch is empty. + (use "git am --skip" to skip this patch) + (use "git am --abort" to restore the original branch) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status when bisecting' ' + git reset --hard master && + git checkout -b bisect && + test_commit one_bisect main.txt one && + test_commit two_bisect main.txt two && + test_commit three_bisect main.txt three && + test_when_finished "git bisect reset" && + git bisect start && + git bisect bad && + git bisect good one_bisect && + TGT=$(git rev-parse --short two_bisect) && + cat >expected <<EOF && +HEAD detached at $TGT +You are currently bisecting, started from branch '\''bisect'\''. + (use "git bisect reset" to get back to the original branch) + +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status when rebase conflicts with statushints disabled' ' + git reset --hard master && + git checkout -b statushints_disabled && + test_when_finished "git config --local advice.statushints true" && + git config --local advice.statushints false && + test_commit one_statushints main.txt one && + test_commit two_statushints main.txt two && + test_commit three_statushints main.txt three && + test_when_finished "git rebase --abort" && + ONTO=$(git rev-parse --short HEAD^^) && + test_must_fail git rebase HEAD^ --onto HEAD^^ && + cat >expected <<EOF && +rebase in progress; onto $ONTO +You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''. + +Unmerged paths: + both modified: main.txt + +no changes added to commit +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'prepare for cherry-pick conflicts' ' + git reset --hard master && + git checkout -b cherry_branch && + test_commit one_cherry main.txt one && + test_commit two_cherries main.txt two && + git checkout -b cherry_branch_second && + test_commit second_cherry main.txt second && + git checkout cherry_branch && + test_commit three_cherries main.txt three +' + + +test_expect_success 'status when cherry-picking before resolving conflicts' ' + test_when_finished "git cherry-pick --abort" && + test_must_fail git cherry-pick cherry_branch_second && + TO_CHERRY_PICK=$(git rev-parse --short CHERRY_PICK_HEAD) && + cat >expected <<EOF && +On branch cherry_branch +You are currently cherry-picking commit $TO_CHERRY_PICK. + (fix conflicts and run "git cherry-pick --continue") + (use "git cherry-pick --abort" to cancel the cherry-pick operation) + +Unmerged paths: + (use "git add <file>..." to mark resolution) + + both modified: main.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + + +test_expect_success 'status when cherry-picking after resolving conflicts' ' + git reset --hard cherry_branch && + test_when_finished "git cherry-pick --abort" && + test_must_fail git cherry-pick cherry_branch_second && + TO_CHERRY_PICK=$(git rev-parse --short CHERRY_PICK_HEAD) && + echo end >main.txt && + git add main.txt && + cat >expected <<EOF && +On branch cherry_branch +You are currently cherry-picking commit $TO_CHERRY_PICK. + (all conflicts fixed: run "git cherry-pick --continue") + (use "git cherry-pick --abort" to cancel the cherry-pick operation) + +Changes to be committed: + + modified: main.txt + +Untracked files not listed (use -u option to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + +test_expect_success 'status showing detached at and from a tag' ' + test_commit atag tagging && + git checkout atag && + cat >expected <<\EOF && +HEAD detached at atag +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual && + + git reset --hard HEAD^ && + cat >expected <<\EOF && +HEAD detached from atag +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + +test_expect_success 'status while reverting commit (conflicts)' ' + git checkout master && + echo before >to-revert.txt && + test_commit before to-revert.txt && + echo old >to-revert.txt && + test_commit old to-revert.txt && + echo new >to-revert.txt && + test_commit new to-revert.txt && + TO_REVERT=$(git rev-parse --short HEAD^) && + test_must_fail git revert $TO_REVERT && + cat >expected <<EOF && +On branch master +You are currently reverting commit $TO_REVERT. + (fix conflicts and run "git revert --continue") + (use "git revert --abort" to cancel the revert operation) + +Unmerged paths: + (use "git reset HEAD <file>..." to unstage) + (use "git add <file>..." to mark resolution) + + both modified: to-revert.txt + +no changes added to commit (use "git add" and/or "git commit -a") +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + +test_expect_success 'status while reverting commit (conflicts resolved)' ' + echo reverted >to-revert.txt && + git add to-revert.txt && + cat >expected <<EOF && +On branch master +You are currently reverting commit $TO_REVERT. + (all conflicts fixed: run "git revert --continue") + (use "git revert --abort" to cancel the revert operation) + +Changes to be committed: + (use "git reset HEAD <file>..." to unstage) + + modified: to-revert.txt + +Untracked files not listed (use -u option to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + +test_expect_success 'status after reverting commit' ' + git revert --continue && + cat >expected <<\EOF && +On branch master +nothing to commit (use -u to show untracked files) +EOF + git status --untracked-files=no >actual && + test_i18ncmp expected actual +' + +test_done diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 9e27bbf902..10aa028017 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -56,7 +56,8 @@ create_merge_msgs () { echo && git log --no-merges ^HEAD c2 c3 } >squash.1-5-9 && - echo >msg.nolog && + : >msg.nologff && + echo >msg.nolognoff && { echo "* tag 'c3':" && echo " commit 3" && @@ -157,7 +158,7 @@ test_expect_success 'merge -h with invalid index' ' >.git/index && test_expect_code 129 git merge -h 2>usage ) && - grep "[Uu]sage: git merge" broken/usage + test_i18ngrep "[Uu]sage: git merge" broken/usage ' test_expect_success 'reject non-strategy with a git-merge-foo name' ' @@ -244,8 +245,7 @@ test_expect_success 'merges with --ff-only' ' test_expect_success 'merges with merge.ff=only' ' git reset --hard c1 && test_tick && - test_when_finished "git config --unset merge.ff" && - git config merge.ff only && + test_config merge.ff "only" && test_must_fail git merge c2 && test_must_fail git merge c3 && test_must_fail git merge c2 c3 && @@ -316,7 +316,7 @@ test_expect_success 'merge c1 with c2 (squash)' ' test_debug 'git log --graph --decorate --oneline --all' -test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' ' +test_expect_success 'unsuccessful merge of c1 with c2 (squash, ff-only)' ' git reset --hard c1 && test_must_fail git merge --squash --ff-only c2 ' @@ -336,7 +336,7 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (no-commit in config)' ' git reset --hard c1 && - git config branch.master.mergeoptions "--no-commit" && + test_config branch.master.mergeoptions "--no-commit" && git merge c2 && verify_merge file result.1-5 && verify_head $c1 && @@ -346,12 +346,11 @@ test_expect_success 'merge c1 with c2 (no-commit in config)' ' test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (log in config)' ' - git config branch.master.mergeoptions "" && git reset --hard c1 && git merge --log c2 && git show -s --pretty=tformat:%s%n%b >expect && - git config branch.master.mergeoptions --log && + test_config branch.master.mergeoptions "--log" && git reset --hard c1 && git merge c2 && git show -s --pretty=tformat:%s%n%b >actual && @@ -360,17 +359,12 @@ test_expect_success 'merge c1 with c2 (log in config)' ' ' test_expect_success 'merge c1 with c2 (log in config gets overridden)' ' - test_when_finished "git config --remove-section branch.master" && - test_when_finished "git config --remove-section merge" && - test_might_fail git config --remove-section branch.master && - test_might_fail git config --remove-section merge && - git reset --hard c1 && git merge c2 && git show -s --pretty=tformat:%s%n%b >expect && - git config branch.master.mergeoptions "--no-log" && - git config merge.log true && + test_config branch.master.mergeoptions "--no-log" && + test_config merge.log "true" && git reset --hard c1 && git merge c2 && git show -s --pretty=tformat:%s%n%b >actual && @@ -380,7 +374,7 @@ test_expect_success 'merge c1 with c2 (log in config gets overridden)' ' test_expect_success 'merge c1 with c2 (squash in config)' ' git reset --hard c1 && - git config branch.master.mergeoptions "--squash" && + test_config branch.master.mergeoptions "--squash" && git merge c2 && verify_merge file result.1-5 && verify_head $c1 && @@ -392,7 +386,7 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'override config option -n with --summary' ' git reset --hard c1 && - git config branch.master.mergeoptions "-n" && + test_config branch.master.mergeoptions "-n" && test_tick && git merge --summary c2 >diffstat.txt && verify_merge file result.1-5 msg.1-5 && @@ -406,7 +400,7 @@ test_expect_success 'override config option -n with --summary' ' test_expect_success 'override config option -n with --stat' ' git reset --hard c1 && - git config branch.master.mergeoptions "-n" && + test_config branch.master.mergeoptions "-n" && test_tick && git merge --stat c2 >diffstat.txt && verify_merge file result.1-5 msg.1-5 && @@ -422,7 +416,7 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'override config option --stat' ' git reset --hard c1 && - git config branch.master.mergeoptions "--stat" && + test_config branch.master.mergeoptions "--stat" && test_tick && git merge -n c2 >diffstat.txt && verify_merge file result.1-5 msg.1-5 && @@ -438,7 +432,7 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (override --no-commit)' ' git reset --hard c1 && - git config branch.master.mergeoptions "--no-commit" && + test_config branch.master.mergeoptions "--no-commit" && test_tick && git merge --commit c2 && verify_merge file result.1-5 msg.1-5 && @@ -449,7 +443,7 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c2 (override --squash)' ' git reset --hard c1 && - git config branch.master.mergeoptions "--squash" && + test_config branch.master.mergeoptions "--squash" && test_tick && git merge --no-squash c2 && verify_merge file result.1-5 msg.1-5 && @@ -460,7 +454,6 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c0 with c1 (no-ff)' ' git reset --hard c0 && - git config branch.master.mergeoptions "" && test_tick && git merge --no-ff c1 && verify_merge file result.1 && @@ -471,10 +464,9 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c0 with c1 (merge.ff=false)' ' git reset --hard c0 && - git config merge.ff false && + test_config merge.ff "false" && test_tick && git merge c1 && - git config --remove-section merge && verify_merge file result.1 && verify_parents $c0 $c1 ' @@ -482,22 +474,19 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'combine branch.master.mergeoptions with merge.ff' ' git reset --hard c0 && - git config branch.master.mergeoptions --ff && - git config merge.ff false && + test_config branch.master.mergeoptions "--ff" && + test_config merge.ff "false" && test_tick && git merge c1 && - git config --remove-section "branch.master" && - git config --remove-section "merge" && verify_merge file result.1 && verify_parents "$c0" ' test_expect_success 'tolerate unknown values for merge.ff' ' git reset --hard c0 && - git config merge.ff something-new && + test_config merge.ff "something-new" && test_tick && git merge c1 2>message && - git config --remove-section "merge" && verify_head "$c1" && test_cmp empty message ' @@ -508,14 +497,20 @@ test_expect_success 'combining --squash and --no-ff is refused' ' test_must_fail git merge --no-ff --squash c1 ' -test_expect_success 'combining --ff-only and --no-ff is refused' ' - test_must_fail git merge --ff-only --no-ff c1 && - test_must_fail git merge --no-ff --ff-only c1 +test_expect_success 'option --ff-only overwrites --no-ff' ' + git merge --no-ff --ff-only c1 && + test_must_fail git merge --no-ff --ff-only c2 +' + +test_expect_success 'option --no-ff overrides merge.ff=only config' ' + git reset --hard c0 && + test_config merge.ff only && + git merge --no-ff c1 ' test_expect_success 'merge c0 with c1 (ff overrides no-ff)' ' git reset --hard c0 && - git config branch.master.mergeoptions "--no-ff" && + test_config branch.master.mergeoptions "--no-ff" && git merge --ff c1 && verify_merge file result.1 && verify_head $c1 @@ -525,14 +520,20 @@ test_expect_success 'merge log message' ' git reset --hard c0 && git merge --no-log c2 && git show -s --pretty=format:%b HEAD >msg.act && - test_cmp msg.nolog msg.act && + test_cmp msg.nologff msg.act && + + git reset --hard c0 && + test_config branch.master.mergeoptions "--no-ff" && + git merge --no-log c2 && + git show -s --pretty=format:%b HEAD >msg.act && + test_cmp msg.nolognoff msg.act && git merge --log c3 && git show -s --pretty=format:%b HEAD >msg.act && test_cmp msg.log msg.act && git reset --hard HEAD^ && - git config merge.log yes && + test_config merge.log "yes" && git merge c3 && git show -s --pretty=format:%b HEAD >msg.act && test_cmp msg.log msg.act @@ -542,7 +543,6 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c0, c2, c0, and c1' ' git reset --hard c1 && - git config branch.master.mergeoptions "" && test_tick && git merge c0 c2 c0 c1 && verify_merge file result.1-5 && @@ -553,7 +553,6 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c0, c2, c0, and c1' ' git reset --hard c1 && - git config branch.master.mergeoptions "" && test_tick && git merge c0 c2 c0 c1 && verify_merge file result.1-5 && @@ -564,7 +563,6 @@ test_debug 'git log --graph --decorate --oneline --all' test_expect_success 'merge c1 with c1 and c2' ' git reset --hard c1 && - git config branch.master.mergeoptions "" && test_tick && git merge c1 c2 && verify_merge file result.1-5 && diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh index b44b293950..830a4c3e9d 100755 --- a/t/t7601-merge-pull-config.sh +++ b/t/t7601-merge-pull-config.sh @@ -109,7 +109,7 @@ test_expect_success 'setup conflicted merge' ' ' # First do the merge with resolve and recursive then verify that -# recusive is choosen. +# recursive is chosen. test_expect_success 'merge picks up the best result' ' git config --unset-all pull.twohead && diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh index 5783ebf3ab..955f09f8e8 100755 --- a/t/t7602-merge-octopus-many.sh +++ b/t/t7602-merge-octopus-many.sh @@ -54,9 +54,9 @@ Trying simple merge with c2 Trying simple merge with c3 Trying simple merge with c4 Merge made by the 'octopus' strategy. - c2.c | 1 + - c3.c | 1 + - c4.c | 1 + + c2.c | 1 + + c3.c | 1 + + c4.c | 1 + 3 files changed, 3 insertions(+) create mode 100644 c2.c create mode 100644 c3.c @@ -66,29 +66,27 @@ EOF test_expect_success 'merge output uses pretty names' ' git reset --hard c1 && git merge c2 c3 c4 >actual && - test_cmp actual expected + test_i18ncmp expected actual ' cat >expected <<\EOF -Already up-to-date with c4 -Trying simple merge with c5 -Merge made by the 'octopus' strategy. - c5.c | 1 + +Merge made by the 'recursive' strategy. + c5.c | 1 + 1 file changed, 1 insertion(+) create mode 100644 c5.c EOF -test_expect_success 'merge up-to-date output uses pretty names' ' - git merge c4 c5 >actual && - test_cmp actual expected +test_expect_success 'merge reduces irrelevant remote heads' ' + GIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual && + test_i18ncmp expected actual ' cat >expected <<\EOF Fast-forwarding to: c1 Trying simple merge with c2 Merge made by the 'octopus' strategy. - c1.c | 1 + - c2.c | 1 + + c1.c | 1 + + c2.c | 1 + 2 files changed, 2 insertions(+) create mode 100644 c1.c create mode 100644 c2.c @@ -97,7 +95,7 @@ EOF test_expect_success 'merge fast-forward output uses pretty names' ' git reset --hard c0 && git merge c1 c2 >actual && - test_cmp actual expected + test_i18ncmp expected actual ' test_done diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh index 7e17eb490d..98948955ae 100755 --- a/t/t7603-merge-reduce-heads.sh +++ b/t/t7603-merge-reduce-heads.sh @@ -57,7 +57,36 @@ test_expect_success 'merge c1 with c2, c3, c4, c5' ' test -f c2.c && test -f c3.c && test -f c4.c && - test -f c5.c + test -f c5.c && + git show --format=%s -s >actual && + ! grep c1 actual && + grep c2 actual && + grep c3 actual && + ! grep c4 actual && + grep c5 actual +' + +test_expect_success 'pull c2, c3, c4, c5 into c1' ' + git reset --hard c1 && + git pull . c2 c3 c4 c5 && + test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" && + test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" && + test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" && + test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" && + test "$(git rev-parse c5)" = "$(git rev-parse HEAD^4)" && + git diff --exit-code && + test -f c0.c && + test -f c1.c && + test -f c2.c && + test -f c3.c && + test -f c4.c && + test -f c5.c && + git show --format=%s -s >actual && + ! grep c1 actual && + grep c2 actual && + grep c3 actual && + ! grep c4 actual && + grep c5 actual ' test_expect_success 'setup' ' @@ -113,4 +142,23 @@ test_expect_success 'verify merge result' ' test $(git rev-parse HEAD^1) = $(git rev-parse E2) && test $(git rev-parse HEAD^2) = $(git rev-parse I2) ' + +test_expect_success 'fast-forward to redundant refs' ' + git reset --hard c0 && + git merge c4 c5 +' + +test_expect_success 'verify merge result' ' + test $(git rev-parse HEAD) = $(git rev-parse c5) +' + +test_expect_success 'merge up-to-date redundant refs' ' + git reset --hard c5 && + git merge c0 c4 +' + +test_expect_success 'verify merge result' ' + test $(git rev-parse HEAD) = $(git rev-parse c5) +' + test_done diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh index aa74184c31..758a623cdb 100755 --- a/t/t7607-merge-overwrite.sh +++ b/t/t7607-merge-overwrite.sh @@ -92,6 +92,15 @@ test_expect_success 'will not overwrite removed file with staged changes' ' test_cmp important c1.c ' +test_expect_failure 'will not overwrite unstaged changes in renamed file' ' + git reset --hard c1 && + git mv c1.c other.c && + git commit -m rename && + cp important other.c && + git merge c1a && + test_cmp important other.c +' + test_expect_success 'will not overwrite untracked subtree' ' git reset --hard c0 && rm -rf sub && @@ -132,11 +141,10 @@ test_expect_success SYMLINKS 'will not overwrite untracked symlink in leading pa test_path_is_missing .git/MERGE_HEAD ' -test_expect_success SYMLINKS 'will not be confused by symlink in leading path' ' +test_expect_success 'will not be confused by symlink in leading path' ' git reset --hard c0 && rm -rf sub && - ln -s sub2 sub && - git add sub && + test_ln_s_add sub2 sub && git commit -m ln && git checkout sub ' diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index f5e16fc7db..05d9db090d 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -55,6 +55,16 @@ test_expect_success 'setup' ' git rm file12 && git commit -m "branch1 changes" && + git checkout -b stash1 master && + echo stash1 change file11 >file11 && + git add file11 && + git commit -m "stash1 changes" && + + git checkout -b stash2 master && + echo stash2 change file11 >file11 && + git add file11 && + git commit -m "stash2 changes" && + git checkout master && git submodule update -N && echo master updated >file1 && @@ -193,13 +203,41 @@ test_expect_success 'mergetool skips resolved paths when rerere is active' ' git reset --hard ' +test_expect_success 'conflicted stash sets up rerere' ' + git config rerere.enabled true && + git checkout stash1 && + echo "Conflicting stash content" >file11 && + git stash && + + git checkout --detach stash2 && + test_must_fail git stash apply && + + test -n "$(git ls-files -u)" && + conflicts="$(git rerere remaining)" && + test "$conflicts" = "file11" && + output="$(git mergetool --no-prompt)" && + test "$output" != "No files need merging" && + + git commit -am "save the stash resolution" && + + git reset --hard stash2 && + test_must_fail git stash apply && + + test -n "$(git ls-files -u)" && + conflicts="$(git rerere remaining)" && + test -z "$conflicts" && + output="$(git mergetool --no-prompt)" && + test "$output" = "No files need merging" +' + test_expect_success 'mergetool takes partial path' ' + git reset --hard git config rerere.enabled false && git checkout -b test12 branch1 && git submodule update -N && test_must_fail git merge master && - #shouldnt need these lines + #should not need these lines #( yes "d" | git mergetool file11 >/dev/null 2>&1 ) && #( yes "d" | git mergetool file12 >/dev/null 2>&1 ) && #( yes "l" | git mergetool submod >/dev/null 2>&1 ) && @@ -215,7 +253,7 @@ test_expect_success 'deleted vs modified submodule' ' git checkout -b test6 branch1 && git submodule update -N && mv submod submod-movedaside && - git rm submod && + git rm --cached submod && git commit -m "Submodule deleted from branch" && git checkout -b test6.a test6 && test_must_fail git merge master && @@ -284,7 +322,7 @@ test_expect_success 'file vs modified submodule' ' git checkout -b test7 branch1 && git submodule update -N && mv submod submod-movedaside && - git rm submod && + git rm --cached submod && echo not a submodule >submod && git add submod && git commit -m "Submodule path becomes file" && @@ -415,7 +453,7 @@ test_expect_success 'submodule in subdirectory' ' test_expect_success 'directory vs modified submodule' ' git checkout -b test11 branch1 && mv submod submod-movedaside && - git rm submod && + git rm --cached submod && mkdir submod && echo not a submodule >submod/file16 && git add submod/file16 && @@ -471,4 +509,17 @@ test_expect_success 'file with no base' ' git reset --hard master >/dev/null 2>&1 ' +test_expect_success 'custom commands override built-ins' ' + git checkout -b test14 branch1 && + git config mergetool.defaults.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" && + git config mergetool.defaults.trustExitCode true && + test_must_fail git merge master && + git mergetool --no-prompt --tool defaults -- both && + echo master both added >expected && + test_cmp both expected && + git config --unset mergetool.defaults.cmd && + git config --unset mergetool.defaults.trustExitCode && + git reset --hard master >/dev/null 2>&1 +' + test_done diff --git a/t/t7612-merge-verify-signatures.sh b/t/t7612-merge-verify-signatures.sh new file mode 100755 index 0000000000..21a0bf8fb8 --- /dev/null +++ b/t/t7612-merge-verify-signatures.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +test_description='merge signature verification tests' +. ./test-lib.sh +. "$TEST_DIRECTORY/lib-gpg.sh" + +test_expect_success GPG 'create signed commits' ' + echo 1 >file && git add file && + test_tick && git commit -m initial && + git tag initial && + + git checkout -b side-signed && + echo 3 >elif && git add elif && + test_tick && git commit -S -m "signed on side" && + git checkout initial && + + git checkout -b side-unsigned && + echo 3 >foo && git add foo && + test_tick && git commit -m "unsigned on side" && + git checkout initial && + + git checkout -b side-bad && + echo 3 >bar && git add bar && + test_tick && git commit -S -m "bad on side" && + git cat-file commit side-bad >raw && + sed -e "s/bad/forged bad/" raw >forged && + git hash-object -w -t commit forged >forged.commit && + git checkout initial && + + git checkout -b side-untrusted && + echo 3 >baz && git add baz && + test_tick && git commit -SB7227189 -m "untrusted on side" + + git checkout master +' + +test_expect_success GPG 'merge unsigned commit with verification' ' + test_must_fail git merge --ff-only --verify-signatures side-unsigned 2>mergeerror && + test_i18ngrep "does not have a GPG signature" mergeerror +' + +test_expect_success GPG 'merge commit with bad signature with verification' ' + test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2>mergeerror && + test_i18ngrep "has a bad GPG signature" mergeerror +' + +test_expect_success GPG 'merge commit with untrusted signature with verification' ' + test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror && + test_i18ngrep "has an untrusted GPG signature" mergeerror +' + +test_expect_success GPG 'merge signed commit with verification' ' + git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput && + test_i18ngrep "has a good GPG signature" mergeoutput +' + +test_expect_success GPG 'merge commit with bad signature without verification' ' + git merge $(cat forged.commit) +' + +test_done diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh index 200ab61278..b8d4cdea8c 100755 --- a/t/t7701-repack-unpack-unreachable.sh +++ b/t/t7701-repack-unpack-unreachable.sh @@ -95,4 +95,18 @@ test_expect_success 'unpacked objects receive timestamp of pack file' ' compare_mtimes < mtimes ' +test_expect_success 'do not bother loosening old objects' ' + obj1=$(echo one | git hash-object -w --stdin) && + obj2=$(echo two | git hash-object -w --stdin) && + pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) && + pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) && + git prune-packed && + git cat-file -p $obj1 && + git cat-file -p $obj2 && + test-chmtime =-86400 .git/objects/pack/pack-$pack2.pack && + git repack -A -d --unpack-unreachable=1.hour.ago && + git cat-file -p $obj1 && + test_must_fail git cat-file -p $obj2 +' + test_done diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index 4fb4c9384a..2418528487 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2009, 2010 David Aguilar +# Copyright (c) 2009, 2010, 2012, 2013 David Aguilar # test_description='git-difftool @@ -10,47 +10,19 @@ Testing basic diff tool invocation . ./test-lib.sh -remove_config_vars() +difftool_test_setup () { - # Unset all config variables used by git-difftool - git config --unset diff.tool - git config --unset diff.guitool - git config --unset difftool.test-tool.cmd - git config --unset difftool.prompt - git config --unset merge.tool - git config --unset mergetool.test-tool.cmd - git config --unset mergetool.prompt - return 0 + test_config diff.tool test-tool && + test_config difftool.test-tool.cmd 'cat "$LOCAL"' && + test_config difftool.bogus-tool.cmd false } -restore_test_defaults() -{ - # Restores the test defaults used by several tests - remove_config_vars - unset GIT_DIFF_TOOL - unset GIT_DIFFTOOL_PROMPT - unset GIT_DIFFTOOL_NO_PROMPT - git config diff.tool test-tool && - git config difftool.test-tool.cmd 'cat $LOCAL' - git config difftool.bogus-tool.cmd false -} - -prompt_given() +prompt_given () { prompt="$1" test "$prompt" = "Launch 'test-tool' [Y/n]: branch" } -stdin_contains() -{ - grep >/dev/null "$1" -} - -stdin_doesnot_contain() -{ - ! stdin_contains "$1" -} - # Create a file on master and change it on branch test_expect_success PERL 'setup' ' echo master >file && @@ -65,214 +37,237 @@ test_expect_success PERL 'setup' ' # Configure a custom difftool.<tool>.cmd and use it test_expect_success PERL 'custom commands' ' - restore_test_defaults && - git config difftool.test-tool.cmd "cat \$REMOTE" && - - diff=$(git difftool --no-prompt branch) && - test "$diff" = "master" && + difftool_test_setup && + test_config difftool.test-tool.cmd "cat \"\$REMOTE\"" && + echo master >expect && + git difftool --no-prompt branch >actual && + test_cmp expect actual && + + test_config difftool.test-tool.cmd "cat \"\$LOCAL\"" && + echo branch >expect && + git difftool --no-prompt branch >actual && + test_cmp expect actual +' - restore_test_defaults && - diff=$(git difftool --no-prompt branch) && - test "$diff" = "branch" +test_expect_success PERL 'custom tool commands override built-ins' ' + test_config difftool.vimdiff.cmd "cat \"\$REMOTE\"" && + echo master >expect && + git difftool --tool vimdiff --no-prompt branch >actual && + test_cmp expect actual ' -# Ensures that git-difftool ignores bogus --tool values test_expect_success PERL 'difftool ignores bad --tool values' ' - diff=$(git difftool --no-prompt --tool=bad-tool branch) - test "$?" = 1 && - test "$diff" = "" + : >expect && + test_expect_code 1 \ + git difftool --no-prompt --tool=bad-tool branch >actual && + test_cmp expect actual ' -test_expect_success PERL 'difftool honors --gui' ' - git config merge.tool bogus-tool && - git config diff.tool bogus-tool && - git config diff.guitool test-tool && +test_expect_success PERL 'difftool forwards arguments to diff' ' + difftool_test_setup && + >for-diff && + git add for-diff && + echo changes>for-diff && + git add for-diff && + : >expect && + git difftool --cached --no-prompt -- for-diff >actual && + test_cmp expect actual && + git reset -- for-diff && + rm for-diff +' - diff=$(git difftool --no-prompt --gui branch) && - test "$diff" = "branch" && +test_expect_success PERL 'difftool honors --gui' ' + difftool_test_setup && + test_config merge.tool bogus-tool && + test_config diff.tool bogus-tool && + test_config diff.guitool test-tool && + + echo branch >expect && + git difftool --no-prompt --gui branch >actual && + test_cmp expect actual +' - restore_test_defaults +test_expect_success PERL 'difftool --gui last setting wins' ' + difftool_test_setup && + : >expect && + git difftool --no-prompt --gui --no-gui >actual && + test_cmp expect actual && + + test_config merge.tool bogus-tool && + test_config diff.tool bogus-tool && + test_config diff.guitool test-tool && + echo branch >expect && + git difftool --no-prompt --no-gui --gui branch >actual && + test_cmp expect actual ' test_expect_success PERL 'difftool --gui works without configured diff.guitool' ' - git config diff.tool test-tool && - - diff=$(git difftool --no-prompt --gui branch) && - test "$diff" = "branch" && - - restore_test_defaults + difftool_test_setup && + echo branch >expect && + git difftool --no-prompt --gui branch >actual && + test_cmp expect actual ' # Specify the diff tool using $GIT_DIFF_TOOL test_expect_success PERL 'GIT_DIFF_TOOL variable' ' - test_might_fail git config --unset diff.tool && - GIT_DIFF_TOOL=test-tool && - export GIT_DIFF_TOOL && - - diff=$(git difftool --no-prompt branch) && - test "$diff" = "branch" && - - restore_test_defaults + difftool_test_setup && + git config --unset diff.tool && + echo branch >expect && + GIT_DIFF_TOOL=test-tool git difftool --no-prompt branch >actual && + test_cmp expect actual ' # Test the $GIT_*_TOOL variables and ensure # that $GIT_DIFF_TOOL always wins unless --tool is specified test_expect_success PERL 'GIT_DIFF_TOOL overrides' ' - git config diff.tool bogus-tool && - git config merge.tool bogus-tool && - - GIT_DIFF_TOOL=test-tool && - export GIT_DIFF_TOOL && - - diff=$(git difftool --no-prompt branch) && - test "$diff" = "branch" && - - GIT_DIFF_TOOL=bogus-tool && - export GIT_DIFF_TOOL && - - diff=$(git difftool --no-prompt --tool=test-tool branch) && - test "$diff" = "branch" && - - restore_test_defaults + difftool_test_setup && + test_config diff.tool bogus-tool && + test_config merge.tool bogus-tool && + + echo branch >expect && + GIT_DIFF_TOOL=test-tool git difftool --no-prompt branch >actual && + test_cmp expect actual && + + test_config diff.tool bogus-tool && + test_config merge.tool bogus-tool && + GIT_DIFF_TOOL=bogus-tool \ + git difftool --no-prompt --tool=test-tool branch >actual && + test_cmp expect actual ' # Test that we don't have to pass --no-prompt to difftool # when $GIT_DIFFTOOL_NO_PROMPT is true test_expect_success PERL 'GIT_DIFFTOOL_NO_PROMPT variable' ' - GIT_DIFFTOOL_NO_PROMPT=true && - export GIT_DIFFTOOL_NO_PROMPT && - - diff=$(git difftool branch) && - test "$diff" = "branch" && - - restore_test_defaults + difftool_test_setup && + echo branch >expect && + GIT_DIFFTOOL_NO_PROMPT=true git difftool branch >actual && + test_cmp expect actual ' # git-difftool supports the difftool.prompt variable. # Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false test_expect_success PERL 'GIT_DIFFTOOL_PROMPT variable' ' - git config difftool.prompt false && - GIT_DIFFTOOL_PROMPT=true && - export GIT_DIFFTOOL_PROMPT && - - prompt=$(echo | git difftool branch | tail -1) && - prompt_given "$prompt" && - - restore_test_defaults + difftool_test_setup && + test_config difftool.prompt false && + echo >input && + GIT_DIFFTOOL_PROMPT=true git difftool branch <input >output && + prompt=$(tail -1 <output) && + prompt_given "$prompt" ' # Test that we don't have to pass --no-prompt when difftool.prompt is false test_expect_success PERL 'difftool.prompt config variable is false' ' - git config difftool.prompt false && - - diff=$(git difftool branch) && - test "$diff" = "branch" && - - restore_test_defaults + difftool_test_setup && + test_config difftool.prompt false && + echo branch >expect && + git difftool branch >actual && + test_cmp expect actual ' # Test that we don't have to pass --no-prompt when mergetool.prompt is false test_expect_success PERL 'difftool merge.prompt = false' ' + difftool_test_setup && test_might_fail git config --unset difftool.prompt && - git config mergetool.prompt false && - - diff=$(git difftool branch) && - test "$diff" = "branch" && - - restore_test_defaults + test_config mergetool.prompt false && + echo branch >expect && + git difftool branch >actual && + test_cmp expect actual ' # Test that the -y flag can override difftool.prompt = true test_expect_success PERL 'difftool.prompt can overridden with -y' ' - git config difftool.prompt true && - - diff=$(git difftool -y branch) && - test "$diff" = "branch" && - - restore_test_defaults + difftool_test_setup && + test_config difftool.prompt true && + echo branch >expect && + git difftool -y branch >actual && + test_cmp expect actual ' # Test that the --prompt flag can override difftool.prompt = false test_expect_success PERL 'difftool.prompt can overridden with --prompt' ' - git config difftool.prompt false && - - prompt=$(echo | git difftool --prompt branch | tail -1) && - prompt_given "$prompt" && - - restore_test_defaults + difftool_test_setup && + test_config difftool.prompt false && + echo >input && + git difftool --prompt branch <input >output && + prompt=$(tail -1 <output) && + prompt_given "$prompt" ' # Test that the last flag passed on the command-line wins test_expect_success PERL 'difftool last flag wins' ' - diff=$(git difftool --prompt --no-prompt branch) && - test "$diff" = "branch" && - - restore_test_defaults && - - prompt=$(echo | git difftool --no-prompt --prompt branch | tail -1) && - prompt_given "$prompt" && - - restore_test_defaults + difftool_test_setup && + echo branch >expect && + git difftool --prompt --no-prompt branch >actual && + test_cmp expect actual && + echo >input && + git difftool --no-prompt --prompt branch <input >output && + prompt=$(tail -1 <output) && + prompt_given "$prompt" ' # git-difftool falls back to git-mergetool config variables # so test that behavior here test_expect_success PERL 'difftool + mergetool config variables' ' - remove_config_vars && - git config merge.tool test-tool && - git config mergetool.test-tool.cmd "cat \$LOCAL" && - - diff=$(git difftool --no-prompt branch) && - test "$diff" = "branch" && + test_config merge.tool test-tool && + test_config mergetool.test-tool.cmd "cat \$LOCAL" && + echo branch >expect && + git difftool --no-prompt branch >actual && + test_cmp expect actual && # set merge.tool to something bogus, diff.tool to test-tool - git config merge.tool bogus-tool && - git config diff.tool test-tool && - - diff=$(git difftool --no-prompt branch) && - test "$diff" = "branch" && - - restore_test_defaults + test_config merge.tool bogus-tool && + test_config diff.tool test-tool && + git difftool --no-prompt branch >actual && + test_cmp expect actual ' test_expect_success PERL 'difftool.<tool>.path' ' - git config difftool.tkdiff.path echo && - diff=$(git difftool --tool=tkdiff --no-prompt branch) && - git config --unset difftool.tkdiff.path && - lines=$(echo "$diff" | grep file | wc -l) && - test "$lines" -eq 1 && - - restore_test_defaults + test_config difftool.tkdiff.path echo && + git difftool --tool=tkdiff --no-prompt branch >output && + lines=$(grep file output | wc -l) && + test "$lines" -eq 1 ' test_expect_success PERL 'difftool --extcmd=cat' ' - diff=$(git difftool --no-prompt --extcmd=cat branch) && - test "$diff" = branch"$LF"master + echo branch >expect && + echo master >>expect && + git difftool --no-prompt --extcmd=cat branch >actual && + test_cmp expect actual ' test_expect_success PERL 'difftool --extcmd cat' ' - diff=$(git difftool --no-prompt --extcmd cat branch) && - test "$diff" = branch"$LF"master + echo branch >expect && + echo master >>expect && + git difftool --no-prompt --extcmd=cat branch >actual && + test_cmp expect actual ' test_expect_success PERL 'difftool -x cat' ' - diff=$(git difftool --no-prompt -x cat branch) && - test "$diff" = branch"$LF"master + echo branch >expect && + echo master >>expect && + git difftool --no-prompt -x cat branch >actual && + test_cmp expect actual ' test_expect_success PERL 'difftool --extcmd echo arg1' ' - diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"echo\ \$1\" branch) && - test "$diff" = file + echo file >expect && + git difftool --no-prompt \ + --extcmd sh\ -c\ \"echo\ \$1\" branch >actual && + test_cmp expect actual ' test_expect_success PERL 'difftool --extcmd cat arg1' ' - diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$1\" branch) && - test "$diff" = master + echo master >expect && + git difftool --no-prompt \ + --extcmd sh\ -c\ \"cat\ \$1\" branch >actual && + test_cmp expect actual ' test_expect_success PERL 'difftool --extcmd cat arg2' ' - diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$2\" branch) && - test "$diff" = branch + echo branch >expect && + git difftool --no-prompt \ + --extcmd sh\ -c\ \"cat\ \$2\" branch >actual && + test_cmp expect actual ' # Create a second file on master and a different version on branch @@ -289,21 +284,154 @@ test_expect_success PERL 'setup with 2 files different' ' ' test_expect_success PERL 'say no to the first file' ' - diff=$( (echo n; echo) | git difftool -x cat branch ) && - - echo "$diff" | stdin_contains m2 && - echo "$diff" | stdin_contains br2 && - echo "$diff" | stdin_doesnot_contain master && - echo "$diff" | stdin_doesnot_contain branch + (echo n && echo) >input && + git difftool -x cat branch <input >output && + grep m2 output && + grep br2 output && + ! grep master output && + ! grep branch output ' test_expect_success PERL 'say no to the second file' ' - diff=$( (echo; echo n) | git difftool -x cat branch ) && + (echo && echo n) >input && + git difftool -x cat branch <input >output && + grep master output && + grep branch output && + ! grep m2 output && + ! grep br2 output +' + +test_expect_success PERL 'difftool --tool-help' ' + git difftool --tool-help >output && + grep tool output +' + +test_expect_success PERL 'setup change in subdirectory' ' + git checkout master && + mkdir sub && + echo master >sub/sub && + git add sub/sub && + git commit -m "added sub/sub" && + echo test >>file && + echo test >>sub/sub && + git add file sub/sub && + git commit -m "modified both" +' + +run_dir_diff_test () { + test_expect_success PERL "$1 --no-symlinks" " + symlinks=--no-symlinks && + $2 + " + test_expect_success PERL,SYMLINKS "$1 --symlinks" " + symlinks=--symlinks && + $2 + " +} + +run_dir_diff_test 'difftool -d' ' + git difftool -d $symlinks --extcmd ls branch >output && + grep sub output && + grep file output +' + +run_dir_diff_test 'difftool --dir-diff' ' + git difftool --dir-diff $symlinks --extcmd ls branch >output && + grep sub output && + grep file output +' + +run_dir_diff_test 'difftool --dir-diff ignores --prompt' ' + git difftool --dir-diff $symlinks --prompt --extcmd ls branch >output && + grep sub output && + grep file output +' + +run_dir_diff_test 'difftool --dir-diff from subdirectory' ' + ( + cd sub && + git difftool --dir-diff $symlinks --extcmd ls branch >output && + grep sub output && + grep file output + ) +' + +run_dir_diff_test 'difftool --dir-diff when worktree file is missing' ' + test_when_finished git reset --hard && + rm file2 && + git difftool --dir-diff $symlinks --extcmd ls branch master >output && + grep file2 output +' + +write_script .git/CHECK_SYMLINKS <<\EOF +for f in file file2 sub/sub +do + echo "$f" + readlink "$2/$f" +done >actual +EOF + +test_expect_success PERL,SYMLINKS 'difftool --dir-diff --symlink without unstaged changes' ' + cat >expect <<-EOF && + file + $(pwd)/file + file2 + $(pwd)/file2 + sub/sub + $(pwd)/sub/sub + EOF + git difftool --dir-diff --symlink \ + --extcmd "./.git/CHECK_SYMLINKS" branch HEAD && + test_cmp actual expect +' + +write_script modify-right-file <<\EOF +echo "new content" >"$2/file" +EOF + +run_dir_diff_test 'difftool --dir-diff syncs worktree with unstaged change' ' + test_when_finished git reset --hard && + echo "orig content" >file && + git difftool -d $symlinks --extcmd "$(pwd)/modify-right-file" branch && + echo "new content" >expect && + test_cmp expect file +' + +run_dir_diff_test 'difftool --dir-diff syncs worktree without unstaged change' ' + test_when_finished git reset --hard && + git difftool -d $symlinks --extcmd "$(pwd)/modify-right-file" branch && + echo "new content" >expect && + test_cmp expect file +' + +write_script modify-file <<\EOF +echo "new content" >file +EOF + +test_expect_success PERL 'difftool --no-symlinks does not overwrite working tree file ' ' + echo "orig content" >file && + git difftool --dir-diff --no-symlinks --extcmd "$(pwd)/modify-file" branch && + echo "new content" >expect && + test_cmp expect file +' - echo "$diff" | stdin_contains master && - echo "$diff" | stdin_contains branch && - echo "$diff" | stdin_doesnot_contain m2 && - echo "$diff" | stdin_doesnot_contain br2 +write_script modify-both-files <<\EOF +echo "wt content" >file && +echo "tmp content" >"$2/file" && +echo "$2" >tmpdir +EOF + +test_expect_success PERL 'difftool --no-symlinks detects conflict ' ' + ( + TMPDIR=$TRASH_DIRECTORY && + export TMPDIR && + echo "orig content" >file && + test_must_fail git difftool --dir-diff --no-symlinks --extcmd "$(pwd)/modify-both-files" branch && + echo "wt content" >expect && + test_cmp expect file && + echo "tmp content" >expect && + test_cmp expect "$(cat tmpdir)/file" + ) ' test_done diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index 75f4716d8c..f698001c99 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -47,6 +47,13 @@ test_expect_success setup ' echo vvv >t/v && mkdir t/a && echo vvv >t/a/v && + { + echo "line without leading space1" + echo " line with leading space1" + echo " line with leading space2" + echo " line with leading space3" + echo "line without leading space2" + } >space && git add . && test_tick && git commit -m initial @@ -243,6 +250,84 @@ do git -c grep.extendedRegexp=true grep "a+b*c" ab >actual && test_cmp expected actual ' + + test_expect_success "grep $L with grep.patterntype=basic" ' + echo "ab:a+bc" >expected && + git -c grep.patterntype=basic grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success "grep $L with grep.patterntype=extended" ' + echo "ab:abc" >expected && + git -c grep.patterntype=extended grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success "grep $L with grep.patterntype=fixed" ' + echo "ab:a+b*c" >expected && + git -c grep.patterntype=fixed grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success LIBPCRE "grep $L with grep.patterntype=perl" ' + echo "ab:a+b*c" >expected && + git -c grep.patterntype=perl grep "a\x{2b}b\x{2a}c" ab >actual && + test_cmp expected actual + ' + + test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" ' + echo "ab:abc" >expected && + git \ + -c grep.patternType=default \ + -c grep.extendedRegexp=true \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success "grep $L with grep.extendedRegexp=true and grep.patternType=default" ' + echo "ab:abc" >expected && + git \ + -c grep.extendedRegexp=true \ + -c grep.patternType=default \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success 'grep $L with grep.patternType=extended and grep.extendedRegexp=false' ' + echo "ab:abc" >expected && + git \ + -c grep.patternType=extended \ + -c grep.extendedRegexp=false \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success 'grep $L with grep.patternType=basic and grep.extendedRegexp=true' ' + echo "ab:a+bc" >expected && + git \ + -c grep.patternType=basic \ + -c grep.extendedRegexp=true \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success 'grep $L with grep.extendedRegexp=false and grep.patternType=extended' ' + echo "ab:abc" >expected && + git \ + -c grep.extendedRegexp=false \ + -c grep.patternType=extended \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' + + test_expect_success 'grep $L with grep.extendedRegexp=true and grep.patternType=basic' ' + echo "ab:a+bc" >expected && + git \ + -c grep.extendedRegexp=true \ + -c grep.patternType=basic \ + grep "a+b*c" ab >actual && + test_cmp expected actual + ' done cat >expected <<EOF @@ -344,6 +429,11 @@ test_expect_success 'grep -f, multiple patterns' ' test_cmp expected actual ' +test_expect_success 'grep, multiple patterns' ' + git grep "$(cat patterns)" >actual && + test_cmp expected actual +' + cat >expected <<EOF file:foo mmap bar file:foo_mmap bar @@ -387,17 +477,6 @@ test_expect_success 'grep -q, silently report matches' ' test_cmp empty actual ' -# Create 1024 file names that sort between "y" and "z" to make sure -# the two files are handled by different calls to an external grep. -# This depends on MAXARGS in builtin-grep.c being 1024 or less. -c32="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v" -test_expect_success 'grep -C1, hunk mark between files' ' - for a in $c32; do for b in $c32; do : >y-$a$b; done; done && - git add y-?? && - git grep -C1 "^[yz]" >actual && - test_cmp expected actual -' - test_expect_success 'grep -C1 hunk mark between files' ' git grep -C1 "^[yz]" >actual && test_cmp expected actual @@ -423,31 +502,41 @@ test_expect_success 'log grep setup' ' test_expect_success 'log grep (1)' ' git log --author=author --pretty=tformat:%s >actual && - ( echo third ; echo initial ) >expect && + { + echo third && echo initial + } >expect && test_cmp expect actual ' test_expect_success 'log grep (2)' ' git log --author=" * " -F --pretty=tformat:%s >actual && - ( echo second ) >expect && + { + echo second + } >expect && test_cmp expect actual ' test_expect_success 'log grep (3)' ' git log --author="^A U" --pretty=tformat:%s >actual && - ( echo third ; echo initial ) >expect && + { + echo third && echo initial + } >expect && test_cmp expect actual ' test_expect_success 'log grep (4)' ' git log --author="frotz\.com>$" --pretty=tformat:%s >actual && - ( echo second ) >expect && + { + echo second + } >expect && test_cmp expect actual ' test_expect_success 'log grep (5)' ' git log --author=Thor -F --pretty=tformat:%s >actual && - ( echo third ; echo initial ) >expect && + { + echo third && echo initial + } >expect && test_cmp expect actual ' @@ -457,11 +546,49 @@ test_expect_success 'log grep (6)' ' test_cmp expect actual ' -test_expect_success 'log --grep --author implicitly uses all-match' ' - # grep matches initial and second but not third - # author matches only initial and third - git log --author="A U Thor" --grep=s --grep=l --format=%s >actual && - echo initial >expect && +test_expect_success 'log grep (7)' ' + git log -g --grep-reflog="commit: third" --pretty=tformat:%s >actual && + echo third >expect && + test_cmp expect actual +' + +test_expect_success 'log grep (8)' ' + git log -g --grep-reflog="commit: third" --grep-reflog="commit: second" --pretty=tformat:%s >actual && + { + echo third && echo second + } >expect && + test_cmp expect actual +' + +test_expect_success 'log grep (9)' ' + git log -g --grep-reflog="commit: third" --author="Thor" --pretty=tformat:%s >actual && + echo third >expect && + test_cmp expect actual +' + +test_expect_success 'log grep (9)' ' + git log -g --grep-reflog="commit: third" --author="non-existant" --pretty=tformat:%s >actual && + : >expect && + test_cmp expect actual +' + +test_expect_success 'log --grep-reflog can only be used under -g' ' + test_must_fail git log --grep-reflog="commit: third" +' + +test_expect_success 'log with multiple --grep uses union' ' + git log --grep=i --grep=r --format=%s >actual && + { + echo fourth && echo third && echo initial + } >expect && + test_cmp expect actual +' + +test_expect_success 'log --all-match with multiple --grep uses intersection' ' + git log --all-match --grep=i --grep=r --format=%s >actual && + { + echo third + } >expect && test_cmp expect actual ' @@ -473,7 +600,47 @@ test_expect_success 'log with multiple --author uses union' ' test_cmp expect actual ' -test_expect_success 'log with --grep and multiple --author uses all-match' ' +test_expect_success 'log --all-match with multiple --author still uses union' ' + git log --all-match --author="Thor" --author="Aster" --format=%s >actual && + { + echo third && echo second && echo initial + } >expect && + test_cmp expect actual +' + +test_expect_success 'log --grep --author uses intersection' ' + # grep matches only third and fourth + # author matches only initial and third + git log --author="A U Thor" --grep=r --format=%s >actual && + { + echo third + } >expect && + test_cmp expect actual +' + +test_expect_success 'log --grep --grep --author takes union of greps and intersects with author' ' + # grep matches initial and second but not third + # author matches only initial and third + git log --author="A U Thor" --grep=s --grep=l --format=%s >actual && + { + echo initial + } >expect && + test_cmp expect actual +' + +test_expect_success 'log ---all-match -grep --author --author still takes union of authors and intersects with grep' ' + # grep matches only initial and third + # author matches all but second + git log --all-match --author="Thor" --author="Night" --grep=i --format=%s >actual && + { + echo third && echo initial + } >expect && + test_cmp expect actual +' + +test_expect_success 'log --grep --author --author takes union of authors and intersects with grep' ' + # grep matches only initial and third + # author matches all but second git log --author="Thor" --author="Night" --grep=i --format=%s >actual && { echo third && echo initial @@ -481,9 +648,25 @@ test_expect_success 'log with --grep and multiple --author uses all-match' ' test_cmp expect actual ' -test_expect_success 'log with --grep and multiple --author uses all-match' ' - git log --author="Thor" --author="Night" --grep=q --format=%s >actual && - >expect && +test_expect_success 'log --all-match --grep --grep --author takes intersection' ' + # grep matches only third + # author matches only initial and third + git log --all-match --author="A U Thor" --grep=i --grep=r --format=%s >actual && + { + echo third + } >expect && + test_cmp expect actual +' + +test_expect_success 'log --author does not search in timestamp' ' + : >expect && + git log --author="$GIT_AUTHOR_DATE" >actual && + test_cmp expect actual +' + +test_expect_success 'log --committer does not search in timestamp' ' + : >expect && + git log --committer="$GIT_COMMITTER_DATE" >actual && test_cmp expect actual ' @@ -760,44 +943,147 @@ test_expect_success 'grep -G invalidpattern properly dies ' ' test_must_fail git grep -G "a[" ' +test_expect_success 'grep invalidpattern properly dies with grep.patternType=basic' ' + test_must_fail git -c grep.patterntype=basic grep "a[" +' + test_expect_success 'grep -E invalidpattern properly dies ' ' test_must_fail git grep -E "a[" ' +test_expect_success 'grep invalidpattern properly dies with grep.patternType=extended' ' + test_must_fail git -c grep.patterntype=extended grep "a[" +' + test_expect_success LIBPCRE 'grep -P invalidpattern properly dies ' ' test_must_fail git grep -P "a[" ' +test_expect_success LIBPCRE 'grep invalidpattern properly dies with grep.patternType=perl' ' + test_must_fail git -c grep.patterntype=perl grep "a[" +' + test_expect_success 'grep -G -E -F pattern' ' echo "ab:a+b*c" >expected && git grep -G -E -F "a+b*c" ab >actual && test_cmp expected actual ' +test_expect_success 'grep pattern with grep.patternType=basic, =extended, =fixed' ' + echo "ab:a+b*c" >expected && + git \ + -c grep.patterntype=basic \ + -c grep.patterntype=extended \ + -c grep.patterntype=fixed \ + grep "a+b*c" ab >actual && + test_cmp expected actual +' + test_expect_success 'grep -E -F -G pattern' ' echo "ab:a+bc" >expected && git grep -E -F -G "a+b*c" ab >actual && test_cmp expected actual ' +test_expect_success 'grep pattern with grep.patternType=extended, =fixed, =basic' ' + echo "ab:a+bc" >expected && + git \ + -c grep.patterntype=extended \ + -c grep.patterntype=fixed \ + -c grep.patterntype=basic \ + grep "a+b*c" ab >actual && + test_cmp expected actual +' + test_expect_success 'grep -F -G -E pattern' ' echo "ab:abc" >expected && git grep -F -G -E "a+b*c" ab >actual && test_cmp expected actual ' +test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =extended' ' + echo "ab:abc" >expected && + git \ + -c grep.patterntype=fixed \ + -c grep.patterntype=basic \ + -c grep.patterntype=extended \ + grep "a+b*c" ab >actual && + test_cmp expected actual +' + test_expect_success 'grep -G -F -P -E pattern' ' >empty && test_must_fail git grep -G -F -P -E "a\x{2b}b\x{2a}c" ab >actual && test_cmp empty actual ' +test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =perl, =extended' ' + >empty && + test_must_fail git \ + -c grep.patterntype=fixed \ + -c grep.patterntype=basic \ + -c grep.patterntype=perl \ + -c grep.patterntype=extended \ + grep "a\x{2b}b\x{2a}c" ab >actual && + test_cmp empty actual +' + test_expect_success LIBPCRE 'grep -G -F -E -P pattern' ' echo "ab:a+b*c" >expected && git grep -G -F -E -P "a\x{2b}b\x{2a}c" ab >actual && test_cmp expected actual ' +test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' ' + echo "ab:a+b*c" >expected && + git \ + -c grep.patterntype=fixed \ + -c grep.patterntype=basic \ + -c grep.patterntype=extended \ + -c grep.patterntype=perl \ + grep "a\x{2b}b\x{2a}c" ab >actual && + test_cmp expected actual +' + +test_expect_success LIBPCRE 'grep -P pattern with grep.patternType=fixed' ' + echo "ab:a+b*c" >expected && + git \ + -c grep.patterntype=fixed \ + grep -P "a\x{2b}b\x{2a}c" ab >actual && + test_cmp expected actual +' + +test_expect_success 'grep -F pattern with grep.patternType=basic' ' + echo "ab:a+b*c" >expected && + git \ + -c grep.patterntype=basic \ + grep -F "*c" ab >actual && + test_cmp expected actual +' + +test_expect_success 'grep -G pattern with grep.patternType=fixed' ' + { + echo "ab:a+b*c" + echo "ab:a+bc" + } >expected && + git \ + -c grep.patterntype=fixed \ + grep -G "a+b" ab >actual && + test_cmp expected actual +' + +test_expect_success 'grep -E pattern with grep.patternType=fixed' ' + { + echo "ab:a+b*c" + echo "ab:a+bc" + echo "ab:abc" + } >expected && + git \ + -c grep.patterntype=fixed \ + grep -E "a+" ab >actual && + test_cmp expected actual +' + test_config() { git config "$1" "$2" && test_when_finished "git config --unset $1" @@ -893,4 +1179,20 @@ test_expect_success 'mimic ack-grep --group' ' test_cmp expected actual ' +cat >expected <<EOF +space: line with leading space1 +space: line with leading space2 +space: line with leading space3 +EOF + +test_expect_success LIBPCRE 'grep -E "^ "' ' + git grep -E "^ " space >actual && + test_cmp expected actual +' + +test_expect_success LIBPCRE 'grep -P "^ "' ' + git grep -P "^ " space >actual && + test_cmp expected actual +' + test_done diff --git a/t/t7811-grep-open.sh b/t/t7811-grep-open.sh index a8957782cf..e1951a5cbb 100755 --- a/t/t7811-grep-open.sh +++ b/t/t7811-grep-open.sh @@ -125,11 +125,6 @@ test_expect_success 'modified file' ' test_cmp empty out ' -test_config() { - git config "$1" "$2" && - test_when_finished "git config --unset $1" -} - test_expect_success 'copes with color settings' ' rm -f actual && echo grep.h >expect && diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh index 41962f04a7..72176e42c1 100755 --- a/t/t8001-annotate.sh +++ b/t/t8001-annotate.sh @@ -6,9 +6,9 @@ test_description='git annotate' PROG='git annotate' . "$TEST_DIRECTORY"/annotate-tests.sh -test_expect_success 'Annotating an old revision works' ' - git annotate file master >result && - awk "{ print \$3; }" <result >authors && +test_expect_success 'annotate old revision' ' + git annotate file master >actual && + awk "{ print \$3; }" <actual >authors && test 2 = $(grep A <authors | wc -l) && test 2 = $(grep B <authors | wc -l) ' diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh index e2896cffc1..5cdf3f178e 100755 --- a/t/t8002-blame.sh +++ b/t/t8002-blame.sh @@ -7,8 +7,16 @@ PROG='git blame -c' . "$TEST_DIRECTORY"/annotate-tests.sh PROG='git blame -c -e' -test_expect_success 'Blame --show-email works' ' - check_count "<A@test.git>" 1 "<B@test.git>" 1 "<B1@test.git>" 1 "<B2@test.git>" 1 "<author@example.com>" 1 "<C@test.git>" 1 "<D@test.git>" 1 "<E at test dot git>" 1 +test_expect_success 'blame --show-email' ' + check_count \ + "<A@test.git>" 1 \ + "<B@test.git>" 1 \ + "<B1@test.git>" 1 \ + "<B2@test.git>" 1 \ + "<author@example.com>" 1 \ + "<C@test.git>" 1 \ + "<D@test.git>" 1 \ + "<E at test dot git>" 1 ' test_done diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh index 230143cf31..e7cac1db55 100755 --- a/t/t8003-blame-corner-cases.sh +++ b/t/t8003-blame-corner-cases.sh @@ -175,6 +175,12 @@ test_expect_success 'blame -L with invalid end' ' grep "has only 2 lines" errors ' +test_expect_success 'blame parses <end> part of -L' ' + git blame -L1,1 tres >out && + cat out && + test $(wc -l < out) -eq 1 +' + test_expect_success 'indent of line numbers, nine lines' ' git blame nine_lines >actual && test $(grep -c " " actual) = 0 diff --git a/t/t8004-blame-with-conflicts.sh b/t/t8004-blame-with-conflicts.sh index ba19ac127e..9c353ab222 100755 --- a/t/t8004-blame-with-conflicts.sh +++ b/t/t8004-blame-with-conflicts.sh @@ -66,7 +66,7 @@ test_expect_success \ git blame file2 ' -test_expect_success 'blame runs on conflicted file in stages 1,3' ' +test_expect_success 'blame does not crash with conflicted file in stages 1,3' ' git blame file1 ' diff --git a/t/t8006-blame-textconv.sh b/t/t8006-blame-textconv.sh index c3c22f7764..7683515155 100755 --- a/t/t8006-blame-textconv.sh +++ b/t/t8006-blame-textconv.sh @@ -10,7 +10,7 @@ find_blame() { cat >helper <<'EOF' #!/bin/sh grep -q '^bin: ' "$1" || { echo "E: $1 is not \"binary\" file" 1>&2; exit 1; } -perl -p -e 's/^bin: /converted: /' "$1" +"$PERL_PATH" -p -e 's/^bin: /converted: /' "$1" EOF chmod +x helper @@ -18,17 +18,13 @@ test_expect_success 'setup ' ' echo "bin: test number 0" >zero.bin && echo "bin: test 1" >one.bin && echo "bin: test number 2" >two.bin && - if test_have_prereq SYMLINKS; then - ln -s one.bin symlink.bin - fi && + test_ln_s_add one.bin symlink.bin && git add . && GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" && echo "bin: test 1 version 2" >one.bin && echo "bin: test number 2 version 2" >>two.bin && - if test_have_prereq SYMLINKS; then - rm symlink.bin && - ln -s two.bin symlink.bin - fi && + rm -f symlink.bin && + test_ln_s_add two.bin symlink.bin && GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00" ' @@ -135,7 +131,7 @@ test_expect_success SYMLINKS 'blame --textconv (on symlink)' ' # cp two.bin three.bin and make small tweak # (this will direct blame -C -C three.bin to consider two.bin and symlink.bin) -test_expect_success SYMLINKS 'make another new commit' ' +test_expect_success 'make another new commit' ' cat >three.bin <<\EOF && bin: test number 2 bin: test number 2 version 2 @@ -146,7 +142,7 @@ EOF GIT_AUTHOR_NAME=Number4 git commit -a -m Fourth --date="2010-01-01 23:00:00" ' -test_expect_success SYMLINKS 'blame on last commit (-C -C, symlink)' ' +test_expect_success 'blame on last commit (-C -C, symlink)' ' git blame -C -C three.bin >blame && find_blame <blame >result && cat >expected <<\EOF && diff --git a/t/t8007-cat-file-textconv.sh b/t/t8007-cat-file-textconv.sh index 78a0085e64..eacd49ade6 100755 --- a/t/t8007-cat-file-textconv.sh +++ b/t/t8007-cat-file-textconv.sh @@ -12,9 +12,7 @@ chmod +x helper test_expect_success 'setup ' ' echo "bin: test" >one.bin && - if test_have_prereq SYMLINKS; then - ln -s one.bin symlink.bin - fi && + test_ln_s_add one.bin symlink.bin && git add . && GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" && echo "bin: test version 2" >one.bin && @@ -22,11 +20,11 @@ test_expect_success 'setup ' ' ' cat >expected <<EOF -fatal: git cat-file --textconv: unable to run textconv on :one.bin +bin: test version 2 EOF test_expect_success 'no filter specified' ' - git cat-file --textconv :one.bin 2>result + git cat-file --textconv :one.bin >result && test_cmp expected result ' @@ -36,10 +34,6 @@ test_expect_success 'setup textconv filters' ' git config diff.test.cachetextconv false ' -cat >expected <<EOF -bin: test version 2 -EOF - test_expect_success 'cat-file without --textconv' ' git cat-file blob :one.bin >result && test_cmp expected result @@ -72,26 +66,20 @@ test_expect_success 'cat-file --textconv on previous commit' ' test_cmp expected result ' -test_expect_success SYMLINKS 'cat-file without --textconv (symlink)' ' +test_expect_success 'cat-file without --textconv (symlink)' ' + printf "%s" "one.bin" >expected && git cat-file blob :symlink.bin >result && - printf "%s" "one.bin" >expected test_cmp expected result ' -test_expect_success SYMLINKS 'cat-file --textconv on index (symlink)' ' - ! git cat-file --textconv :symlink.bin 2>result && - cat >expected <<\EOF && -fatal: git cat-file --textconv: unable to run textconv on :symlink.bin -EOF +test_expect_success 'cat-file --textconv on index (symlink)' ' + git cat-file --textconv :symlink.bin >result && test_cmp expected result ' -test_expect_success SYMLINKS 'cat-file --textconv on HEAD (symlink)' ' - ! git cat-file --textconv HEAD:symlink.bin 2>result && - cat >expected <<EOF && -fatal: git cat-file --textconv: unable to run textconv on HEAD:symlink.bin -EOF +test_expect_success 'cat-file --textconv on HEAD (symlink)' ' + git cat-file --textconv HEAD:symlink.bin >result && test_cmp expected result ' diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 8c12c65c72..3119c8c523 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -23,7 +23,6 @@ test_expect_success $PREREQ \ echo do echo " echo \"!\$a!\"" echo "done >commandline\$output" - test_have_prereq MINGW && echo "dos2unix commandline\$output" echo "cat > msgtxt\$output" ) >fake.sendmail && chmod +x ./fake.sendmail && @@ -101,7 +100,7 @@ test_expect_success $PREREQ \ test_expect_success $PREREQ 'Send patches with --envelope-sender' ' clean_fake_sendmail && - git send-email --envelope-sender="Patch Contributer <patch@example.com>" --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors + git send-email --envelope-sender="Patch Contributor <patch@example.com>" --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors ' test_expect_success $PREREQ 'setup expect' ' @@ -171,6 +170,81 @@ Result: OK EOF " +test_suppress_self () { + test_commit $3 && + test_when_finished "git reset --hard HEAD^" && + + write_script cccmd-sed <<-EOF && + sed -n -e s/^cccmd--//p "\$1" + EOF + + git commit --amend --author="$1 <$2>" -F - && + clean_fake_sendmail && + git format-patch --stdout -1 >"suppress-self-$3.patch" && + + git send-email --from="$1 <$2>" \ + --to=nobody@example.com \ + --cc-cmd=./cccmd-sed \ + --suppress-cc=self \ + --smtp-server="$(pwd)/fake.sendmail" \ + suppress-self-$3.patch && + + mv msgtxt1 msgtxt1-$3 && + sed -e '/^$/q' msgtxt1-$3 >"msghdr1-$3" && + >"expected-no-cc-$3" && + + (grep '^Cc:' msghdr1-$3 >"actual-no-cc-$3"; + test_cmp expected-no-cc-$3 actual-no-cc-$3) +} + +test_suppress_self_unquoted () { + test_suppress_self "$1" "$2" "unquoted-$3" <<-EOF + test suppress-cc.self unquoted-$3 with name $1 email $2 + + unquoted-$3 + + cccmd--$1 <$2> + + Cc: $1 <$2> + Signed-off-by: $1 <$2> + EOF +} + +test_suppress_self_quoted () { + test_suppress_self "$1" "$2" "quoted-$3" <<-EOF + test suppress-cc.self quoted-$3 with name $1 email $2 + + quoted-$3 + + cccmd--"$1" <$2> + + Cc: $1 <$2> + Cc: "$1" <$2> + Signed-off-by: $1 <$2> + Signed-off-by: "$1" <$2> + EOF +} + +test_expect_success $PREREQ 'self name is suppressed' " + test_suppress_self_unquoted 'A U Thor' 'author@example.com' \ + 'self_name_suppressed' +" + +test_expect_success $PREREQ 'self name with dot is suppressed' " + test_suppress_self_quoted 'A U. Thor' 'author@example.com' \ + 'self_name_dot_suppressed' +" + +test_expect_success $PREREQ 'non-ascii self name is suppressed' " + test_suppress_self_quoted 'Füñný Nâmé' 'odd_?=mail@example.com' \ + 'non_ascii_self_suppressed' +" + +test_expect_success $PREREQ 'sanitized self name is suppressed' " + test_suppress_self_unquoted '\"A U. Thor\"' 'author@example.com' \ + 'self_name_sanitized_suppressed' +" + test_expect_success $PREREQ 'Show all headers' ' git send-email \ --dry-run \ @@ -191,17 +265,44 @@ test_expect_success $PREREQ 'Show all headers' ' test_expect_success $PREREQ 'Prompting works' ' clean_fake_sendmail && - (echo "Example <from@example.com>" - echo "to@example.com" + (echo "to@example.com" echo "" ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \ --smtp-server="$(pwd)/fake.sendmail" \ $patches \ 2>errors && - grep "^From: Example <from@example.com>\$" msgtxt1 && + grep "^From: A U Thor <author@example.com>\$" msgtxt1 && grep "^To: to@example.com\$" msgtxt1 ' +test_expect_success $PREREQ,AUTOIDENT 'implicit ident is allowed' ' + clean_fake_sendmail && + (sane_unset GIT_AUTHOR_NAME && + sane_unset GIT_AUTHOR_EMAIL && + sane_unset GIT_COMMITTER_NAME && + sane_unset GIT_COMMITTER_EMAIL && + GIT_SEND_EMAIL_NOTTY=1 git send-email \ + --smtp-server="$(pwd)/fake.sendmail" \ + --to=to@example.com \ + $patches </dev/null 2>errors + ) +' + +test_expect_success $PREREQ,!AUTOIDENT 'broken implicit ident aborts send-email' ' + clean_fake_sendmail && + (sane_unset GIT_AUTHOR_NAME && + sane_unset GIT_AUTHOR_EMAIL && + sane_unset GIT_COMMITTER_NAME && + sane_unset GIT_COMMITTER_EMAIL && + GIT_SEND_EMAIL_NOTTY=1 && export GIT_SEND_EMAIL_NOTTY && + test_must_fail git send-email \ + --smtp-server="$(pwd)/fake.sendmail" \ + --to=to@example.com \ + $patches </dev/null 2>errors && + test_i18ngrep "tell me who you are" errors + ) +' + test_expect_success $PREREQ 'tocmd works' ' clean_fake_sendmail && cp $patches tocmd.patch && @@ -760,7 +861,7 @@ test_expect_success $PREREQ 'confirm detects EOF (auto causes failure)' ' test $ret = "0" ' -test_expect_success $PREREQ 'confirm doesnt loop forever' ' +test_expect_success $PREREQ 'confirm does not loop forever' ' CONFIRM=$(git config --get sendemail.confirm) && git config sendemail.confirm auto && GIT_SEND_EMAIL_NOTTY=1 && @@ -841,6 +942,102 @@ test_expect_success $PREREQ '--compose adds MIME for utf8 subject' ' grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 ' +test_expect_success $PREREQ 'utf8 author is correctly passed on' ' + clean_fake_sendmail && + test_commit weird_author && + test_when_finished "git reset --hard HEAD^" && + git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" && + git format-patch --stdout -1 >funny_name.patch && + git send-email --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + funny_name.patch && + grep "^From: Füñný Nâmé <odd_?=mail@example.com>" msgtxt1 +' + +test_expect_success $PREREQ 'utf8 sender is not duplicated' ' + clean_fake_sendmail && + test_commit weird_sender && + test_when_finished "git reset --hard HEAD^" && + git commit --amend --author "Füñný Nâmé <odd_?=mail@example.com>" && + git format-patch --stdout -1 >funny_name.patch && + git send-email --from="Füñný Nâmé <odd_?=mail@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + funny_name.patch && + grep "^From: " msgtxt1 >msgfrom && + test_line_count = 1 msgfrom +' + +test_expect_success $PREREQ 'sendemail.composeencoding works' ' + clean_fake_sendmail && + git config sendemail.composeencoding iso-8859-1 && + (echo "#!$SHELL_PATH" && + echo "echo utf8 body: à éìöú >>\"\$1\"" + ) >fake-editor-utf8 && + chmod +x fake-editor-utf8 && + GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \ + git send-email \ + --compose --subject foo \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches && + grep "^utf8 body" msgtxt1 && + grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 +' + +test_expect_success $PREREQ '--compose-encoding works' ' + clean_fake_sendmail && + (echo "#!$SHELL_PATH" && + echo "echo utf8 body: à éìöú >>\"\$1\"" + ) >fake-editor-utf8 && + chmod +x fake-editor-utf8 && + GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \ + git send-email \ + --compose-encoding iso-8859-1 \ + --compose --subject foo \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches && + grep "^utf8 body" msgtxt1 && + grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 +' + +test_expect_success $PREREQ '--compose-encoding overrides sendemail.composeencoding' ' + clean_fake_sendmail && + git config sendemail.composeencoding iso-8859-1 && + (echo "#!$SHELL_PATH" && + echo "echo utf8 body: à éìöú >>\"\$1\"" + ) >fake-editor-utf8 && + chmod +x fake-editor-utf8 && + GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \ + git send-email \ + --compose-encoding iso-8859-2 \ + --compose --subject foo \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches && + grep "^utf8 body" msgtxt1 && + grep "^Content-Type: text/plain; charset=iso-8859-2" msgtxt1 +' + +test_expect_success $PREREQ '--compose-encoding adds correct MIME for subject' ' + clean_fake_sendmail && + GIT_EDITOR="\"$(pwd)/fake-editor\"" \ + git send-email \ + --compose-encoding iso-8859-2 \ + --compose --subject utf8-sübjëct \ + --from="Example <nobody@example.com>" \ + --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + $patches && + grep "^fake edit" msgtxt1 && + grep "^Subject: =?iso-8859-2?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1 +' + test_expect_success $PREREQ 'detects ambiguous reference/file conflict' ' echo master > master && git add master && @@ -894,55 +1091,6 @@ test_expect_success $PREREQ 'threading but no chain-reply-to' ' grep "In-Reply-To: " stdout ' -test_expect_success $PREREQ 'warning with an implicit --chain-reply-to' ' - git send-email \ - --dry-run \ - --from="Example <nobody@example.com>" \ - --to=nobody@example.com \ - outdir/000?-*.patch 2>errors >out && - grep "no-chain-reply-to" errors -' - -test_expect_success $PREREQ 'no warning with an explicit --chain-reply-to' ' - git send-email \ - --dry-run \ - --from="Example <nobody@example.com>" \ - --to=nobody@example.com \ - --chain-reply-to \ - outdir/000?-*.patch 2>errors >out && - ! grep "no-chain-reply-to" errors -' - -test_expect_success $PREREQ 'no warning with an explicit --no-chain-reply-to' ' - git send-email \ - --dry-run \ - --from="Example <nobody@example.com>" \ - --to=nobody@example.com \ - --nochain-reply-to \ - outdir/000?-*.patch 2>errors >out && - ! grep "no-chain-reply-to" errors -' - -test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = false' ' - git config sendemail.chainreplyto false && - git send-email \ - --dry-run \ - --from="Example <nobody@example.com>" \ - --to=nobody@example.com \ - outdir/000?-*.patch 2>errors >out && - ! grep "no-chain-reply-to" errors -' - -test_expect_success $PREREQ 'no warning with sendemail.chainreplyto = true' ' - git config sendemail.chainreplyto true && - git send-email \ - --dry-run \ - --from="Example <nobody@example.com>" \ - --to=nobody@example.com \ - outdir/000?-*.patch 2>errors >out && - ! grep "no-chain-reply-to" errors -' - test_expect_success $PREREQ 'sendemail.to works' ' git config --replace-all sendemail.to "Somebody <somebody@ex.com>" && git send-email \ @@ -1061,6 +1209,23 @@ EOF ' test_expect_success $PREREQ 'setup expect' ' +cat >expected <<EOF +Subject: subject goes here +EOF +' + +test_expect_success $PREREQ 'ASCII subject is not RFC2047 quoted' ' + clean_fake_sendmail && + echo bogus | + git send-email --from=author@example.com --to=nobody@example.com \ + --smtp-server="$(pwd)/fake.sendmail" \ + --8bit-encoding=UTF-8 \ + email-using-8bit >stdout && + grep "Subject" msgtxt1 >actual && + test_cmp expected actual +' + +test_expect_success $PREREQ 'setup expect' ' cat >content-type-decl <<EOF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/t/t9002-column.sh b/t/t9002-column.sh new file mode 100755 index 0000000000..89983527b6 --- /dev/null +++ b/t/t9002-column.sh @@ -0,0 +1,180 @@ +#!/bin/sh + +test_description='git column' +. ./test-lib.sh + +test_expect_success 'setup' ' + cat >lista <<\EOF +one +two +three +four +five +six +seven +eight +nine +ten +eleven +EOF +' + +test_expect_success 'never' ' + git column --indent=Z --mode=never <lista >actual && + test_cmp lista actual +' + +test_expect_success 'always' ' + cat >expected <<\EOF && +Zone +Ztwo +Zthree +Zfour +Zfive +Zsix +Zseven +Zeight +Znine +Zten +Zeleven +EOF + git column --indent=Z --mode=plain <lista >actual && + test_cmp expected actual +' + +test_expect_success '80 columns' ' + cat >expected <<\EOF && +one two three four five six seven eight nine ten eleven +EOF + COLUMNS=80 git column --mode=column <lista >actual && + test_cmp expected actual +' + +cat >expected <<\EOF +one +two +three +four +five +six +seven +eight +nine +ten +eleven +EOF + +test_expect_success COLUMNS_CAN_BE_1 'COLUMNS = 1' ' + COLUMNS=1 git column --mode=column <lista >actual && + test_cmp expected actual +' + +test_expect_success 'width = 1' ' + git column --mode=column --width=1 <lista >actual && + test_cmp expected actual +' + +COLUMNS=20 +export COLUMNS + +test_expect_success '20 columns' ' + cat >expected <<\EOF && +one seven +two eight +three nine +four ten +five eleven +six +EOF + git column --mode=column <lista >actual && + test_cmp expected actual +' + +test_expect_success '20 columns, nodense' ' + cat >expected <<\EOF && +one seven +two eight +three nine +four ten +five eleven +six +EOF + git column --mode=column,nodense < lista > actual && + test_cmp expected actual +' + +test_expect_success '20 columns, dense' ' + cat >expected <<\EOF && +one five nine +two six ten +three seven eleven +four eight +EOF + git column --mode=column,dense < lista > actual && + test_cmp expected actual +' + +test_expect_success '20 columns, padding 2' ' + cat >expected <<\EOF && +one seven +two eight +three nine +four ten +five eleven +six +EOF + git column --mode=column --padding 2 <lista >actual && + test_cmp expected actual +' + +test_expect_success '20 columns, indented' ' + cat >expected <<\EOF && + one seven + two eight + three nine + four ten + five eleven + six +EOF + git column --mode=column --indent=" " <lista >actual && + test_cmp expected actual +' + +test_expect_success '20 columns, row first' ' + cat >expected <<\EOF && +one two +three four +five six +seven eight +nine ten +eleven +EOF + git column --mode=row <lista >actual && + test_cmp expected actual +' + +test_expect_success '20 columns, row first, nodense' ' + cat >expected <<\EOF && +one two +three four +five six +seven eight +nine ten +eleven +EOF + git column --mode=row,nodense <lista >actual && + test_cmp expected actual +' + +test_expect_success '20 columns, row first, dense' ' + cat >expected <<\EOF && +one two three +four five six +seven eight nine +ten eleven +EOF + git column --mode=row,dense <lista >actual && + test_cmp expected actual +' + +test_done diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh index 6f6175a8f7..6dafe7e99a 100755 --- a/t/t9010-svn-fe.sh +++ b/t/t9010-svn-fe.sh @@ -5,8 +5,27 @@ test_description='check svn dumpfile importer' . ./test-lib.sh reinit_git () { + if ! test_declared_prereq PIPE + then + echo >&4 "reinit_git: need to declare PIPE prerequisite" + return 127 + fi rm -fr .git && - git init + rm -f stream backflow && + git init && + mkfifo stream backflow +} + +try_dump () { + input=$1 && + maybe_fail_svnfe=${2:+test_$2} && + maybe_fail_fi=${3:+test_$3} && + + { + $maybe_fail_svnfe test-svn-fe "$input" >stream 3<backflow & + } && + $maybe_fail_fi git fast-import --cat-blob-fd=3 <stream 3>backflow && + wait $! } properties () { @@ -35,21 +54,19 @@ text_no_props () { >empty -test_expect_success 'empty dump' ' +test_expect_success PIPE 'empty dump' ' reinit_git && echo "SVN-fs-dump-format-version: 2" >input && - test-svn-fe input >stream && - git fast-import <stream + try_dump input ' -test_expect_success 'v4 dumps not supported' ' +test_expect_success PIPE 'v4 dumps not supported' ' reinit_git && echo "SVN-fs-dump-format-version: 4" >v4.dump && - test_must_fail test-svn-fe v4.dump >stream && - test_cmp empty stream + try_dump v4.dump must_fail ' -test_expect_failure 'empty revision' ' +test_expect_failure PIPE 'empty revision' ' reinit_git && printf "rev <nobody, nobody@local>: %s\n" "" "" >expect && cat >emptyrev.dump <<-\EOF && @@ -64,13 +81,12 @@ test_expect_failure 'empty revision' ' Content-length: 0 EOF - test-svn-fe emptyrev.dump >stream && - git fast-import <stream && + try_dump emptyrev.dump && git log -p --format="rev <%an, %ae>: %s" HEAD >actual && test_cmp expect actual ' -test_expect_success 'empty properties' ' +test_expect_success PIPE 'empty properties' ' reinit_git && printf "rev <nobody, nobody@local>: %s\n" "" "" >expect && cat >emptyprop.dump <<-\EOF && @@ -88,13 +104,12 @@ test_expect_success 'empty properties' ' PROPS-END EOF - test-svn-fe emptyprop.dump >stream && - git fast-import <stream && + try_dump emptyprop.dump && git log -p --format="rev <%an, %ae>: %s" HEAD >actual && test_cmp expect actual ' -test_expect_success 'author name and commit message' ' +test_expect_success PIPE 'author name and commit message' ' reinit_git && echo "<author@example.com, author@example.com@local>" >expect.author && cat >message <<-\EOF && @@ -121,15 +136,14 @@ test_expect_success 'author name and commit message' ' echo && cat props } >log.dump && - test-svn-fe log.dump >stream && - git fast-import <stream && + try_dump log.dump && git log -p --format="%B" HEAD >actual.log && git log --format="<%an, %ae>" >actual.author && test_cmp message actual.log && test_cmp expect.author actual.author ' -test_expect_success 'unsupported properties are ignored' ' +test_expect_success PIPE 'unsupported properties are ignored' ' reinit_git && echo author >expect && cat >extraprop.dump <<-\EOF && @@ -149,13 +163,12 @@ test_expect_success 'unsupported properties are ignored' ' author PROPS-END EOF - test-svn-fe extraprop.dump >stream && - git fast-import <stream && + try_dump extraprop.dump && git log -p --format=%an HEAD >actual && test_cmp expect actual ' -test_expect_failure 'timestamp and empty file' ' +test_expect_failure PIPE 'timestamp and empty file' ' echo author@example.com >expect.author && echo 1999-01-01 >expect.date && echo file >expect.files && @@ -186,8 +199,7 @@ test_expect_failure 'timestamp and empty file' ' EOF } >emptyfile.dump && - test-svn-fe emptyfile.dump >stream && - git fast-import <stream && + try_dump emptyfile.dump && git log --format=%an HEAD >actual.author && git log --date=short --format=%ad HEAD >actual.date && git ls-tree -r --name-only HEAD >actual.files && @@ -198,7 +210,7 @@ test_expect_failure 'timestamp and empty file' ' test_cmp empty file ' -test_expect_success 'directory with files' ' +test_expect_success PIPE 'directory with files' ' reinit_git && printf "%s\n" directory/file1 directory/file2 >expect.files && echo hi >hi && @@ -242,8 +254,7 @@ test_expect_success 'directory with files' ' EOF text_no_props hi } >directory.dump && - test-svn-fe directory.dump >stream && - git fast-import <stream && + try_dump directory.dump && git ls-tree -r --name-only HEAD >actual.files && git checkout HEAD directory && @@ -252,7 +263,107 @@ test_expect_success 'directory with files' ' test_cmp hi directory/file2 ' -test_expect_success 'node without action' ' +test_expect_success PIPE 'branch name with backslash' ' + reinit_git && + sort <<-\EOF >expect.branch-files && + trunk/file1 + trunk/file2 + "branches/UpdateFOPto094\\/file1" + "branches/UpdateFOPto094\\/file2" + EOF + + echo hi >hi && + echo hello >hello && + { + properties \ + svn:author author@example.com \ + svn:date "1999-02-02T00:01:02.000000Z" \ + svn:log "add directory with some files in it" && + echo PROPS-END + } >props.setup && + { + properties \ + svn:author brancher@example.com \ + svn:date "2007-12-06T21:38:34.000000Z" \ + svn:log "Updating fop to .94 and adjust fo-stylesheets" && + echo PROPS-END + } >props.branch && + { + cat <<-EOF && + SVN-fs-dump-format-version: 3 + + Revision-number: 1 + EOF + echo Prop-content-length: $(wc -c <props.setup) && + echo Content-length: $(wc -c <props.setup) && + echo && + cat props.setup && + cat <<-\EOF && + + Node-path: trunk + Node-kind: dir + Node-action: add + Prop-content-length: 10 + Content-length: 10 + + PROPS-END + + Node-path: branches + Node-kind: dir + Node-action: add + Prop-content-length: 10 + Content-length: 10 + + PROPS-END + + Node-path: trunk/file1 + Node-kind: file + Node-action: add + EOF + text_no_props hello && + cat <<-\EOF && + Node-path: trunk/file2 + Node-kind: file + Node-action: add + EOF + text_no_props hi && + cat <<-\EOF && + + Revision-number: 2 + EOF + echo Prop-content-length: $(wc -c <props.branch) && + echo Content-length: $(wc -c <props.branch) && + echo && + cat props.branch && + cat <<-\EOF + + Node-path: branches/UpdateFOPto094\ + Node-kind: dir + Node-action: add + Node-copyfrom-rev: 1 + Node-copyfrom-path: trunk + + Node-kind: dir + Node-action: add + Prop-content-length: 34 + Content-length: 34 + + K 13 + svn:mergeinfo + V 0 + + PROPS-END + EOF + } >branch.dump && + try_dump branch.dump && + + git ls-tree -r --name-only HEAD | + sort >actual.branch-files && + test_cmp expect.branch-files actual.branch-files +' + +test_expect_success PIPE 'node without action' ' + reinit_git && cat >inaction.dump <<-\EOF && SVN-fs-dump-format-version: 3 @@ -269,10 +380,11 @@ test_expect_success 'node without action' ' PROPS-END EOF - test_must_fail test-svn-fe inaction.dump + try_dump inaction.dump must_fail ' -test_expect_success 'action: add node without text' ' +test_expect_success PIPE 'action: add node without text' ' + reinit_git && cat >textless.dump <<-\EOF && SVN-fs-dump-format-version: 3 @@ -290,10 +402,10 @@ test_expect_success 'action: add node without text' ' PROPS-END EOF - test_must_fail test-svn-fe textless.dump + try_dump textless.dump must_fail ' -test_expect_failure 'change file mode but keep old content' ' +test_expect_failure PIPE 'change file mode but keep old content' ' reinit_git && cat >expect <<-\EOF && OBJID @@ -356,8 +468,7 @@ test_expect_failure 'change file mode but keep old content' ' PROPS-END EOF - test-svn-fe filemode.dump >stream && - git fast-import <stream && + try_dump filemode.dump && { git rev-list HEAD | git diff-tree --root --stdin | @@ -370,7 +481,7 @@ test_expect_failure 'change file mode but keep old content' ' test_cmp hello actual.target ' -test_expect_success 'NUL in property value' ' +test_expect_success PIPE 'NUL in property value' ' reinit_git && echo "commit message" >expect.message && { @@ -391,13 +502,12 @@ test_expect_success 'NUL in property value' ' echo && cat props } >nulprop.dump && - test-svn-fe nulprop.dump >stream && - git fast-import <stream && + try_dump nulprop.dump && git diff-tree --always -s --format=%s HEAD >actual.message && test_cmp expect.message actual.message ' -test_expect_success 'NUL in log message, file content, and property name' ' +test_expect_success PIPE 'NUL in log message, file content, and property name' ' # Caveat: svnadmin 1.6.16 (r1073529) truncates at \0 in the # svn:specialQnotreally example. reinit_git && @@ -458,8 +568,7 @@ test_expect_success 'NUL in log message, file content, and property name' ' link hello EOF } >8bitclean.dump && - test-svn-fe 8bitclean.dump >stream && - git fast-import <stream && + try_dump 8bitclean.dump && { git rev-list HEAD | git diff-tree --root --stdin | @@ -478,7 +587,7 @@ test_expect_success 'NUL in log message, file content, and property name' ' test_cmp expect.hello2 actual.hello2 ' -test_expect_success 'change file mode and reiterate content' ' +test_expect_success PIPE 'change file mode and reiterate content' ' reinit_git && cat >expect <<-\EOF && OBJID @@ -490,7 +599,7 @@ test_expect_success 'change file mode and reiterate content' ' EOF echo "link hello" >expect.blob && echo hello >hello && - cat >filemode.dump <<-\EOF && + cat >filemode2.dump <<-\EOF && SVN-fs-dump-format-version: 3 Revision-number: 1 @@ -545,8 +654,7 @@ test_expect_success 'change file mode and reiterate content' ' PROPS-END link hello EOF - test-svn-fe filemode.dump >stream && - git fast-import <stream && + try_dump filemode2.dump && { git rev-list HEAD | git diff-tree --root --stdin | @@ -559,7 +667,8 @@ test_expect_success 'change file mode and reiterate content' ' test_cmp hello actual.target ' -test_expect_success 'deltas not supported' ' +test_expect_success PIPE 'deltas supported' ' + reinit_git && { # (old) h + (inline) ello + (old) \n printf "SVNQ%b%b%s" "Q\003\006\005\004" "\001Q\0204\001\002" "ello" | @@ -619,10 +728,10 @@ test_expect_success 'deltas not supported' ' echo PROPS-END && cat delta } >delta.dump && - test_must_fail test-svn-fe delta.dump + try_dump delta.dump ' -test_expect_success 'property deltas supported' ' +test_expect_success PIPE 'property deltas supported' ' reinit_git && cat >expect <<-\EOF && OBJID @@ -678,8 +787,7 @@ test_expect_success 'property deltas supported' ' PROPS-END EOF } >propdelta.dump && - test-svn-fe propdelta.dump >stream && - git fast-import <stream && + try_dump propdelta.dump && { git rev-list HEAD | git diff-tree --stdin | @@ -688,7 +796,7 @@ test_expect_success 'property deltas supported' ' test_cmp expect actual ' -test_expect_success 'properties on /' ' +test_expect_success PIPE 'properties on /' ' reinit_git && cat <<-\EOF >expect && OBJID @@ -733,8 +841,7 @@ test_expect_success 'properties on /' ' PROPS-END EOF - test-svn-fe changeroot.dump >stream && - git fast-import <stream && + try_dump changeroot.dump && { git rev-list HEAD | git diff-tree --root --always --stdin | @@ -743,7 +850,7 @@ test_expect_success 'properties on /' ' test_cmp expect actual ' -test_expect_success 'deltas for typechange' ' +test_expect_success PIPE 'deltas for typechange' ' reinit_git && cat >expect <<-\EOF && OBJID @@ -819,8 +926,7 @@ test_expect_success 'deltas for typechange' ' PROPS-END link testing 321 EOF - test-svn-fe deleteprop.dump >stream && - git fast-import <stream && + try_dump deleteprop.dump && { git rev-list HEAD | git diff-tree --root --stdin | @@ -829,6 +935,143 @@ test_expect_success 'deltas for typechange' ' test_cmp expect actual ' +test_expect_success PIPE 'deltas need not consume the whole preimage' ' + reinit_git && + cat >expect <<-\EOF && + OBJID + :120000 100644 OBJID OBJID T postimage + OBJID + :100644 120000 OBJID OBJID T postimage + OBJID + :000000 100644 OBJID OBJID A postimage + EOF + echo "first preimage" >expect.1 && + printf target >expect.2 && + printf lnk >expect.3 && + { + printf "SVNQ%b%b%b" "QQ\017\001\017" "\0217" "first preimage\n" | + q_to_nul + } >delta.1 && + { + properties svn:special "*" && + echo PROPS-END + } >symlink.props && + { + printf "SVNQ%b%b%b" "Q\002\013\004\012" "\0201\001\001\0211" "lnk target" | + q_to_nul + } >delta.2 && + { + printf "SVNQ%b%b" "Q\004\003\004Q" "\001Q\002\002" | + q_to_nul + } >delta.3 && + { + cat <<-\EOF && + SVN-fs-dump-format-version: 3 + + Revision-number: 1 + Prop-content-length: 10 + Content-length: 10 + + PROPS-END + + Node-path: postimage + Node-kind: file + Node-action: add + Text-delta: true + Prop-content-length: 10 + EOF + echo Text-content-length: $(wc -c <delta.1) && + echo Content-length: $((10 + $(wc -c <delta.1))) && + echo && + echo PROPS-END && + cat delta.1 && + cat <<-\EOF && + + Revision-number: 2 + Prop-content-length: 10 + Content-length: 10 + + PROPS-END + + Node-path: postimage + Node-kind: file + Node-action: change + Text-delta: true + EOF + echo Prop-content-length: $(wc -c <symlink.props) && + echo Text-content-length: $(wc -c <delta.2) && + echo Content-length: $(($(wc -c <symlink.props) + $(wc -c <delta.2))) && + echo && + cat symlink.props && + cat delta.2 && + cat <<-\EOF && + + Revision-number: 3 + Prop-content-length: 10 + Content-length: 10 + + PROPS-END + + Node-path: postimage + Node-kind: file + Node-action: change + Text-delta: true + Prop-content-length: 10 + EOF + echo Text-content-length: $(wc -c <delta.3) && + echo Content-length: $((10 + $(wc -c <delta.3))) && + echo && + echo PROPS-END && + cat delta.3 && + echo + } >deltapartial.dump && + try_dump deltapartial.dump && + { + git rev-list HEAD | + git diff-tree --root --stdin | + sed "s/$_x40/OBJID/g" + } >actual && + test_cmp expect actual && + git show HEAD:postimage >actual.3 && + git show HEAD^:postimage >actual.2 && + git show HEAD^^:postimage >actual.1 && + test_cmp expect.1 actual.1 && + test_cmp expect.2 actual.2 && + test_cmp expect.3 actual.3 +' + +test_expect_success PIPE 'no hang for delta trying to read past end of preimage' ' + reinit_git && + { + # COPY 1 + printf "SVNQ%b%b" "Q\001\001\002Q" "\001Q" | + q_to_nul + } >greedy.delta && + { + cat <<-\EOF && + SVN-fs-dump-format-version: 3 + + Revision-number: 1 + Prop-content-length: 10 + Content-length: 10 + + PROPS-END + + Node-path: bootstrap + Node-kind: file + Node-action: add + Text-delta: true + Prop-content-length: 10 + EOF + echo Text-content-length: $(wc -c <greedy.delta) && + echo Content-length: $((10 + $(wc -c <greedy.delta))) && + echo && + echo PROPS-END && + cat greedy.delta && + echo + } >greedydelta.dump && + try_dump greedydelta.dump must_fail might_fail +' test_expect_success 'set up svn repo' ' svnconf=$PWD/svnconf && @@ -844,12 +1087,12 @@ test_expect_success 'set up svn repo' ' fi ' -test_expect_success SVNREPO 't9135/svn.dump' ' - git init simple-git && - test-svn-fe "$TEST_DIRECTORY/t9135/svn.dump" >simple.fe && +test_expect_success SVNREPO,PIPE 't9135/svn.dump' ' + mkdir -p simple-git && ( cd simple-git && - git fast-import <../simple.fe + reinit_git && + try_dump "$TEST_DIRECTORY/t9135/svn.dump" ) && ( cd simple-svnco && diff --git a/t/t9011-svn-da.sh b/t/t9011-svn-da.sh new file mode 100755 index 0000000000..b38d16f9db --- /dev/null +++ b/t/t9011-svn-da.sh @@ -0,0 +1,248 @@ +#!/bin/sh + +test_description='test parsing of svndiff0 files + +Using the "test-svn-fe -d" helper, check that svn-fe correctly +interprets deltas using various facilities (some from the spec, +some only learned from practice). +' +. ./test-lib.sh + +>empty +printf foo >preimage + +test_expect_success 'reject empty delta' ' + test_must_fail test-svn-fe -d preimage empty 0 +' + +test_expect_success 'delta can empty file' ' + printf "SVNQ" | q_to_nul >clear.delta && + test-svn-fe -d preimage clear.delta 4 >actual && + test_cmp empty actual +' + +test_expect_success 'reject svndiff2' ' + printf "SVN\002" >bad.filetype && + test_must_fail test-svn-fe -d preimage bad.filetype 4 +' + +test_expect_success 'one-window empty delta' ' + printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow && + test-svn-fe -d preimage clear.onewindow 9 >actual && + test_cmp empty actual +' + +test_expect_success 'reject incomplete window header' ' + printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow && + printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow && + test_must_fail test-svn-fe -d preimage clear.onewindow 6 && + test_must_fail test-svn-fe -d preimage clear.partialwindow 6 +' + +test_expect_success 'reject declared delta longer than actual delta' ' + printf "SVNQ%s" "QQQQQ" | q_to_nul >clear.onewindow && + printf "SVNQ%s" "QQ" | q_to_nul >clear.partialwindow && + test_must_fail test-svn-fe -d preimage clear.onewindow 14 && + test_must_fail test-svn-fe -d preimage clear.partialwindow 9 +' + +test_expect_success 'two-window empty delta' ' + printf "SVNQ%s%s" "QQQQQ" "QQQQQ" | q_to_nul >clear.twowindow && + test-svn-fe -d preimage clear.twowindow 14 >actual && + test_must_fail test-svn-fe -d preimage clear.twowindow 13 && + test_cmp empty actual +' + +test_expect_success 'noisy zeroes' ' + printf "SVNQ%s" \ + "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRQQQQQ" | + tr R "\200" | + q_to_nul >clear.noisy && + len=$(wc -c <clear.noisy) && + test-svn-fe -d preimage clear.noisy $len && + test_cmp empty actual +' + +test_expect_success 'reject variable-length int in magic' ' + printf "SVNRQ" | tr R "\200" | q_to_nul >clear.badmagic && + test_must_fail test-svn-fe -d preimage clear.badmagic 5 +' + +test_expect_success 'reject truncated integer' ' + printf "SVNQ%s%s" "QQQQQ" "QQQQRRQ" | + tr R "\200" | + q_to_nul >clear.fullint && + printf "SVNQ%s%s" "QQQQQ" "QQQQRR" | + tr RT "\201" | + q_to_nul >clear.partialint && + test_must_fail test-svn-fe -d preimage clear.fullint 15 && + test-svn-fe -d preimage clear.fullint 16 && + test_must_fail test-svn-fe -d preimage clear.partialint 15 +' + +test_expect_success 'nonempty (but unused) preimage view' ' + printf "SVNQ%b" "Q\003QQQ" | q_to_nul >clear.readpreimage && + test-svn-fe -d preimage clear.readpreimage 9 >actual && + test_cmp empty actual +' + +test_expect_success 'preimage view: right endpoint cannot backtrack' ' + printf "SVNQ%b%b" "Q\003QQQ" "Q\002QQQ" | + q_to_nul >clear.backtrack && + test_must_fail test-svn-fe -d preimage clear.backtrack 14 +' + +test_expect_success 'preimage view: left endpoint can advance' ' + printf "SVNQ%b%b" "Q\003QQQ" "\001\002QQQ" | + q_to_nul >clear.preshrink && + printf "SVNQ%b%b" "Q\003QQQ" "\001\001QQQ" | + q_to_nul >clear.shrinkbacktrack && + test-svn-fe -d preimage clear.preshrink 14 >actual && + test_must_fail test-svn-fe -d preimage clear.shrinkbacktrack 14 && + test_cmp empty actual +' + +test_expect_success 'preimage view: offsets compared by value' ' + printf "SVNQ%b%b" "\001\001QQQ" "\0200Q\003QQQ" | + q_to_nul >clear.noisybacktrack && + printf "SVNQ%b%b" "\001\001QQQ" "\0200\001\002QQQ" | + q_to_nul >clear.noisyadvance && + test_must_fail test-svn-fe -d preimage clear.noisybacktrack 15 && + test-svn-fe -d preimage clear.noisyadvance 15 && + test_cmp empty actual +' + +test_expect_success 'preimage view: reject truncated preimage' ' + printf "SVNQ%b" "\010QQQQ" | q_to_nul >clear.lateemptyread && + printf "SVNQ%b" "\010\001QQQ" | q_to_nul >clear.latenonemptyread && + printf "SVNQ%b" "\001\010QQQ" | q_to_nul >clear.longread && + test_must_fail test-svn-fe -d preimage clear.lateemptyread 9 && + test_must_fail test-svn-fe -d preimage clear.latenonemptyread 9 && + test_must_fail test-svn-fe -d preimage clear.longread 9 +' + +test_expect_success 'forbid unconsumed inline data' ' + printf "SVNQ%b%s%b%s" "QQQQ\003" "bar" "QQQQ\001" "x" | + q_to_nul >inline.clear && + test_must_fail test-svn-fe -d preimage inline.clear 18 >actual +' + +test_expect_success 'reject truncated inline data' ' + printf "SVNQ%b%s" "QQQQ\003" "b" | q_to_nul >inline.trunc && + test_must_fail test-svn-fe -d preimage inline.trunc 10 +' + +test_expect_success 'reject truncated inline data (after instruction section)' ' + printf "SVNQ%b%b%s" "QQ\001\001\003" "\0201" "b" | q_to_nul >insn.trunc && + test_must_fail test-svn-fe -d preimage insn.trunc 11 +' + +test_expect_success 'copyfrom_data' ' + echo hi >expect && + printf "SVNQ%b%b%b" "QQ\003\001\003" "\0203" "hi\n" | q_to_nul >copydat && + test-svn-fe -d preimage copydat 13 >actual && + test_cmp expect actual +' + +test_expect_success 'multiple copyfrom_data' ' + echo hi >expect && + printf "SVNQ%b%b%b%b%b" "QQ\003\002\003" "\0201\0202" "hi\n" \ + "QQQ\002Q" "\0200Q" | q_to_nul >copy.multi && + len=$(wc -c <copy.multi) && + test-svn-fe -d preimage copy.multi $len >actual && + test_cmp expect actual +' + +test_expect_success 'incomplete multiple insn' ' + printf "SVNQ%b%b%b" "QQ\003\002\003" "\0203\0200" "hi\n" | + q_to_nul >copy.partial && + len=$(wc -c <copy.partial) && + test_must_fail test-svn-fe -d preimage copy.partial $len +' + +test_expect_success 'catch attempt to copy missing data' ' + printf "SVNQ%b%b%s%b%s" "QQ\002\002\001" "\0201\0201" "X" \ + "QQQQ\002" "YZ" | + q_to_nul >copy.incomplete && + len=$(wc -c <copy.incomplete) && + test_must_fail test-svn-fe -d preimage copy.incomplete $len +' + +test_expect_success 'copyfrom target to repeat data' ' + printf foofoo >expect && + printf "SVNQ%b%b%s" "QQ\006\004\003" "\0203\0100\003Q" "foo" | + q_to_nul >copytarget.repeat && + len=$(wc -c <copytarget.repeat) && + test-svn-fe -d preimage copytarget.repeat $len >actual && + test_cmp expect actual +' + +test_expect_success 'copyfrom target out of order' ' + printf foooof >expect && + printf "SVNQ%b%b%s" \ + "QQ\006\007\003" "\0203\0101\002\0101\001\0101Q" "foo" | + q_to_nul >copytarget.reverse && + len=$(wc -c <copytarget.reverse) && + test-svn-fe -d preimage copytarget.reverse $len >actual && + test_cmp expect actual +' + +test_expect_success 'catch copyfrom future' ' + printf "SVNQ%b%b%s" "QQ\004\004\003" "\0202\0101\002\0201" "XYZ" | + q_to_nul >copytarget.infuture && + len=$(wc -c <copytarget.infuture) && + test_must_fail test-svn-fe -d preimage copytarget.infuture $len +' + +test_expect_success 'copy to sustain' ' + printf XYXYXYXYXYXZ >expect && + printf "SVNQ%b%b%s" "QQ\014\004\003" "\0202\0111Q\0201" "XYZ" | + q_to_nul >copytarget.sustain && + len=$(wc -c <copytarget.sustain) && + test-svn-fe -d preimage copytarget.sustain $len >actual && + test_cmp expect actual +' + +test_expect_success 'catch copy that overflows' ' + printf "SVNQ%b%b%s" "QQ\003\003\001" "\0201\0177Q" X | + q_to_nul >copytarget.overflow && + len=$(wc -c <copytarget.overflow) && + test_must_fail test-svn-fe -d preimage copytarget.overflow $len +' + +test_expect_success 'copyfrom source' ' + printf foo >expect && + printf "SVNQ%b%b" "Q\003\003\002Q" "\003Q" | q_to_nul >copysource.all && + test-svn-fe -d preimage copysource.all 11 >actual && + test_cmp expect actual +' + +test_expect_success 'copy backwards' ' + printf oof >expect && + printf "SVNQ%b%b" "Q\003\003\006Q" "\001\002\001\001\001Q" | + q_to_nul >copysource.rev && + test-svn-fe -d preimage copysource.rev 15 >actual && + test_cmp expect actual +' + +test_expect_success 'offsets are relative to window' ' + printf fo >expect && + printf "SVNQ%b%b%b%b" "Q\003\001\002Q" "\001Q" \ + "\002\001\001\002Q" "\001Q" | + q_to_nul >copysource.two && + test-svn-fe -d preimage copysource.two 18 >actual && + test_cmp expect actual +' + +test_expect_success 'example from notes/svndiff' ' + printf aaaaccccdddddddd >expect && + printf aaaabbbbcccc >source && + printf "SVNQ%b%b%s" "Q\014\020\007\001" \ + "\004Q\004\010\0201\0107\010" d | + q_to_nul >delta.example && + len=$(wc -c <delta.example) && + test-svn-fe -d source delta.example $len >actual && + test_cmp expect actual +' + +test_done diff --git a/t/t9020-remote-svn.sh b/t/t9020-remote-svn.sh new file mode 100755 index 0000000000..4d81ba1c2c --- /dev/null +++ b/t/t9020-remote-svn.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +test_description='tests remote-svn' + +. ./test-lib.sh + +MARKSPATH=.git/info/fast-import/remote-svn + +if ! test_have_prereq PYTHON +then + skip_all='skipping remote-svn tests, python not available' + test_done +fi + +# Override svnrdump with our simulator +PATH="$HOME:$PATH" +export PATH PYTHON_PATH GIT_BUILD_DIR + +write_script "$HOME/svnrdump" <<\EOF +exec "$PYTHON_PATH" "$GIT_BUILD_DIR/contrib/svn-fe/svnrdump_sim.py" "$@" +EOF + +init_git () { + rm -fr .git && + git init && + #git remote add svnsim testsvn::sim:///$TEST_DIRECTORY/t9020/example.svnrdump + # let's reuse an existing dump file!? + git remote add svnsim testsvn::sim://$TEST_DIRECTORY/t9154/svn.dump + git remote add svnfile testsvn::file://$TEST_DIRECTORY/t9154/svn.dump +} + +if test -e "$GIT_BUILD_DIR/git-remote-testsvn" +then + test_set_prereq REMOTE_SVN +fi + +test_debug ' + git --version + type git + type svnrdump +' + +test_expect_success REMOTE_SVN 'simple fetch' ' + init_git && + git fetch svnsim && + test_cmp .git/refs/svn/svnsim/master .git/refs/remotes/svnsim/master && + cp .git/refs/remotes/svnsim/master master.good +' + +test_debug ' + cat .git/refs/svn/svnsim/master + cat .git/refs/remotes/svnsim/master +' + +test_expect_success REMOTE_SVN 'repeated fetch, nothing shall change' ' + git fetch svnsim && + test_cmp master.good .git/refs/remotes/svnsim/master +' + +test_expect_success REMOTE_SVN 'fetch from a file:// url gives the same result' ' + git fetch svnfile +' + +test_expect_failure REMOTE_SVN 'the sha1 differ because the git-svn-id line in the commit msg contains the url' ' + test_cmp .git/refs/remotes/svnfile/master .git/refs/remotes/svnsim/master +' + +test_expect_success REMOTE_SVN 'mark-file regeneration' ' + # filter out any other marks, that can not be regenerated. Only up to 3 digit revisions are allowed here + grep ":[0-9]\{1,3\} " $MARKSPATH/svnsim.marks > $MARKSPATH/svnsim.marks.old && + rm $MARKSPATH/svnsim.marks && + git fetch svnsim && + test_cmp $MARKSPATH/svnsim.marks.old $MARKSPATH/svnsim.marks +' + +test_expect_success REMOTE_SVN 'incremental imports must lead to the same head' ' + SVNRMAX=3 && + export SVNRMAX && + init_git && + git fetch svnsim && + test_cmp .git/refs/svn/svnsim/master .git/refs/remotes/svnsim/master && + unset SVNRMAX && + git fetch svnsim && + test_cmp master.good .git/refs/remotes/svnsim/master +' + +test_debug 'git branch -a' + +test_done diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh index b041516a1d..4fea8d901b 100755 --- a/t/t9100-git-svn-basic.sh +++ b/t/t9100-git-svn-basic.sh @@ -65,7 +65,8 @@ test_expect_success "$name" " git update-index --add dir/file/file && git commit -m '$name' && test_must_fail git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch" || true + ${remotes_git_svn}..mybranch +" name='detect node change from directory to file #1' @@ -79,7 +80,8 @@ test_expect_success "$name" ' git update-index --add -- bar && git commit -m "$name" && test_must_fail git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch2' || true + ${remotes_git_svn}..mybranch2 +' name='detect node change from file to directory #2' @@ -92,9 +94,12 @@ test_expect_success "$name" ' echo yyy > bar/zzz/yyy && git update-index --add bar/zzz/yyy && git commit -m "$name" && - test_must_fail git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch3' || true - + git svn set-tree --find-copies-harder --rmdir \ + ${remotes_git_svn}..mybranch3 && + svn_cmd up "$SVN_TREE" && + test -d "$SVN_TREE"/bar/zzz && + test -e "$SVN_TREE"/bar/zzz/yyy +' name='detect node change from directory to file #2' test_expect_success "$name" ' @@ -107,7 +112,8 @@ test_expect_success "$name" ' git update-index --add -- dir && git commit -m "$name" && test_must_fail git svn set-tree --find-copies-harder --rmdir \ - ${remotes_git_svn}..mybranch4' || true + ${remotes_git_svn}..mybranch4 +' name='remove executable bit from a file' @@ -134,10 +140,10 @@ test_expect_success "$name" ' test -x "$SVN_TREE"/exec.sh' -name='executable file becomes a symlink to bar/zzz (file)' +name='executable file becomes a symlink to file' test_expect_success "$name" ' rm exec.sh && - ln -s bar/zzz exec.sh && + ln -s file exec.sh && git update-index exec.sh && git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ @@ -148,19 +154,19 @@ test_expect_success "$name" ' name='new symlink is added to a file that was also just made executable' test_expect_success "$name" ' - chmod +x bar/zzz && - ln -s bar/zzz exec-2.sh && - git update-index --add bar/zzz exec-2.sh && + chmod +x file && + ln -s file exec-2.sh && + git update-index --add file exec-2.sh && git commit -m "$name" && git svn set-tree --find-copies-harder --rmdir \ ${remotes_git_svn}..mybranch5 && svn_cmd up "$SVN_TREE" && - test -x "$SVN_TREE"/bar/zzz && + test -x "$SVN_TREE"/file && test -h "$SVN_TREE"/exec-2.sh' name='modify a symlink to become a file' test_expect_success "$name" ' - echo git help > help || true && + echo git help >help && rm exec-2.sh && cp help exec-2.sh && git update-index exec-2.sh && @@ -195,14 +201,15 @@ name='check imported tree checksums expected tree checksums' rm -f expected if test_have_prereq UTF8 then - echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected + echo tree dc68b14b733e4ec85b04ab6f712340edc5dc936e > expected fi cat >> expected <<\EOF -tree 83654bb36f019ae4fe77a0171f81075972087624 -tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1 -tree 0b094cbff17168f24c302e297f55bfac65eb8bd3 -tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e -tree 56a30b966619b863674f5978696f4a3594f2fca9 +tree c3322890dcf74901f32d216f05c5044f670ce632 +tree d3ccd5035feafd17b030c5732e7808cc49122853 +tree d03e1630363d4881e68929d532746b20b0986b83 +tree 149d63cd5878155c846e8c55d7d8487de283f89e +tree 312b76e4f64ce14893aeac8591eb3960b065e247 +tree 149d63cd5878155c846e8c55d7d8487de283f89e tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4 EOF @@ -299,5 +306,13 @@ test_expect_success 'git-svn works in a bare repository' ' git svn fetch ) && rm -rf bare-repo ' +test_expect_success 'git-svn works in in a repository with a gitdir: link' ' + mkdir worktree gitdir && + ( cd worktree && + git svn init "$svnrepo" && + git init --separate-git-dir ../gitdir && + git svn fetch ) && + rm -rf worktree gitdir + ' test_done diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh index 289fc313fb..ee73013eed 100755 --- a/t/t9107-git-svn-migrate.sh +++ b/t/t9107-git-svn-migrate.sh @@ -27,15 +27,17 @@ test_expect_success 'setup old-looking metadata' ' head=`git rev-parse --verify refs/heads/git-svn-HEAD^0` test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'" +svnrepo_escaped=`echo $svnrepo | sed 's/ /%20/'` + test_expect_success 'initialize old-style (v0) git svn layout' ' mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info && echo "$svnrepo" > "$GIT_DIR"/git-svn/info/url && echo "$svnrepo" > "$GIT_DIR"/svn/info/url && git svn migrate && - ! test -d "$GIT_DIR"/git svn && + ! test -d "$GIT_DIR"/git-svn && git rev-parse --verify refs/${remotes_git_svn}^0 && git rev-parse --verify refs/remotes/svn^0 && - test "$(git config --get svn-remote.svn.url)" = "$svnrepo" && + test "$(git config --get svn-remote.svn.url)" = "$svnrepo_escaped" && test `git config --get svn-remote.svn.fetch` = \ ":refs/${remotes_git_svn}" ' diff --git a/t/t9112-git-svn-md5less-file.sh b/t/t9112-git-svn-md5less-file.sh index a61d6716d2..9861c719f8 100755 --- a/t/t9112-git-svn-md5less-file.sh +++ b/t/t9112-git-svn-md5less-file.sh @@ -7,7 +7,7 @@ test_description='test that git handles an svn repository with missing md5sums' # Loading a node from a svn dumpfile without a Text-Content-Length # field causes svn to neglect to store or report an md5sum. (it will # calculate one if you had put Text-Content-Length: 0). This showed -# up in a repository creted with cvs2svn. +# up in a repository created with cvs2svn. cat > dumpfile.svn <<EOF SVN-fs-dump-format-version: 1 diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh index 3077851015..d33d714006 100755 --- a/t/t9114-git-svn-dcommit-merge.sh +++ b/t/t9114-git-svn-dcommit-merge.sh @@ -48,7 +48,7 @@ test_expect_success 'setup svn repository' ' test_expect_success 'setup git mirror and merge' ' git svn init "$svnrepo" -t tags -T trunk -b branches && git svn fetch && - git checkout --track -b svn remotes/trunk && + git checkout -b svn remotes/trunk && git checkout -b merge && echo new file > new_file && git add new_file && @@ -62,7 +62,7 @@ test_expect_success 'setup git mirror and merge' ' echo friend > README && cat tmp >> README && git commit -a -m "friend" && - git pull . merge + git merge merge ' test_debug 'gitk --all & sleep 1' diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh index b7ef9e2589..69e9c0db5d 100755 --- a/t/t9117-git-svn-init-clone.sh +++ b/t/t9117-git-svn-init-clone.sh @@ -52,4 +52,71 @@ test_expect_success 'clone to target directory with --stdlayout' ' rm -rf target ' +test_expect_success 'init without -s/-T/-b/-t does not warn' ' + test ! -d trunk && + git svn init "$svnrepo"/project/trunk trunk 2>warning && + test_must_fail grep -q prefix warning && + rm -rf trunk && + rm -f warning + ' + +test_expect_success 'clone without -s/-T/-b/-t does not warn' ' + test ! -d trunk && + git svn clone "$svnrepo"/project/trunk 2>warning && + test_must_fail grep -q prefix warning && + rm -rf trunk && + rm -f warning + ' + +test_svn_configured_prefix () { + prefix=$1 && + cat >expect <<EOF && +project/trunk:refs/remotes/${prefix}trunk +project/branches/*:refs/remotes/${prefix}* +project/tags/*:refs/remotes/${prefix}tags/* +EOF + test ! -f actual && + git --git-dir=project/.git config svn-remote.svn.fetch >>actual && + git --git-dir=project/.git config svn-remote.svn.branches >>actual && + git --git-dir=project/.git config svn-remote.svn.tags >>actual && + test_cmp expect actual && + rm -f expect actual +} + +test_expect_success 'init with -s/-T/-b/-t without --prefix warns' ' + test ! -d project && + git svn init -s "$svnrepo"/project project 2>warning && + grep -q prefix warning && + test_svn_configured_prefix "" && + rm -rf project && + rm -f warning + ' + +test_expect_success 'clone with -s/-T/-b/-t without --prefix warns' ' + test ! -d project && + git svn clone -s "$svnrepo"/project 2>warning && + grep -q prefix warning && + test_svn_configured_prefix "" && + rm -rf project && + rm -f warning + ' + +test_expect_success 'init with -s/-T/-b/-t and --prefix does not warn' ' + test ! -d project && + git svn init -s "$svnrepo"/project project --prefix="" 2>warning && + test_must_fail grep -q prefix warning && + test_svn_configured_prefix "" && + rm -rf project && + rm -f warning + ' + +test_expect_success 'clone with -s/-T/-b/-t and --prefix does not warn' ' + test ! -d project && + git svn clone -s "$svnrepo"/project --prefix="" 2>warning && + test_must_fail grep -q prefix warning && + test_svn_configured_prefix "" && + rm -rf project && + rm -f warning + ' + test_done diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh index 63fc982c8c..15f93b4c28 100755 --- a/t/t9118-git-svn-funky-branch-names.sh +++ b/t/t9118-git-svn-funky-branch-names.sh @@ -28,10 +28,15 @@ test_expect_success 'setup svnrepo' ' svn_cmd cp -m "trailing .lock" "$svnrepo/pr ject/trunk" \ "$svnrepo/pr ject/branches/trailing_dotlock.lock" && svn_cmd cp -m "reflog" "$svnrepo/pr ject/trunk" \ - "$svnrepo/pr ject/branches/not-a%40{0}reflog" && + "$svnrepo/pr ject/branches/not-a@{0}reflog@" && start_httpd ' +# SVN 1.7 will truncate "not-a%40{0]" to just "not-a". +# Look at what SVN wound up naming the branch and use that. +# Be sure to escape the @ if it shows up. +non_reflog=`svn_cmd ls "$svnrepo/pr ject/branches" | grep not-a | sed 's/\///' | sed 's/@/%40/'` + test_expect_success 'test clone with funky branch names' ' git svn clone -s "$svnrepo/pr ject" project && ( @@ -42,7 +47,7 @@ test_expect_success 'test clone with funky branch names' ' git rev-parse "refs/remotes/%2Eleading_dot" && git rev-parse "refs/remotes/trailing_dot%2E" && git rev-parse "refs/remotes/trailing_dotlock%2Elock" && - git rev-parse "refs/remotes/not-a%40{0}reflog" + git rev-parse "refs/remotes/$non_reflog" ) ' diff --git a/t/t9147-git-svn-include-paths.sh b/t/t9147-git-svn-include-paths.sh new file mode 100755 index 0000000000..a90ff58629 --- /dev/null +++ b/t/t9147-git-svn-include-paths.sh @@ -0,0 +1,149 @@ +#!/bin/sh +# +# Copyright (c) 2013 Paul Walmsley - based on t9134 by Vitaly Shukela +# + +test_description='git svn property tests' +. ./lib-git-svn.sh + +test_expect_success 'setup test repository' ' + svn_cmd co "$svnrepo" s && + ( + cd s && + mkdir qqq www xxx && + echo test_qqq > qqq/test_qqq.txt && + echo test_www > www/test_www.txt && + echo test_xxx > xxx/test_xxx.txt && + svn_cmd add qqq && + svn_cmd add www && + svn_cmd add xxx && + svn_cmd commit -m "create some files" && + svn_cmd up && + echo hi >> www/test_www.txt && + svn_cmd commit -m "modify www/test_www.txt" && + svn_cmd up + ) +' + +test_expect_success 'clone an SVN repository with filter to include qqq directory' ' + git svn clone --include-paths="qqq" "$svnrepo" g && + echo test_qqq > expect && + for i in g/*/*.txt; do cat $i >> expect2; done && + test_cmp expect expect2 +' + + +test_expect_success 'init+fetch an SVN repository with included qqq directory' ' + git svn init "$svnrepo" c && + ( cd c && git svn fetch --include-paths="qqq" ) && + rm expect2 && + echo test_qqq > expect && + for i in c/*/*.txt; do cat $i >> expect2; done && + test_cmp expect expect2 +' + +test_expect_success 'verify include-paths config saved by clone' ' + ( + cd g && + git config --get svn-remote.svn.include-paths | fgrep "qqq" + ) +' + +test_expect_success 'SVN-side change outside of www' ' + ( + cd s && + echo b >> qqq/test_qqq.txt && + svn_cmd commit -m "SVN-side change outside of www" && + svn_cmd up && + svn_cmd log -v | fgrep "SVN-side change outside of www" + ) +' + +test_expect_success 'update git svn-cloned repo (config include)' ' + ( + cd g && + git svn rebase && + printf "test_qqq\nb\n" > expect && + for i in */*.txt; do cat $i >> expect2; done && + test_cmp expect2 expect && + rm expect expect2 + ) +' + +test_expect_success 'update git svn-cloned repo (option include)' ' + ( + cd c && + git svn rebase --include-paths="qqq" && + printf "test_qqq\nb\n" > expect && + for i in */*.txt; do cat $i >> expect2; done && + test_cmp expect2 expect && + rm expect expect2 + ) +' + +test_expect_success 'SVN-side change inside of ignored www' ' + ( + cd s && + echo zaq >> www/test_www.txt + svn_cmd commit -m "SVN-side change inside of www/test_www.txt" && + svn_cmd up && + svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt" + ) +' + +test_expect_success 'update git svn-cloned repo (config include)' ' + ( + cd g && + git svn rebase && + printf "test_qqq\nb\n" > expect && + for i in */*.txt; do cat $i >> expect2; done && + test_cmp expect2 expect && + rm expect expect2 + ) +' + +test_expect_success 'update git svn-cloned repo (option include)' ' + ( + cd c && + git svn rebase --include-paths="qqq" && + printf "test_qqq\nb\n" > expect && + for i in */*.txt; do cat $i >> expect2; done && + test_cmp expect2 expect && + rm expect expect2 + ) +' + +test_expect_success 'SVN-side change in and out of included qqq' ' + ( + cd s && + echo cvf >> www/test_www.txt + echo ygg >> qqq/test_qqq.txt + svn_cmd commit -m "SVN-side change in and out of ignored www" && + svn_cmd up && + svn_cmd log -v | fgrep "SVN-side change in and out of ignored www" + ) +' + +test_expect_success 'update git svn-cloned repo again (config include)' ' + ( + cd g && + git svn rebase && + printf "test_qqq\nb\nygg\n" > expect && + for i in */*.txt; do cat $i >> expect2; done && + test_cmp expect2 expect && + rm expect expect2 + ) +' + +test_expect_success 'update git svn-cloned repo again (option include)' ' + ( + cd c && + git svn rebase --include-paths="qqq" && + printf "test_qqq\nb\nygg\n" > expect && + for i in */*.txt; do cat $i >> expect2; done && + test_cmp expect2 expect && + rm expect expect2 + ) +' + +test_done diff --git a/t/t9154-git-svn-fancy-glob.sh b/t/t9154-git-svn-fancy-glob.sh index a6a56a6cb9..b780e0efe3 100755 --- a/t/t9154-git-svn-fancy-glob.sh +++ b/t/t9154-git-svn-fancy-glob.sh @@ -21,6 +21,15 @@ test_expect_success 'add red branch' " test_must_fail git rev-parse refs/remotes/blue " +test_expect_success 'add gre branch' " + GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev && + git config svn-remote.svn.branches 'branches/{red,gre}:refs/remotes/*' && + git svn fetch && + git rev-parse refs/remotes/red && + test_must_fail git rev-parse refs/remotes/green && + test_must_fail git rev-parse refs/remotes/blue + " + test_expect_success 'add green branch' " GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev && git config svn-remote.svn.branches 'branches/{red,green}:refs/remotes/*' && diff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh index 6ef0c0bde3..1eab7015c7 100755 --- a/t/t9161-git-svn-mergeinfo-push.sh +++ b/t/t9161-git-svn-mergeinfo-push.sh @@ -88,7 +88,6 @@ test_expect_success 'check reintegration mergeinfo' ' test "$mergeinfo" = "/branches/svnb1:2-4,7-9,13-18 /branches/svnb2:3,8,16-17 /branches/svnb3:4,9 -/branches/svnb4:5-6,10-12 /branches/svnb5:6,11" ' diff --git a/t/t9163-git-svn-reset-clears-caches.sh b/t/t9163-git-svn-reset-clears-caches.sh new file mode 100755 index 0000000000..cd4c662ba2 --- /dev/null +++ b/t/t9163-git-svn-reset-clears-caches.sh @@ -0,0 +1,78 @@ +#!/bin/sh +# +# Copyright (c) 2012 Peter Baumann +# + +test_description='git svn reset clears memoized caches' +. ./lib-git-svn.sh + +svn_ver="$(svn --version --quiet)" +case $svn_ver in +0.* | 1.[0-4].*) + skip_all="skipping git-svn test - SVN too old ($svn_ver)" + test_done + ;; +esac + +# ... a - b - m <- trunk +# \ / +# ... c <- branch1 +# +# SVN Commits not interesting for this test are abbreviated with "..." +# +test_expect_success 'initialize source svn repo' ' + svn_cmd mkdir -m "create trunk" "$svnrepo"/trunk && + svn_cmd mkdir -m "create branches" "$svnrepo/branches" && + svn_cmd co "$svnrepo"/trunk "$SVN_TREE" && + ( + cd "$SVN_TREE" && + touch foo && + svn_cmd add foo && + svn_cmd commit -m "a" && + svn_cmd cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 && + svn_cmd switch "$svnrepo"/branches/branch1 && + touch bar && + svn_cmd add bar && + svn_cmd commit -m b && + svn_cmd switch "$svnrepo"/trunk && + touch baz && + svn_cmd add baz && + svn_cmd commit -m c && + svn_cmd up && + svn_cmd merge "$svnrepo"/branches/branch1 && + svn_cmd commit -m "m" + ) && + rm -rf "$SVN_TREE" +' + +test_expect_success 'fetch to merge-base (a)' ' + git svn init -s "$svnrepo" && + git svn fetch --revision BASE:3 +' + +# git svn rebase looses the merge commit +# +# ... a - b - m <- trunk +# \ +# ... c +# +test_expect_success 'rebase looses SVN merge (m)' ' + git svn rebase && + git svn fetch && + test 1 = $(git cat-file -p master|grep parent|wc -l) +' + +# git svn fetch creates correct history with merge commit +# +# ... a - b - m <- trunk +# \ / +# ... c <- branch1 +# +test_expect_success 'reset and fetch gets the SVN merge (m) correctly' ' + git svn reset -r 3 && + git reset --hard trunk && + git svn fetch && + test 2 = $(git cat-file -p trunk|grep parent|wc -l) +' + +test_done diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh new file mode 100755 index 0000000000..d8464d4218 --- /dev/null +++ b/t/t9164-git-svn-dcommit-concurrent.sh @@ -0,0 +1,216 @@ +#!/bin/sh +# +# Copyright (c) 2012 Robert Luberda +# + +test_description='concurrent git svn dcommit' +. ./lib-git-svn.sh + + + +test_expect_success 'setup svn repository' ' + svn_cmd checkout "$svnrepo" work.svn && + ( + cd work.svn && + echo >file && echo > auto_updated_file + svn_cmd add file auto_updated_file && + svn_cmd commit -m "initial commit" + ) && + svn_cmd checkout "$svnrepo" work-auto-commits.svn +' +N=0 +next_N() +{ + N=$(( $N + 1 )) +} + +# Setup SVN repository hooks to emulate SVN failures or concurrent commits +# The function adds +# either pre-commit hook, which causes SVN commit given in second argument +# to fail +# or post-commit hook, which creates a new commit (a new line added to +# auto_updated_file) after given SVN commit +# The first argument contains a type of the hook +# The second argument contains a number (not SVN revision) of commit +# the hook should be applied for (each time the hook is run, the given +# number is decreased by one until it gets 0, in which case the hook +# will execute its real action) +setup_hook() +{ + hook_type="$1" # "pre-commit" or "post-commit" + skip_revs="$2" + [ "$hook_type" = "pre-commit" ] || + [ "$hook_type" = "post-commit" ] || + { echo "ERROR: invalid argument ($hook_type)" \ + "passed to setup_hook" >&2 ; return 1; } + echo "cnt=$skip_revs" > "$hook_type-counter" + rm -f "$rawsvnrepo/hooks/"*-commit # drop previous hooks + hook="$rawsvnrepo/hooks/$hook_type" + cat > "$hook" <<- 'EOF1' + #!/bin/sh + set -e + cd "$1/.." # "$1" is repository location + exec >> svn-hook.log 2>&1 + hook="$(basename "$0")" + echo "*** Executing $hook $@" + set -x + . ./$hook-counter + cnt="$(($cnt - 1))" + echo "cnt=$cnt" > ./$hook-counter + [ "$cnt" = "0" ] || exit 0 +EOF1 + if [ "$hook_type" = "pre-commit" ]; then + echo "echo 'commit disallowed' >&2; exit 1" >>"$hook" + else + echo "PATH=\"$PATH\"; export PATH" >>"$hook" + echo "svnconf=\"$svnconf\"" >>"$hook" + cat >>"$hook" <<- 'EOF2' + cd work-auto-commits.svn + svn up --config-dir "$svnconf" + echo "$$" >> auto_updated_file + svn commit --config-dir "$svnconf" \ + -m "auto-committing concurrent change" + exit 0 +EOF2 + fi + chmod 755 "$hook" +} + +check_contents() +{ + gitdir="$1" + (cd ../work.svn && svn_cmd up) && + test_cmp file ../work.svn/file && + test_cmp auto_updated_file ../work.svn/auto_updated_file +} + +test_expect_success 'check if post-commit hook creates a concurrent commit' ' + setup_hook post-commit 1 && + ( + cd work.svn && + cp auto_updated_file au_file_saved && + echo 1 >> file && + svn_cmd commit -m "changing file" && + svn_cmd up && + test_must_fail test_cmp auto_updated_file au_file_saved + ) +' + +test_expect_success 'check if pre-commit hook fails' ' + setup_hook pre-commit 2 && + ( + cd work.svn && + echo 2 >> file && + svn_cmd commit -m "changing file once again" && + echo 3 >> file && + test_must_fail svn_cmd commit -m "this commit should fail" && + svn_cmd revert file + ) +' + +test_expect_success 'dcommit error handling' ' + setup_hook pre-commit 2 && + next_N && git svn clone "$svnrepo" work$N.git && + ( + cd work$N.git && + echo 1 >> file && git commit -am "commit change $N.1" && + echo 2 >> file && git commit -am "commit change $N.2" && + echo 3 >> file && git commit -am "commit change $N.3" && + # should fail to dcommit 2nd and 3rd change + # but still should leave the repository in reasonable state + test_must_fail git svn dcommit && + git update-index --refresh && + git show HEAD~2 | grep -q git-svn-id && + ! git show HEAD~1 | grep -q git-svn-id && + ! git show HEAD | grep -q git-svn-id + ) +' + +test_expect_success 'dcommit concurrent change in non-changed file' ' + setup_hook post-commit 2 && + next_N && git svn clone "$svnrepo" work$N.git && + ( + cd work$N.git && + echo 1 >> file && git commit -am "commit change $N.1" && + echo 2 >> file && git commit -am "commit change $N.2" && + echo 3 >> file && git commit -am "commit change $N.3" && + # should rebase and leave the repository in reasonable state + git svn dcommit && + git update-index --refresh && + check_contents && + git show HEAD~3 | grep -q git-svn-id && + git show HEAD~2 | grep -q git-svn-id && + git show HEAD~1 | grep -q auto-committing && + git show HEAD | grep -q git-svn-id + ) +' + +# An utility function used in the following test +delete_first_line() +{ + file="$1" && + sed 1d < "$file" > "${file}.tmp" && + rm "$file" && + mv "${file}.tmp" "$file" +} + +test_expect_success 'dcommit concurrent non-conflicting change' ' + setup_hook post-commit 2 && + next_N && git svn clone "$svnrepo" work$N.git && + ( + cd work$N.git && + cat file >> auto_updated_file && + git commit -am "commit change $N.1" && + delete_first_line auto_updated_file && + git commit -am "commit change $N.2" && + delete_first_line auto_updated_file && + git commit -am "commit change $N.3" && + # should rebase and leave the repository in reasonable state + git svn dcommit && + git update-index --refresh && + check_contents && + git show HEAD~3 | grep -q git-svn-id && + git show HEAD~2 | grep -q git-svn-id && + git show HEAD~1 | grep -q auto-committing && + git show HEAD | grep -q git-svn-id + ) +' + +test_expect_success 'dcommit --no-rebase concurrent non-conflicting change' ' + setup_hook post-commit 2 && + next_N && git svn clone "$svnrepo" work$N.git && + ( + cd work$N.git && + cat file >> auto_updated_file && + git commit -am "commit change $N.1" && + delete_first_line auto_updated_file && + git commit -am "commit change $N.2" && + delete_first_line auto_updated_file && + git commit -am "commit change $N.3" && + # should fail as rebase is needed + test_must_fail git svn dcommit --no-rebase && + # but should leave HEAD unchanged + git update-index --refresh && + ! git show HEAD~2 | grep -q git-svn-id && + ! git show HEAD~1 | grep -q git-svn-id && + ! git show HEAD | grep -q git-svn-id + ) +' + +test_expect_success 'dcommit fails on concurrent conflicting change' ' + setup_hook post-commit 1 && + next_N && git svn clone "$svnrepo" work$N.git && + ( + cd work$N.git && + echo a >> file && + git commit -am "commit change $N.1" && + echo b >> auto_updated_file && + git commit -am "commit change $N.2" && + echo c >> auto_updated_file && + git commit -am "commit change $N.3" && + test_must_fail git svn dcommit && # rebase should fail + test_must_fail git update-index --refresh + ) +' + +test_done diff --git a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh new file mode 100755 index 0000000000..13ae7e33f9 --- /dev/null +++ b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh @@ -0,0 +1,60 @@ +#!/bin/sh +# +# Copyright (c) 2012 Steven Walter +# + +test_description='git svn merge detection' +. ./lib-git-svn.sh + +svn_ver="$(svn --version --quiet)" +case $svn_ver in +0.* | 1.[0-4].*) + skip_all="skipping git-svn test - SVN too old ($svn_ver)" + test_done + ;; +esac + +test_expect_success 'initialize source svn repo' ' + svn_cmd mkdir -m x "$svnrepo"/trunk && + svn_cmd mkdir -m x "$svnrepo"/branches && + svn_cmd co "$svnrepo"/trunk "$SVN_TREE" && + ( + cd "$SVN_TREE" && + touch foo && + svn_cmd add foo && + svn_cmd commit -m "initial commit" && + svn_cmd cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 && + svn_cmd switch "$svnrepo"/branches/branch1 && + touch bar && + svn_cmd add bar && + svn_cmd commit -m branch1 && + svn_cmd cp -m branch "$svnrepo"/branches/branch1 "$svnrepo"/branches/branch2 && + svn_cmd switch "$svnrepo"/branches/branch2 && + touch baz && + svn_cmd add baz && + svn_cmd commit -m branch2 && + svn_cmd switch "$svnrepo"/trunk && + touch bar2 && + svn_cmd add bar2 && + svn_cmd commit -m trunk && + svn_cmd switch "$svnrepo"/branches/branch2 && + svn_cmd merge "$svnrepo"/trunk && + svn_cmd commit -m "merge trunk" + svn_cmd switch "$svnrepo"/trunk && + svn_cmd merge --reintegrate "$svnrepo"/branches/branch2 && + svn_cmd commit -m "merge branch2" + ) && + rm -rf "$SVN_TREE" +' + +test_expect_success 'clone svn repo' ' + git svn init -s "$svnrepo" && + git svn fetch +' + +test_expect_success 'verify merge commit' 'x=$(git rev-parse HEAD^2) && + y=$(git rev-parse branch2) && + test "x$x" = "x$y" +' + +test_done diff --git a/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh b/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh new file mode 100755 index 0000000000..af0ec0e2e3 --- /dev/null +++ b/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# +# Copyright (c) 2012 Steven Walter +# + +test_description='git svn merge detection' +. ./lib-git-svn.sh + +svn_ver="$(svn --version --quiet)" +case $svn_ver in +0.* | 1.[0-4].*) + skip_all="skipping git-svn test - SVN too old ($svn_ver)" + test_done + ;; +esac + +test_expect_success 'initialize source svn repo' ' + svn_cmd mkdir -m x "$svnrepo"/trunk && + svn_cmd mkdir -m x "$svnrepo"/branches && + svn_cmd co "$svnrepo"/trunk "$SVN_TREE" && + ( + cd "$SVN_TREE" && + touch foo && + svn_cmd add foo && + svn_cmd commit -m "initial commit" && + svn_cmd cp -m branch "$svnrepo"/trunk "$svnrepo"/branches/branch1 && + svn_cmd switch "$svnrepo"/branches/branch1 && + touch bar && + svn_cmd add bar && + svn_cmd commit -m branch1 && + svn_cmd cp -m branch "$svnrepo"/branches/branch1 "$svnrepo"/branches/branch2 && + svn_cmd switch "$svnrepo"/branches/branch2 && + touch baz && + svn_cmd add baz && + svn_cmd commit -m branch2 && + svn_cmd switch "$svnrepo"/trunk && + svn_cmd merge --reintegrate "$svnrepo"/branches/branch2 && + svn_cmd commit -m "merge branch2" + ) && + rm -rf "$SVN_TREE" +' + +test_expect_success 'clone svn repo' ' + git svn init -s "$svnrepo" && + git svn fetch +' + +test_expect_success 'verify merge commit' 'x=$(git rev-parse HEAD^2) && + y=$(git rev-parse branch2) && + test "x$x" = "x$y" +' + +test_done diff --git a/t/t9167-git-svn-cmd-branch-subproject.sh b/t/t9167-git-svn-cmd-branch-subproject.sh new file mode 100755 index 0000000000..53def876ed --- /dev/null +++ b/t/t9167-git-svn-cmd-branch-subproject.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# +# Copyright (c) 2013 Tobias Schulte +# + +test_description='git svn branch for subproject clones' +. ./lib-git-svn.sh + +test_expect_success 'initialize svnrepo' ' + mkdir import && + ( + cd import && + mkdir -p trunk/project branches tags && + ( + cd trunk/project && + echo foo > foo + ) && + svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null + ) && + rm -rf import && + svn_cmd co "$svnrepo"/trunk/project trunk/project && + ( + cd trunk/project && + echo bar >> foo && + svn_cmd ci -m "updated trunk" + ) && + rm -rf trunk +' + +test_expect_success 'import into git' ' + git svn init --trunk=trunk/project --branches=branches/*/project \ + --tags=tags/*/project "$svnrepo" && + git svn fetch && + git checkout remotes/trunk +' + +test_expect_success 'git svn branch tests' ' + test_must_fail git svn branch a && + git svn branch --parents a && + test_must_fail git svn branch -t tag1 && + git svn branch --parents -t tag1 && + test_must_fail git svn branch --tag tag2 && + git svn branch --parents --tag tag2 && + test_must_fail git svn tag tag3 && + git svn tag --parents tag3 +' + +test_done diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh index b59be9a894..3fb3368903 100755 --- a/t/t9200-git-cvsexportcommit.sh +++ b/t/t9200-git-cvsexportcommit.sh @@ -19,14 +19,15 @@ then test_done fi -CVSROOT=$PWD/cvsroot +CVSROOT=$PWD/tmpcvsroot CVSWORK=$PWD/cvswork GIT_DIR=$PWD/.git export CVSROOT CVSWORK GIT_DIR rm -rf "$CVSROOT" "$CVSWORK" -mkdir "$CVSROOT" && + cvs init && +test -d "$CVSROOT" && cvs -Q co -d "$CVSWORK" . && echo >empty && git add empty && diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index 0f5b5e5964..27263dfb80 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -24,6 +24,13 @@ head_c () { ' - "$1" } +verify_packs () { + for p in .git/objects/pack/*.pack + do + git verify-pack "$@" "$p" || return + done +} + file2_data='file2 second line of EOF' @@ -42,14 +49,6 @@ echo "$@"' >empty -test_expect_success 'setup: have pipes?' ' - rm -f frob && - if mkfifo frob - then - test_set_prereq PIPE - fi -' - ### ### series A ### @@ -105,9 +104,10 @@ test_expect_success \ 'A: create pack from stdin' \ 'git fast-import --export-marks=marks.out <input && git whatchanged master' -test_expect_success \ - 'A: verify pack' \ - 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done' + +test_expect_success 'A: verify pack' ' + verify_packs +' cat >expect <<EOF author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE @@ -252,9 +252,11 @@ test_expect_success \ 'A: verify marks import does not crash' \ 'git fast-import --import-marks=marks.out <input && git whatchanged verify--import-marks' -test_expect_success \ - 'A: verify pack' \ - 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done' + +test_expect_success 'A: verify pack' ' + verify_packs +' + cat >expect <<EOF :000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A copy-of-file2 EOF @@ -514,9 +516,11 @@ test_expect_success \ 'C: incremental import create pack from stdin' \ 'git fast-import <input && git whatchanged branch' -test_expect_success \ - 'C: verify pack' \ - 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done' + +test_expect_success 'C: verify pack' ' + verify_packs +' + test_expect_success \ 'C: validate reuse existing blob' \ 'test $newf = `git rev-parse --verify branch:file2/newf` && @@ -572,9 +576,10 @@ test_expect_success \ 'D: inline data in commit' \ 'git fast-import <input && git whatchanged branch' -test_expect_success \ - 'D: verify pack' \ - 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done' + +test_expect_success 'D: verify pack' ' + verify_packs +' cat >expect <<EOF :000000 100755 0000000000000000000000000000000000000000 35a59026a33beac1569b1c7f66f3090ce9c09afc A newdir/exec.sh @@ -618,9 +623,10 @@ test_expect_success 'E: rfc2822 date, --date-format=raw' ' test_expect_success \ 'E: rfc2822 date, --date-format=rfc2822' \ 'git fast-import --date-format=rfc2822 <input' -test_expect_success \ - 'E: verify pack' \ - 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done' + +test_expect_success 'E: verify pack' ' + verify_packs +' cat >expect <<EOF author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 1170778938 -0500 @@ -669,9 +675,10 @@ test_expect_success \ fi fi ' -test_expect_success \ - 'F: verify pack' \ - 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done' + +test_expect_success 'F: verify pack' ' + verify_packs +' cat >expect <<EOF tree `git rev-parse branch~1^{tree}` @@ -705,9 +712,11 @@ INPUT_END test_expect_success \ 'G: non-fast-forward update forced' \ 'git fast-import --force <input' -test_expect_success \ - 'G: verify pack' \ - 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done' + +test_expect_success 'G: verify pack' ' + verify_packs +' + test_expect_success \ 'G: branch changed, but logged' \ 'test $old_branch != `git rev-parse --verify branch^0` && @@ -742,9 +751,10 @@ test_expect_success \ 'H: deletall, add 1' \ 'git fast-import <input && git whatchanged H' -test_expect_success \ - 'H: verify pack' \ - 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done' + +test_expect_success 'H: verify pack' ' + verify_packs +' cat >expect <<EOF :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D file2/newf @@ -1021,6 +1031,32 @@ test_expect_success \ git diff-tree -M -r M3^ M3 >actual && compare_diff_raw expect actual' +cat >input <<INPUT_END +commit refs/heads/M4 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +rename root +COMMIT + +from refs/heads/M2^0 +R "" sub + +INPUT_END + +cat >expect <<EOF +:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100 file2/oldf sub/file2/oldf +:100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 R100 file4 sub/file4 +:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 i/am/new/to/you sub/i/am/new/to/you +:100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 R100 newdir/exec.sh sub/newdir/exec.sh +:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100 newdir/interesting sub/newdir/interesting +EOF +test_expect_success \ + 'M: rename root to subdirectory' \ + 'git fast-import <input && + git diff-tree -M -r M4^ M4 >actual && + cat actual && + compare_diff_raw expect actual' + ### ### series N ### @@ -1218,6 +1254,29 @@ test_expect_success \ compare_diff_raw expect actual' test_expect_success \ + 'N: copy root by path' \ + 'cat >expect <<-\EOF && + :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf oldroot/file2/newf + :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf oldroot/file2/oldf + :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 C100 file4 oldroot/file4 + :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 C100 newdir/exec.sh oldroot/newdir/exec.sh + :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting oldroot/newdir/interesting + EOF + cat >input <<-INPUT_END && + commit refs/heads/N-copy-root-path + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + copy root directory by (empty) path + COMMIT + + from refs/heads/branch^0 + C "" oldroot + INPUT_END + git fast-import <input && + git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual && + compare_diff_raw expect actual' + +test_expect_success \ 'N: delete directory by copying' \ 'cat >expect <<-\EOF && OBJID @@ -1639,7 +1698,7 @@ M 160000 :6 sub INPUT_END test_expect_success \ - 'P: supermodule & submodule mix' \ + 'P: superproject & submodule mix' \ 'git fast-import <input && git checkout subuse1 && rm -rf sub && mkdir sub && (cd sub && @@ -1857,9 +1916,10 @@ test_expect_success \ 'Q: commit notes' \ 'git fast-import <input && git whatchanged notes-test' -test_expect_success \ - 'Q: verify pack' \ - 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done' + +test_expect_success 'Q: verify pack' ' + verify_packs +' commit1=$(git rev-parse notes-test~2) commit2=$(git rev-parse notes-test^) @@ -2098,7 +2158,7 @@ test_expect_success \ grep :1 git.marks' test_expect_success \ - 'R: export-marks options can be overriden by commandline options' \ + 'R: export-marks options can be overridden by commandline options' \ 'cat input | git fast-import --export-marks=other.marks && grep :1 other.marks' @@ -2616,13 +2676,14 @@ test_expect_success \ 'R: blob bigger than threshold' \ 'test_create_repo R && git --git-dir=R/.git fast-import --big-file-threshold=1 <input' -test_expect_success \ - 'R: verify created pack' \ - ': >verify && - for p in R/.git/objects/pack/*.pack; - do - git verify-pack -v $p >>verify || exit; - done' + +test_expect_success 'R: verify created pack' ' + ( + cd R && + verify_packs -v > ../verify + ) +' + test_expect_success \ 'R: verify written objects' \ 'git --git-dir=R/.git cat-file blob big-file:big1 >actual && @@ -2635,4 +2696,307 @@ test_expect_success \ 'n=$(grep $a verify | wc -l) && test 1 = $n' +### +### series S +### +# +# Make sure missing spaces and EOLs after mark references +# cause errors. +# +# Setup: +# +# 1--2--4 +# \ / +# -3- +# +# commit marks: 301, 302, 303, 304 +# blob marks: 403, 404, resp. +# note mark: 202 +# +# The error message when a space is missing not at the +# end of the line is: +# +# Missing space after .. +# +# or when extra characters come after the mark at the end +# of the line: +# +# Garbage after .. +# +# or when the dataref is neither "inline " or a known SHA1, +# +# Invalid dataref .. +# +test_tick + +cat >input <<INPUT_END +commit refs/heads/S +mark :301 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +commit 1 +COMMIT +M 100644 inline hello.c +data <<BLOB +blob 1 +BLOB + +commit refs/heads/S +mark :302 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +commit 2 +COMMIT +from :301 +M 100644 inline hello.c +data <<BLOB +blob 2 +BLOB + +blob +mark :403 +data <<BLOB +blob 3 +BLOB + +blob +mark :202 +data <<BLOB +note 2 +BLOB +INPUT_END + +test_expect_success 'S: initialize for S tests' ' + git fast-import --export-marks=marks <input +' + +# +# filemodify, three datarefs +# +test_expect_success 'S: filemodify with garbage after mark must fail' ' + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + commit refs/heads/S + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + commit N + COMMIT + M 100644 :403x hello.c + EOF + cat err && + test_i18ngrep "space after mark" err +' + +# inline is misspelled; fast-import thinks it is some unknown dataref +test_expect_success 'S: filemodify with garbage after inline must fail' ' + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + commit refs/heads/S + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + commit N + COMMIT + M 100644 inlineX hello.c + data <<BLOB + inline + BLOB + EOF + cat err && + test_i18ngrep "nvalid dataref" err +' + +test_expect_success 'S: filemodify with garbage after sha1 must fail' ' + sha1=$(grep :403 marks | cut -d\ -f2) && + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + commit refs/heads/S + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + commit N + COMMIT + M 100644 ${sha1}x hello.c + EOF + cat err && + test_i18ngrep "space after SHA1" err +' + +# +# notemodify, three ways to say dataref +# +test_expect_success 'S: notemodify with garabge after mark dataref must fail' ' + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + commit refs/heads/S + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + commit S note dataref markref + COMMIT + N :202x :302 + EOF + cat err && + test_i18ngrep "space after mark" err +' + +test_expect_success 'S: notemodify with garbage after inline dataref must fail' ' + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + commit refs/heads/S + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + commit S note dataref inline + COMMIT + N inlineX :302 + data <<BLOB + note blob + BLOB + EOF + cat err && + test_i18ngrep "nvalid dataref" err +' + +test_expect_success 'S: notemodify with garbage after sha1 dataref must fail' ' + sha1=$(grep :202 marks | cut -d\ -f2) && + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + commit refs/heads/S + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + commit S note dataref sha1 + COMMIT + N ${sha1}x :302 + EOF + cat err && + test_i18ngrep "space after SHA1" err +' + +# +# notemodify, mark in commit-ish +# +test_expect_success 'S: notemodify with garbarge after mark commit-ish must fail' ' + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + commit refs/heads/Snotes + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + commit S note commit-ish + COMMIT + N :202 :302x + EOF + cat err && + test_i18ngrep "after mark" err +' + +# +# from +# +test_expect_success 'S: from with garbage after mark must fail' ' + # no && + git fast-import --import-marks=marks --export-marks=marks <<-EOF 2>err + commit refs/heads/S2 + mark :303 + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + commit 3 + COMMIT + from :301x + M 100644 :403 hello.c + EOF + + ret=$? && + echo returned $ret && + test $ret -ne 0 && # failed, but it created the commit + + # go create the commit, need it for merge test + git fast-import --import-marks=marks --export-marks=marks <<-EOF && + commit refs/heads/S2 + mark :303 + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + commit 3 + COMMIT + from :301 + M 100644 :403 hello.c + EOF + + # now evaluate the error + cat err && + test_i18ngrep "after mark" err +' + + +# +# merge +# +test_expect_success 'S: merge with garbage after mark must fail' ' + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + commit refs/heads/S + mark :304 + committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<COMMIT + merge 4 + COMMIT + from :302 + merge :303x + M 100644 :403 hello.c + EOF + cat err && + test_i18ngrep "after mark" err +' + +# +# tag, from markref +# +test_expect_success 'S: tag with garbage after mark must fail' ' + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + tag refs/tags/Stag + from :302x + tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE + data <<TAG + tag S + TAG + EOF + cat err && + test_i18ngrep "after mark" err +' + +# +# cat-blob markref +# +test_expect_success 'S: cat-blob with garbage after mark must fail' ' + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + cat-blob :403x + EOF + cat err && + test_i18ngrep "after mark" err +' + +# +# ls markref +# +test_expect_success 'S: ls with garbage after mark must fail' ' + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + ls :302x hello.c + EOF + cat err && + test_i18ngrep "space after mark" err +' + +test_expect_success 'S: ls with garbage after sha1 must fail' ' + sha1=$(grep :302 marks | cut -d\ -f2) && + test_must_fail git fast-import --import-marks=marks <<-EOF 2>err && + ls ${sha1}x hello.c + EOF + cat err && + test_i18ngrep "space after tree-ish" err +' + +### +### series T (ls) +### +# Setup is carried over from series S. + +test_expect_success 'T: ls root tree' ' + sed -e "s/Z\$//" >expect <<-EOF && + 040000 tree $(git rev-parse S^{tree}) Z + EOF + sha1=$(git rev-parse --verify S) && + git fast-import --import-marks=marks <<-EOF >actual && + ls $sha1 "" + EOF + test_cmp expect actual +' + test_done diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh index 950d0ff498..2312dec8f0 100755 --- a/t/t9350-fast-export.sh +++ b/t/t9350-fast-export.sh @@ -86,7 +86,7 @@ test_expect_success 'import/export-marks' ' git checkout -b marks master && git fast-export --export-marks=tmp-marks HEAD && test -s tmp-marks && - test $(wc -l < tmp-marks) -eq 3 && + test_line_count = 3 tmp-marks && test $( git fast-export --import-marks=tmp-marks\ --export-marks=tmp-marks HEAD | @@ -101,7 +101,7 @@ test_expect_success 'import/export-marks' ' grep ^commit\ | wc -l) \ -eq 1 && - test $(wc -l < tmp-marks) -eq 4 + test_line_count = 4 tmp-marks ' @@ -146,6 +146,12 @@ test_expect_success 'signed-tags=strip' ' ' +test_expect_success 'signed-tags=warn-strip' ' + git fast-export --signed-tags=warn-strip sign-your-name >output 2>err && + ! grep PGP output && + test -s err +' + test_expect_success 'setup submodule' ' git checkout -f master && @@ -303,7 +309,7 @@ test_expect_success 'dropping tag of filtered out object' ' ( cd limit-by-paths && git fast-export --tag-of-filtered-object=drop mytag -- there > output && - test_cmp output expected + test_cmp expected output ) ' @@ -320,7 +326,7 @@ test_expect_success 'rewriting tag of filtered out object' ' ( cd limit-by-paths && git fast-export --tag-of-filtered-object=rewrite mytag -- there > output && - test_cmp output expected + test_cmp expected output ) ' @@ -351,7 +357,7 @@ test_expect_failure 'no exact-ref revisions included' ' ( cd limit-by-paths && git fast-export master~2..master~1 > output && - test_cmp output expected + test_cmp expected output ) ' @@ -390,7 +396,7 @@ test_expect_success 'tree_tag-obj' 'git fast-export tree_tag-obj' test_expect_success 'tag-obj_tag' 'git fast-export tag-obj_tag' test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj' -test_expect_success SYMLINKS 'directory becomes symlink' ' +test_expect_success 'directory becomes symlink' ' git init dirtosymlink && git init result && ( @@ -402,8 +408,7 @@ test_expect_success SYMLINKS 'directory becomes symlink' ' git add foo/world bar/world && git commit -q -mone && git rm -r foo && - ln -s bar foo && - git add foo && + test_ln_s_add bar foo && git commit -q -mtwo ) && ( @@ -430,7 +435,7 @@ test_expect_success 'fast-export quotes pathnames' ' git commit -m rename && git read-tree --empty && git commit -m deletion && - git fast-export HEAD >export.out && + git fast-export -M HEAD >export.out && git rev-list HEAD >expect && git init result && cd result && @@ -440,4 +445,63 @@ test_expect_success 'fast-export quotes pathnames' ' ) ' +test_expect_success 'test bidirectionality' ' + >marks-cur && + >marks-new && + git init marks-test && + git fast-export --export-marks=marks-cur --import-marks=marks-cur --branches | \ + git --git-dir=marks-test/.git fast-import --export-marks=marks-new --import-marks=marks-new && + (cd marks-test && + git reset --hard && + echo Wohlauf > file && + git commit -a -m "back in time") && + git --git-dir=marks-test/.git fast-export --export-marks=marks-new --import-marks=marks-new --branches | \ + git fast-import --export-marks=marks-cur --import-marks=marks-cur +' + +cat > expected << EOF +blob +mark :13 +data 5 +bump + +commit refs/heads/master +mark :14 +author A U Thor <author@example.com> 1112912773 -0700 +committer C O Mitter <committer@example.com> 1112912773 -0700 +data 5 +bump +from :12 +M 100644 :13 file + +EOF + +test_expect_success 'avoid uninteresting refs' ' + > tmp-marks && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks master > /dev/null && + git tag v1.0 && + git branch uninteresting && + echo bump > file && + git commit -a -m bump && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks ^uninteresting ^v1.0 master > actual && + test_cmp expected actual +' + +cat > expected << EOF +reset refs/heads/master +from :14 + +EOF + +test_expect_success 'refs are updated even if no commits need to be exported' ' + > tmp-marks && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks master > /dev/null && + git fast-export --import-marks=tmp-marks \ + --export-marks=tmp-marks master > actual && + test_cmp expected actual +' + test_done diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 9199550ef4..3edc4086d8 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -20,7 +20,7 @@ then skip_all='skipping git-cvsserver tests, cvs not found' test_done fi -"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { +perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done } @@ -36,6 +36,7 @@ export CVSROOT CVS_SERVER rm -rf "$CVSWORK" "$SERVERDIR" test_expect_success 'setup' ' + git config push.default matching && echo >empty && git add empty && git commit -q -m "First Commit" && @@ -400,7 +401,7 @@ cat >expected.C <<EOF Line 0 ======= LINE 0 ->>>>>>> merge.3 +>>>>>>> merge.1.3 EOF for i in 1 2 3 4 5 6 7 8 @@ -476,14 +477,14 @@ test_expect_success 'cvs status' ' cd cvswork && GIT_CONFIG="$git_config" cvs update && GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out && - test $(wc -l <../out) = 2 + test_line_count = 2 ../out ' cd "$WORKDIR" test_expect_success 'cvs status (nonrecursive)' ' cd cvswork && GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out && - test $(wc -l <../out) = 1 + test_line_count = 1 ../out ' cd "$WORKDIR" @@ -500,8 +501,78 @@ test_expect_success 'cvs status (no subdirs in header)' ' cd "$WORKDIR" test_expect_success 'cvs co -c (shows module database)' ' GIT_CONFIG="$git_config" cvs co -c > out && - grep "^master[ ]\+master$" < out && - ! grep -v "^master[ ]\+master$" < out + grep "^master[ ][ ]*master$" <out && + ! grep -v "^master[ ][ ]*master$" <out +' + +#------------ +# CVS LOG +#------------ + +# Known issues with git-cvsserver current log output: +# - Hard coded "lines: +2 -3" placeholder, instead of real numbers. +# - CVS normally does not internally add a blank first line +# nor a last line with nothing but a space to log messages. +# - The latest cvs 1.12.x server sends +0000 timezone (with some hidden "MT" +# tagging in the protocol), and if cvs 1.12.x client sees the MT tags, +# it converts to local time zone. git-cvsserver doesn't do the +0000 +# or the MT tags... +# - The latest 1.12.x releases add a "commitid:" field on to the end of the +# "date:" line (after "lines:"). Maybe we could stick git's commit id +# in it? Or does CVS expect a certain number of bits (too few for +# a full sha1)? +# +# Given the above, expect the following test to break if git-cvsserver's +# log output is improved. The test is just to ensure it doesn't +# accidentally get worse. + +sed -e 's/^x//' -e 's/SP$/ /' > "$WORKDIR/expect" <<EOF +x +xRCS file: $WORKDIR/gitcvs.git/master/merge,v +xWorking file: merge +xhead: 1.4 +xbranch: +xlocks: strict +xaccess list: +xsymbolic names: +xkeyword substitution: kv +xtotal revisions: 4; selected revisions: 4 +xdescription: +x---------------------------- +xrevision 1.4 +xdate: __DATE__; author: author; state: Exp; lines: +2 -3 +x +xMerge test (no-op) +xSP +x---------------------------- +xrevision 1.3 +xdate: __DATE__; author: author; state: Exp; lines: +2 -3 +x +xMerge test (conflict) +xSP +x---------------------------- +xrevision 1.2 +xdate: __DATE__; author: author; state: Exp; lines: +2 -3 +x +xMerge test (merge) +xSP +x---------------------------- +xrevision 1.1 +xdate: __DATE__; author: author; state: Exp; lines: +2 -3 +x +xMerge test (pre-merge) +xSP +x============================================================================= +EOF +expectStat="$?" + +cd "$WORKDIR" +test_expect_success 'cvs log' ' + cd cvswork && + test x"$expectStat" = x"0" && + GIT_CONFIG="$git_config" cvs log merge >../out && + sed -e "s%2[0-9][0-9][0-9]/[01][0-9]/[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9]%__DATE__%" ../out > ../actual && + test_cmp ../expect ../actual ' #------------ diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh index ff6d6fb473..5a4ed28e49 100755 --- a/t/t9401-git-cvsserver-crlf.sh +++ b/t/t9401-git-cvsserver-crlf.sh @@ -38,6 +38,25 @@ not_present() { fi } +check_status_options() { + (cd "$1" && + GIT_CONFIG="$git_config" cvs -Q status "$2" > "${WORKDIR}/status.out" 2>&1 + ) + if [ x"$?" != x"0" ] ; then + echo "Error from cvs status: $1 $2" >> "${WORKDIR}/marked.log" + return 1; + fi + got="$(sed -n -e 's/^[ ]*Sticky Options:[ ]*//p' "${WORKDIR}/status.out")" + expect="$3" + if [ x"$expect" = x"" ] ; then + expect="(none)" + fi + test x"$got" = x"$expect" + stat=$? + echo "cvs status: $1 $2 $stat '$3' '$got'" >> "${WORKDIR}/marked.log" + return $stat +} + cvs >/dev/null 2>&1 if test $? -ne 1 then @@ -49,7 +68,7 @@ then skip_all='skipping git-cvsserver tests, perl not available' test_done fi -"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { +perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' test_done } @@ -65,6 +84,7 @@ export CVSROOT CVS_SERVER rm -rf "$CVSWORK" "$SERVERDIR" test_expect_success 'setup' ' + git config push.default matching && echo "Simple text file" >textfile.c && echo "File with embedded NUL: Q <- there" | q_to_nul > binfile.bin && mkdir subdir && @@ -233,6 +253,22 @@ test_expect_success 'cvs co another copy (guess)' ' marked_as cvswork2/subdir newfile.c "" ' +test_expect_success 'cvs status - sticky options' ' + check_status_options cvswork2 textfile.c "" && + check_status_options cvswork2 binfile.bin -kb && + check_status_options cvswork2 .gitattributes "" && + check_status_options cvswork2 mixedUp.c -kb && + check_status_options cvswork2 multiline.c -kb && + check_status_options cvswork2 multilineTxt.c "" && + check_status_options cvswork2/subdir withCr.bin -kb && + check_status_options cvswork2 subdir/withCr.bin -kb && + check_status_options cvswork2/subdir file.h "" && + check_status_options cvswork2 subdir/file.h "" && + check_status_options cvswork2/subdir unspecified.other "" && + check_status_options cvswork2/subdir newfile.bin "" && + check_status_options cvswork2/subdir newfile.c "" +' + test_expect_success 'add text (guess)' ' (cd cvswork && echo "simpleText" > simpleText.c && diff --git a/t/t9402-git-cvsserver-refs.sh b/t/t9402-git-cvsserver-refs.sh new file mode 100755 index 0000000000..1e266effff --- /dev/null +++ b/t/t9402-git-cvsserver-refs.sh @@ -0,0 +1,551 @@ +#!/bin/sh + +test_description='git-cvsserver and git refspecs + +tests ability for git-cvsserver to switch between and compare +tags, branches and other git refspecs' + +. ./test-lib.sh + +######### + +check_start_tree() { + rm -f "$WORKDIR/list.expected" + echo "start $1" >>"${WORKDIR}/check.log" +} + +check_file() { + sandbox="$1" + file="$2" + ver="$3" + GIT_DIR=$SERVERDIR git show "${ver}:${file}" \ + >"$WORKDIR/check.got" 2>"$WORKDIR/check.stderr" + test_cmp "$WORKDIR/check.got" "$sandbox/$file" + stat=$? + echo "check_file $sandbox $file $ver : $stat" >>"$WORKDIR/check.log" + echo "$file" >>"$WORKDIR/list.expected" + return $stat +} + +check_end_tree() { + sandbox="$1" && + find "$sandbox" -name CVS -prune -o -type f -print >"$WORKDIR/list.actual" && + sort <"$WORKDIR/list.expected" >expected && + sort <"$WORKDIR/list.actual" | sed -e "s%cvswork/%%" >actual && + test_cmp expected actual && + rm expected actual +} + +check_end_full_tree() { + sandbox="$1" && + sort <"$WORKDIR/list.expected" >expected && + find "$sandbox" -name CVS -prune -o -type f -print | + sed -e "s%$sandbox/%%" | sort >act1 && + test_cmp expected act1 && + git ls-tree --name-only -r "$2" | sort >act2 && + test_cmp expected act2 && + rm expected act1 act2 +} + +######### + +check_diff() { + diffFile="$1" + vOld="$2" + vNew="$3" + rm -rf diffSandbox + git clone -q -n . diffSandbox && + ( + cd diffSandbox && + git checkout "$vOld" && + git apply -p0 --index <"../$diffFile" && + git diff --exit-code "$vNew" + ) >check_diff_apply.out 2>&1 +} + +######### + +cvs >/dev/null 2>&1 +if test $? -ne 1 +then + skip_all='skipping git-cvsserver tests, cvs not found' + test_done +fi +if ! test_have_prereq PERL +then + skip_all='skipping git-cvsserver tests, perl not available' + test_done +fi +perl -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || { + skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable' + test_done +} + +unset GIT_DIR GIT_CONFIG +WORKDIR=$(pwd) +SERVERDIR=$(pwd)/gitcvs.git +git_config="$SERVERDIR/config" +CVSROOT=":fork:$SERVERDIR" +CVSWORK="$(pwd)/cvswork" +CVS_SERVER=git-cvsserver +export CVSROOT CVS_SERVER + +rm -rf "$CVSWORK" "$SERVERDIR" +test_expect_success 'setup v1, b1' ' + echo "Simple text file" >textfile.c && + echo "t2" >t2 && + mkdir adir && + echo "adir/afile line1" >adir/afile && + echo "adir/afile line2" >>adir/afile && + echo "adir/afile line3" >>adir/afile && + echo "adir/afile line4" >>adir/afile && + echo "adir/a2file" >>adir/a2file && + mkdir adir/bdir && + echo "adir/bdir/bfile line 1" >adir/bdir/bfile && + echo "adir/bdir/bfile line 2" >>adir/bdir/bfile && + echo "adir/bdir/b2file" >adir/bdir/b2file && + git add textfile.c t2 adir && + git commit -q -m "First Commit (v1)" && + git tag v1 && + git branch b1 && + git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 && + GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true && + GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" +' + +rm -rf cvswork +test_expect_success 'cvs co v1' ' + cvs -f -Q co -r v1 -d cvswork master >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +rm -rf cvswork +test_expect_success 'cvs co b1' ' + cvs -f co -r b1 -d cvswork master >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'cvs co b1 [cvswork3]' ' + cvs -f co -r b1 -d cvswork3 master >cvs.log 2>&1 && + check_start_tree cvswork3 && + check_file cvswork3 textfile.c v1 && + check_file cvswork3 t2 v1 && + check_file cvswork3 adir/afile v1 && + check_file cvswork3 adir/a2file v1 && + check_file cvswork3 adir/bdir/bfile v1 && + check_file cvswork3 adir/bdir/b2file v1 && + check_end_full_tree cvswork3 v1 +' + +test_expect_success 'edit cvswork3 and save diff' ' + ( + cd cvswork3 && + sed -e "s/line1/line1 - data/" adir/afile >adir/afileNEW && + mv -f adir/afileNEW adir/afile && + echo "afile5" >adir/afile5 && + rm t2 && + cvs -f add adir/afile5 && + cvs -f rm t2 && + ! cvs -f diff -N -u >"$WORKDIR/cvswork3edit.diff" + ) +' + +test_expect_success 'setup v1.2 on b1' ' + git checkout b1 && + echo "new v1.2" >t3 && + rm t2 && + sed -e "s/line3/line3 - more data/" adir/afile >adir/afileNEW && + mv -f adir/afileNEW adir/afile && + rm adir/a2file && + echo "a3file" >>adir/a3file && + echo "bfile line 3" >>adir/bdir/bfile && + rm adir/bdir/b2file && + echo "b3file" >adir/bdir/b3file && + mkdir cdir && + echo "cdir/cfile" >cdir/cfile && + git add -A cdir adir t3 t2 && + git commit -q -m 'v1.2' && + git tag v1.2 && + git push --tags gitcvs.git b1:b1 +' + +test_expect_success 'cvs -f up (on b1 adir)' ' + ( cd cvswork/adir && cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_end_tree cvswork +' + +test_expect_success 'cvs up (on b1 /)' ' + ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_tree cvswork +' + +# Make sure "CVS/Tag" files didn't get messed up: +test_expect_success 'cvs up (on b1 /) (again; check CVS/Tag files)' ' + ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_tree cvswork +' + +# update to another version: +test_expect_success 'cvs up -r v1' ' + ( cd cvswork && cvs -f up -r v1 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'cvs up' ' + ( cd cvswork && cvs -f up ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'cvs up (again; check CVS/Tag files)' ' + ( cd cvswork && cvs -f up -d ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'setup simple b2' ' + git branch b2 v1 && + git push --tags gitcvs.git b2:b2 +' + +test_expect_success 'cvs co b2 [into cvswork2]' ' + cvs -f co -r b2 -d cvswork2 master >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_tree cvswork +' + +test_expect_success 'root dir edit [cvswork2]' ' + ( + cd cvswork2 && echo "Line 2" >>textfile.c && + ! cvs -f diff -u >"$WORKDIR/cvsEdit1.diff" && + cvs -f commit -m "edit textfile.c" textfile.c + ) >cvsEdit1.log 2>&1 +' + +test_expect_success 'root dir rm file [cvswork2]' ' + ( + cd cvswork2 && + cvs -f rm -f t2 && + cvs -f diff -u >../cvsEdit2-empty.diff && + ! cvs -f diff -N -u >"$WORKDIR/cvsEdit2-N.diff" && + cvs -f commit -m "rm t2" + ) >cvsEdit2.log 2>&1 +' + +test_expect_success 'subdir edit/add/rm files [cvswork2]' ' + ( + cd cvswork2 && + sed -e "s/line 1/line 1 (v2)/" adir/bdir/bfile >adir/bdir/bfileNEW && + mv -f adir/bdir/bfileNEW adir/bdir/bfile && + rm adir/bdir/b2file && + cd adir && + cvs -f rm bdir/b2file && + echo "4th file" >bdir/b4file && + cvs -f add bdir/b4file && + ! cvs -f diff -N -u >"$WORKDIR/cvsEdit3.diff" && + git fetch gitcvs.git b2:b2 && + ( + cd .. && + ! cvs -f diff -u -N -r v1.2 >"$WORKDIR/cvsEdit3-v1.2.diff" && + ! cvs -f diff -u -N -r v1.2 -r v1 >"$WORKDIR/cvsEdit3-v1.2-v1.diff" + ) && + cvs -f commit -m "various add/rm/edit" + ) >cvs.log 2>&1 +' + +test_expect_success 'validate result of edits [cvswork2]' ' + git fetch gitcvs.git b2:b2 && + git tag v2 b2 && + git push --tags gitcvs.git b2:b2 && + check_start_tree cvswork2 && + check_file cvswork2 textfile.c v2 && + check_file cvswork2 adir/afile v2 && + check_file cvswork2 adir/a2file v2 && + check_file cvswork2 adir/bdir/bfile v2 && + check_file cvswork2 adir/bdir/b4file v2 && + check_end_full_tree cvswork2 v2 +' + +test_expect_success 'validate basic diffs saved during above cvswork2 edits' ' + test $(grep Index: cvsEdit1.diff | wc -l) = 1 && + test_must_be_empty cvsEdit2-empty.diff && + test $(grep Index: cvsEdit2-N.diff | wc -l) = 1 && + test $(grep Index: cvsEdit3.diff | wc -l) = 3 && + rm -rf diffSandbox && + git clone -q -n . diffSandbox && + ( + cd diffSandbox && + git checkout v1 && + git apply -p0 --index <"$WORKDIR/cvsEdit1.diff" && + git apply -p0 --index <"$WORKDIR/cvsEdit2-N.diff" && + git apply -p0 --directory=adir --index <"$WORKDIR/cvsEdit3.diff" && + git diff --exit-code v2 + ) >"check_diff_apply.out" 2>&1 +' + +test_expect_success 'validate v1.2 diff saved during last cvswork2 edit' ' + test $(grep Index: cvsEdit3-v1.2.diff | wc -l) = 9 && + check_diff cvsEdit3-v1.2.diff v1.2 v2 +' + +test_expect_success 'validate v1.2 v1 diff saved during last cvswork2 edit' ' + test $(grep Index: cvsEdit3-v1.2-v1.diff | wc -l) = 9 && + check_diff cvsEdit3-v1.2-v1.diff v1.2 v1 +' + +test_expect_success 'cvs up [cvswork2]' ' + ( cd cvswork2 && cvs -f up ) >cvs.log 2>&1 && + check_start_tree cvswork2 && + check_file cvswork2 textfile.c v2 && + check_file cvswork2 adir/afile v2 && + check_file cvswork2 adir/a2file v2 && + check_file cvswork2 adir/bdir/bfile v2 && + check_file cvswork2 adir/bdir/b4file v2 && + check_end_full_tree cvswork2 v2 +' + +test_expect_success 'cvs up -r b2 [back to cvswork]' ' + ( cd cvswork && cvs -f up -r b2 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v2 && + check_file cvswork adir/afile v2 && + check_file cvswork adir/a2file v2 && + check_file cvswork adir/bdir/bfile v2 && + check_file cvswork adir/bdir/b4file v2 && + check_end_full_tree cvswork v2 +' + +test_expect_success 'cvs up -r b1' ' + ( cd cvswork && cvs -f up -r b1 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_full_tree cvswork v1.2 +' + +test_expect_success 'cvs up -A' ' + ( cd cvswork && cvs -f up -A ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_full_tree cvswork v1 +' + +test_expect_success 'cvs up (check CVS/Tag files)' ' + ( cd cvswork && cvs -f up ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_full_tree cvswork v1 +' + +# This is not really legal CVS, but it seems to work anyway: +test_expect_success 'cvs up -r heads/b1' ' + ( cd cvswork && cvs -f up -r heads/b1 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1.2 && + check_file cvswork t3 v1.2 && + check_file cvswork adir/afile v1.2 && + check_file cvswork adir/a3file v1.2 && + check_file cvswork adir/bdir/bfile v1.2 && + check_file cvswork adir/bdir/b3file v1.2 && + check_file cvswork cdir/cfile v1.2 && + check_end_full_tree cvswork v1.2 +' + +# But this should work even if CVS client checks -r more carefully: +test_expect_success 'cvs up -r heads_-s-b2 (cvsserver escape mechanism)' ' + ( cd cvswork && cvs -f up -r heads_-s-b2 ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v2 && + check_file cvswork adir/afile v2 && + check_file cvswork adir/a2file v2 && + check_file cvswork adir/bdir/bfile v2 && + check_file cvswork adir/bdir/b4file v2 && + check_end_full_tree cvswork v2 +' + +v1hash=$(git rev-parse v1) +test_expect_success 'cvs up -r $(git rev-parse v1)' ' + test -n "$v1hash" && + ( cd cvswork && cvs -f up -r "$v1hash" ) >cvs.log 2>&1 && + check_start_tree cvswork && + check_file cvswork textfile.c v1 && + check_file cvswork t2 v1 && + check_file cvswork adir/afile v1 && + check_file cvswork adir/a2file v1 && + check_file cvswork adir/bdir/bfile v1 && + check_file cvswork adir/bdir/b2file v1 && + check_end_full_tree cvswork v1 +' + +test_expect_success 'cvs diff -r v1 -u' ' + ( cd cvswork && cvs -f diff -r v1 -u ) >cvsDiff.out 2>cvs.log && + test_must_be_empty cvsDiff.out && + test_must_be_empty cvs.log +' + +test_expect_success 'cvs diff -N -r v2 -u' ' + ( cd cvswork && ! cvs -f diff -N -r v2 -u ) >cvsDiff.out 2>cvs.log && + test_must_be_empty cvs.log && + test -s cvsDiff.out && + check_diff cvsDiff.out v2 v1 >check_diff.out 2>&1 +' + +test_expect_success 'cvs diff -N -r v2 -r v1.2' ' + ( cd cvswork && ! cvs -f diff -N -r v2 -r v1.2 -u ) >cvsDiff.out 2>cvs.log && + test_must_be_empty cvs.log && + test -s cvsDiff.out && + check_diff cvsDiff.out v2 v1.2 >check_diff.out 2>&1 +' + +test_expect_success 'apply early [cvswork3] diff to b3' ' + git clone -q . gitwork3 && + ( + cd gitwork3 && + git checkout -b b3 v1 && + git apply -p0 --index <"$WORKDIR/cvswork3edit.diff" && + git commit -m "cvswork3 edits applied" + ) && + git fetch gitwork3 b3:b3 && + git tag v3 b3 +' + +test_expect_success 'check [cvswork3] diff' ' + ( cd cvswork3 && ! cvs -f diff -N -u ) >"$WORKDIR/cvsDiff.out" 2>cvs.log && + test_must_be_empty cvs.log && + test -s cvsDiff.out && + test $(grep Index: cvsDiff.out | wc -l) = 3 && + test_cmp cvsDiff.out cvswork3edit.diff && + check_diff cvsDiff.out v1 v3 >check_diff.out 2>&1 +' + +test_expect_success 'merge early [cvswork3] b3 with b1' ' + ( cd gitwork3 && git merge "message" HEAD b1 ) && + git fetch gitwork3 b3:b3 && + git tag v3merged b3 && + git push --tags gitcvs.git b3:b3 +' + +# This test would fail if cvsserver properly created a ".#afile"* file +# for the merge. +# TODO: Validate that the .# file was saved properly, and then +# delete/ignore it when checking the tree. +test_expect_success 'cvs up dirty [cvswork3]' ' + ( + cd cvswork3 && + cvs -f up && + ! cvs -f diff -N -u >"$WORKDIR/cvsDiff.out" + ) >cvs.log 2>&1 && + test -s cvsDiff.out && + test $(grep Index: cvsDiff.out | wc -l) = 2 && + check_start_tree cvswork3 && + check_file cvswork3 textfile.c v3merged && + check_file cvswork3 t3 v3merged && + check_file cvswork3 adir/afile v3merged && + check_file cvswork3 adir/a3file v3merged && + check_file cvswork3 adir/afile5 v3merged && + check_file cvswork3 adir/bdir/bfile v3merged && + check_file cvswork3 adir/bdir/b3file v3merged && + check_file cvswork3 cdir/cfile v3merged && + check_end_full_tree cvswork3 v3merged +' + +# TODO: test cvs status + +test_expect_success 'cvs commit [cvswork3]' ' + ( + cd cvswork3 && + cvs -f commit -m "dirty sandbox after auto-merge" + ) >cvs.log 2>&1 && + check_start_tree cvswork3 && + check_file cvswork3 textfile.c v3merged && + check_file cvswork3 t3 v3merged && + check_file cvswork3 adir/afile v3merged && + check_file cvswork3 adir/a3file v3merged && + check_file cvswork3 adir/afile5 v3merged && + check_file cvswork3 adir/bdir/bfile v3merged && + check_file cvswork3 adir/bdir/b3file v3merged && + check_file cvswork3 cdir/cfile v3merged && + check_end_full_tree cvswork3 v3merged && + git fetch gitcvs.git b3:b4 && + git tag v4.1 b4 && + git diff --exit-code v4.1 v3merged >check_diff_apply.out 2>&1 +' + +test_done diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index 90bb6050c1..e74b9ab1e1 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -156,10 +156,10 @@ test_expect_success \ git commit -a -m "File renamed." && gitweb_run "p=.git;a=commitdiff"' -test_expect_success SYMLINKS \ +test_expect_success \ 'commitdiff(0): file to symlink' \ 'rm renamed_file && - ln -s file renamed_file && + test_ln_s_add file renamed_file && git commit -a -m "File to symlink." && gitweb_run "p=.git;a=commitdiff"' @@ -212,15 +212,14 @@ test_expect_success \ # ---------------------------------------------------------------------- # commitdiff testing (taken from t4114-apply-typechange.sh) -test_expect_success SYMLINKS 'setup typechange commits' ' +test_expect_success 'setup typechange commits' ' echo "hello world" > foo && echo "hi planet" > bar && git update-index --add foo bar && git commit -m initial && git branch initial && rm -f foo && - ln -s bar foo && - git update-index foo && + test_ln_s_add bar foo && git commit -m "foo symlinked to bar" && git branch foo-symlinked-to-bar && rm -f foo && @@ -329,7 +328,7 @@ test_expect_success \ git add b && git commit -a -m "On branch" && git checkout master && - git pull . b && + git merge b && git tag merge_commit' test_expect_success \ @@ -361,11 +360,7 @@ test_expect_success \ echo "Changed" >> 04-rename-to && test_chmod +x 05-mode-change && rm -f 06-file-or-symlink && - if test_have_prereq SYMLINKS; then - ln -s 01-change 06-file-or-symlink - else - printf %s 01-change > 06-file-or-symlink - fi && + test_ln_s_add 01-change 06-file-or-symlink && echo "Changed and have mode changed" > 07-change-mode-change && test_chmod +x 07-change-mode-change && git commit -a -m "Large commit" && @@ -539,8 +534,7 @@ test_expect_success \ test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" && echo "ISO-8859-1" >> file && git add file && - git config i18n.commitencoding ISO-8859-1 && - test_when_finished "git config --unset i18n.commitencoding" && + test_config i18n.commitencoding ISO-8859-1 && git commit -F "$TEST_DIRECTORY"/t3900/ISO8859-1.txt && gitweb_run "p=.git;a=commit"' @@ -689,9 +683,11 @@ test_expect_success \ # syntax highlighting -highlight --version >/dev/null 2>&1 +highlight_version=$(highlight --version </dev/null 2>/dev/null) if [ $? -eq 127 ]; then - say "Skipping syntax highlighting test, because 'highlight' was not found" + say "Skipping syntax highlighting tests: 'highlight' not found" +elif test -z "$highlight_version"; then + say "Skipping syntax highlighting tests: incorrect 'highlight' found" else test_set_prereq HIGHLIGHT cat >>gitweb_config.perl <<-\EOF diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh index 31076edc5b..d3a5bac754 100755 --- a/t/t9501-gitweb-standalone-http-status.sh +++ b/t/t9501-gitweb-standalone-http-status.sh @@ -12,6 +12,13 @@ code and message.' . ./gitweb-lib.sh +# +# Gitweb only provides the functionality tested by the 'modification times' +# tests if it can access a date parser from one of these modules: +# +perl -MHTTP::Date -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER +perl -MTime::ParseDate -e 0 >/dev/null 2>&1 && test_set_prereq DATE_PARSER + # ---------------------------------------------------------------------- # snapshot settings @@ -92,7 +99,7 @@ test_debug 'cat gitweb.output' test_expect_success 'snapshots: bad tree-ish id (tagged object)' ' echo object > tag-object && git add tag-object && - git commit -m "Object to be tagged" && + test_tick && git commit -m "Object to be tagged" && git tag tagged-object `git hash-object tag-object` && gitweb_run "p=.git;a=snapshot;h=tagged-object;sf=tgz" && grep "400 - Object is not a tree-ish" gitweb.output @@ -112,6 +119,69 @@ test_expect_success 'snapshots: bad object id' ' ' test_debug 'cat gitweb.output' +# ---------------------------------------------------------------------- +# modification times (Last-Modified and If-Modified-Since) + +test_expect_success DATE_PARSER 'modification: feed last-modified' ' + gitweb_run "p=.git;a=atom;h=master" && + grep "Status: 200 OK" gitweb.headers && + grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers +' +test_debug 'cat gitweb.headers' + +test_expect_success DATE_PARSER 'modification: feed if-modified-since (modified)' ' + HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" && + export HTTP_IF_MODIFIED_SINCE && + test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && + gitweb_run "p=.git;a=atom;h=master" && + grep "Status: 200 OK" gitweb.headers +' +test_debug 'cat gitweb.headers' + +test_expect_success DATE_PARSER 'modification: feed if-modified-since (unmodified)' ' + HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" && + export HTTP_IF_MODIFIED_SINCE && + test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && + gitweb_run "p=.git;a=atom;h=master" && + grep "Status: 304 Not Modified" gitweb.headers +' +test_debug 'cat gitweb.headers' + +test_expect_success DATE_PARSER 'modification: snapshot last-modified' ' + gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" && + grep "Status: 200 OK" gitweb.headers && + grep "Last-modified: Thu, 7 Apr 2005 22:14:13 +0000" gitweb.headers +' +test_debug 'cat gitweb.headers' + +test_expect_success DATE_PARSER 'modification: snapshot if-modified-since (modified)' ' + HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" && + export HTTP_IF_MODIFIED_SINCE && + test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && + gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" && + grep "Status: 200 OK" gitweb.headers +' +test_debug 'cat gitweb.headers' + +test_expect_success DATE_PARSER 'modification: snapshot if-modified-since (unmodified)' ' + HTTP_IF_MODIFIED_SINCE="Thu, 7 Apr 2005 22:14:13 +0000" && + export HTTP_IF_MODIFIED_SINCE && + test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && + gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" && + grep "Status: 304 Not Modified" gitweb.headers +' +test_debug 'cat gitweb.headers' + +test_expect_success DATE_PARSER 'modification: tree snapshot' ' + ID=`git rev-parse --verify HEAD^{tree}` && + HTTP_IF_MODIFIED_SINCE="Wed, 6 Apr 2005 22:14:13 +0000" && + export HTTP_IF_MODIFIED_SINCE && + test_when_finished "unset HTTP_IF_MODIFIED_SINCE" && + gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" && + grep "Status: 200 OK" gitweb.headers && + ! grep -i "last-modified" gitweb.headers +' +test_debug 'cat gitweb.headers' # ---------------------------------------------------------------------- # load checking diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh index 731e64c3ad..86dfee2e4f 100755 --- a/t/t9502-gitweb-standalone-parse-output.sh +++ b/t/t9502-gitweb-standalone-parse-output.sh @@ -40,7 +40,7 @@ check_snapshot () { echo "basename=$basename" grep "filename=.*$basename.tar" gitweb.headers >/dev/null 2>&1 && "$TAR" tf gitweb.body >file_list && - ! grep -v "^$prefix/" file_list + ! grep -v -e "^$prefix$" -e "^$prefix/" -e "^pax_global_header$" file_list } test_expect_success setup ' @@ -185,5 +185,20 @@ test_expect_success 'forks: project_index lists all projects (incl. forks)' ' test_cmp expected actual ' +xss() { + echo >&2 "Checking $1..." && + gitweb_run "$1" && + if grep "$TAG" gitweb.body; then + echo >&2 "xss: $TAG should have been quoted in output" + return 1 + fi + return 0 +} + +test_expect_success 'xss checks' ' + TAG="<magic-xss-tag>" && + xss "a=rss&p=$TAG" && + xss "a=rss&p=foo.git&f=$TAG" +' test_done diff --git a/t/t9604-cvsimport-timestamps.sh b/t/t9604-cvsimport-timestamps.sh new file mode 100755 index 0000000000..1fd51423ee --- /dev/null +++ b/t/t9604-cvsimport-timestamps.sh @@ -0,0 +1,71 @@ +#!/bin/sh + +test_description='git cvsimport timestamps' +. ./lib-cvs.sh + +setup_cvs_test_repository t9604 + +test_expect_success 'check timestamps are UTC (TZ=CST6CDT)' ' + + TZ=CST6CDT git cvsimport -p"-x" -C module-1 module && + git cvsimport -p"-x" -C module-1 module && + ( + cd module-1 && + git log --format="%s %ai" + ) >actual-1 && + cat >expect-1 <<-EOF && + Rev 16 2006-10-29 07:00:01 +0000 + Rev 15 2006-10-29 06:59:59 +0000 + Rev 14 2006-04-02 08:00:01 +0000 + Rev 13 2006-04-02 07:59:59 +0000 + Rev 12 2005-12-01 00:00:00 +0000 + Rev 11 2005-11-01 00:00:00 +0000 + Rev 10 2005-10-01 00:00:00 +0000 + Rev 9 2005-09-01 00:00:00 +0000 + Rev 8 2005-08-01 00:00:00 +0000 + Rev 7 2005-07-01 00:00:00 +0000 + Rev 6 2005-06-01 00:00:00 +0000 + Rev 5 2005-05-01 00:00:00 +0000 + Rev 4 2005-04-01 00:00:00 +0000 + Rev 3 2005-03-01 00:00:00 +0000 + Rev 2 2005-02-01 00:00:00 +0000 + Rev 1 2005-01-01 00:00:00 +0000 + EOF + test_cmp actual-1 expect-1 +' + +test_expect_success 'check timestamps with author-specific timezones' ' + + cat >cvs-authors <<-EOF && + user1=User One <user1@domain.org> + user2=User Two <user2@domain.org> CST6CDT + user3=User Three <user3@domain.org> EST5EDT + user4=User Four <user4@domain.org> MST7MDT + EOF + git cvsimport -p"-x" -A cvs-authors -C module-2 module && + ( + cd module-2 && + git log --format="%s %ai %an" + ) >actual-2 && + cat >expect-2 <<-EOF && + Rev 16 2006-10-29 01:00:01 -0600 User Two + Rev 15 2006-10-29 01:59:59 -0500 User Two + Rev 14 2006-04-02 03:00:01 -0500 User Two + Rev 13 2006-04-02 01:59:59 -0600 User Two + Rev 12 2005-11-30 17:00:00 -0700 User Four + Rev 11 2005-10-31 19:00:00 -0500 User Three + Rev 10 2005-09-30 19:00:00 -0500 User Two + Rev 9 2005-09-01 00:00:00 +0000 User One + Rev 8 2005-07-31 18:00:00 -0600 User Four + Rev 7 2005-06-30 20:00:00 -0400 User Three + Rev 6 2005-05-31 19:00:00 -0500 User Two + Rev 5 2005-05-01 00:00:00 +0000 User One + Rev 4 2005-03-31 17:00:00 -0700 User Four + Rev 3 2005-02-28 19:00:00 -0500 User Three + Rev 2 2005-01-31 18:00:00 -0600 User Two + Rev 1 2005-01-01 00:00:00 +0000 User One + EOF + test_cmp actual-2 expect-2 +' + +test_done diff --git a/t/t9604/cvsroot/.gitattributes b/t/t9604/cvsroot/.gitattributes new file mode 100644 index 0000000000..562b12e16e --- /dev/null +++ b/t/t9604/cvsroot/.gitattributes @@ -0,0 +1 @@ +* -whitespace diff --git a/t/t9604/cvsroot/CVSROOT/.gitignore b/t/t9604/cvsroot/CVSROOT/.gitignore new file mode 100644 index 0000000000..3bb9b34173 --- /dev/null +++ b/t/t9604/cvsroot/CVSROOT/.gitignore @@ -0,0 +1,2 @@ +history +val-tags diff --git a/t/t9604/cvsroot/module/a,v b/t/t9604/cvsroot/module/a,v new file mode 100644 index 0000000000..80658c3c8d --- /dev/null +++ b/t/t9604/cvsroot/module/a,v @@ -0,0 +1,264 @@ +head 1.16; +access; +symbols; +locks; strict; +comment @# @; + + +1.16 +date 2006.10.29.07.00.01; author user2; state Exp; +branches; +next 1.15; + +1.15 +date 2006.10.29.06.59.59; author user2; state Exp; +branches; +next 1.14; + +1.14 +date 2006.04.02.08.00.01; author user2; state Exp; +branches; +next 1.13; + +1.13 +date 2006.04.02.07.59.59; author user2; state Exp; +branches; +next 1.12; + +1.12 +date 2005.12.01.00.00.00; author user4; state Exp; +branches; +next 1.11; + +1.11 +date 2005.11.01.00.00.00; author user3; state Exp; +branches; +next 1.10; + +1.10 +date 2005.10.01.00.00.00; author user2; state Exp; +branches; +next 1.9; + +1.9 +date 2005.09.01.00.00.00; author user1; state Exp; +branches; +next 1.8; + +1.8 +date 2005.08.01.00.00.00; author user4; state Exp; +branches; +next 1.7; + +1.7 +date 2005.07.01.00.00.00; author user3; state Exp; +branches; +next 1.6; + +1.6 +date 2005.06.01.00.00.00; author user2; state Exp; +branches; +next 1.5; + +1.5 +date 2005.05.01.00.00.00; author user1; state Exp; +branches; +next 1.4; + +1.4 +date 2005.04.01.00.00.00; author user4; state Exp; +branches; +next 1.3; + +1.3 +date 2005.03.01.00.00.00; author user3; state Exp; +branches; +next 1.2; + +1.2 +date 2005.02.01.00.00.00; author user2; state Exp; +branches; +next 1.1; + +1.1 +date 2005.01.01.00.00.00; author user1; state Exp; +branches; +next ; + + +desc +@@ + + +1.16 +log +@Rev 16 +@ +text +@Rev 16 +@ + + +1.15 +log +@Rev 15 +@ +text +@d1 1 +a1 1 +Rev 15 +@ + + +1.14 +log +@Rev 14 +@ +text +@d1 1 +a1 1 +Rev 14 +@ + + +1.13 +log +@Rev 13 +@ +text +@d1 1 +a1 1 +Rev 13 +@ + + +1.12 +log +@Rev 12 +@ +text +@d1 1 +a1 1 +Rev 12 +@ + + +1.11 +log +@Rev 11 +@ +text +@d1 1 +a1 1 +Rev 11 +@ + + +1.10 +log +@Rev 10 +@ +text +@d1 1 +a1 1 +Rev 10 +@ + + +1.9 +log +@Rev 9 +@ +text +@d1 1 +a1 1 +Rev 9 +@ + + +1.8 +log +@Rev 8 +@ +text +@d1 1 +a1 1 +Rev 8 +@ + + +1.7 +log +@Rev 7 +@ +text +@d1 1 +a1 1 +Rev 7 +@ + + +1.6 +log +@Rev 6 +@ +text +@d1 1 +a1 1 +Rev 6 +@ + + +1.5 +log +@Rev 5 +@ +text +@d1 1 +a1 1 +Rev 5 +@ + + +1.4 +log +@Rev 4 +@ +text +@d1 1 +a1 1 +Rev 4 +@ + + +1.3 +log +@Rev 3 +@ +text +@d1 1 +a1 1 +Rev 3 +@ + + +1.2 +log +@Rev 2 +@ +text +@d1 1 +a1 1 +Rev 2 +@ + + +1.1 +log +@Rev 1 +@ +text +@d1 1 +a1 1 +Rev 1 +@ diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh index 435d896476..102c133112 100755 --- a/t/t9700-perl-git.sh +++ b/t/t9700-perl-git.sh @@ -11,7 +11,7 @@ if ! test_have_prereq PERL; then test_done fi -"$PERL_PATH" -MTest::More -e 0 2>/dev/null || { +perl -MTest::More -e 0 2>/dev/null || { skip_all="Perl Test::More unavailable, skipping test" test_done } @@ -55,6 +55,6 @@ test_external_has_tap=1 test_external_without_stderr \ 'Perl API' \ - "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl + perl "$TEST_DIRECTORY"/t9700/test.pl test_done diff --git a/t/t9700/test.pl b/t/t9700/test.pl index 3b9b48408a..1140767b50 100755 --- a/t/t9700/test.pl +++ b/t/t9700/test.pl @@ -45,9 +45,9 @@ is($r->get_color("color.test.slot1", "red"), $ansi_green, "get_color"); # Failure cases for config: # Save and restore STDERR; we will probably extract this into a # "dies_ok" method and possibly move the STDERR handling to Git.pm. -open our $tmpstderr, ">&STDERR" or die "cannot save STDERR"; close STDERR; -eval { $r->config("test.dupstring") }; -ok($@, "config: duplicate entry in scalar context fails"); +open our $tmpstderr, ">&STDERR" or die "cannot save STDERR"; +open STDERR, ">", "/dev/null" or die "cannot redirect STDERR to /dev/null"; +is($r->config("test.dupstring"), "value2", "config: multivar"); eval { $r->config_bool("test.boolother") }; ok($@, "config_bool: non-boolean values fail"); open STDERR, ">&", $tmpstderr or die "cannot restore STDERR"; diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh index 486c8eeb7e..665607c9cb 100755 --- a/t/t9800-git-p4-basic.sh +++ b/t/t9800-git-p4-basic.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 tests' +test_description='git p4 tests' . ./lib-git-p4.sh @@ -20,8 +20,8 @@ test_expect_success 'add p4 files' ' ) ' -test_expect_success 'basic git-p4 clone' ' - "$GITP4" clone --dest="$git" //depot && +test_expect_success 'basic git p4 clone' ' + git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && @@ -30,8 +30,13 @@ test_expect_success 'basic git-p4 clone' ' ) ' -test_expect_success 'git-p4 clone @all' ' - "$GITP4" clone --dest="$git" //depot@all && +test_expect_success 'depot typo error' ' + test_must_fail git p4 clone --dest="$git" /depot 2>errs && + grep "Depot paths must start with" errs +' + +test_expect_success 'git p4 clone @all' ' + git p4 clone --dest="$git" //depot@all && test_when_finished cleanup_git && ( cd "$git" && @@ -40,12 +45,13 @@ test_expect_success 'git-p4 clone @all' ' ) ' -test_expect_success 'git-p4 sync uninitialized repo' ' +test_expect_success 'git p4 sync uninitialized repo' ' test_create_repo "$git" && test_when_finished cleanup_git && ( cd "$git" && - test_must_fail "$GITP4" sync + test_must_fail git p4 sync 2>errs && + test_i18ngrep "Perhaps you never did" errs ) ' @@ -53,13 +59,13 @@ test_expect_success 'git-p4 sync uninitialized repo' ' # Create a git repo by hand. Add a commit so that HEAD is valid. # Test imports a new p4 repository into a new git branch. # -test_expect_success 'git-p4 sync new branch' ' +test_expect_success 'git p4 sync new branch' ' test_create_repo "$git" && test_when_finished cleanup_git && ( cd "$git" && test_commit head && - "$GITP4" sync --branch=refs/remotes/p4/depot //depot@all && + git p4 sync --branch=refs/remotes/p4/depot //depot@all && git log --oneline p4/depot >lines && test_line_count = 2 lines ) @@ -76,7 +82,7 @@ test_expect_success 'clone two dirs' ' p4 add sub2/f2 && p4 submit -d "sub2/f2" ) && - "$GITP4" clone --dest="$git" //depot/sub1 //depot/sub2 && + git p4 clone --dest="$git" //depot/sub1 //depot/sub2 && test_when_finished cleanup_git && ( cd "$git" && @@ -94,7 +100,7 @@ test_expect_success 'clone two dirs, @all' ' p4 add sub1/f3 && p4 submit -d "sub1/f3" ) && - "$GITP4" clone --dest="$git" //depot/sub1@all //depot/sub2@all && + git p4 clone --dest="$git" //depot/sub1@all //depot/sub2@all && test_when_finished cleanup_git && ( cd "$git" && @@ -112,7 +118,7 @@ test_expect_success 'clone two dirs, @all, conflicting files' ' p4 add sub2/f3 && p4 submit -d "sub2/f3" ) && - "$GITP4" clone --dest="$git" //depot/sub1@all //depot/sub2@all && + git p4 clone --dest="$git" //depot/sub1@all //depot/sub2@all && test_when_finished cleanup_git && ( cd "$git" && @@ -126,186 +132,48 @@ test_expect_success 'clone two dirs, @all, conflicting files' ' ' test_expect_success 'exit when p4 fails to produce marshaled output' ' - badp4dir="$TRASH_DIRECTORY/badp4dir" && - mkdir "$badp4dir" && - test_when_finished "rm \"$badp4dir/p4\" && rmdir \"$badp4dir\"" && - cat >"$badp4dir"/p4 <<-EOF && + mkdir badp4dir && + test_when_finished "rm badp4dir/p4 && rmdir badp4dir" && + cat >badp4dir/p4 <<-EOF && #!$SHELL_PATH exit 1 EOF - chmod 755 "$badp4dir"/p4 && - PATH="$badp4dir:$PATH" "$GITP4" clone --dest="$git" //depot >errs 2>&1 ; retval=$? && - test $retval -eq 1 && - test_must_fail grep -q Traceback errs -' - -test_expect_success 'add p4 files with wildcards in the names' ' - ( - cd "$cli" && - echo file-wild-hash >file-wild#hash && - echo file-wild-star >file-wild\*star && - echo file-wild-at >file-wild@at && - echo file-wild-percent >file-wild%percent && - p4 add -f file-wild* && - p4 submit -d "file wildcards" - ) -' - -test_expect_success 'wildcard files git-p4 clone' ' - "$GITP4" clone --dest="$git" //depot && - test_when_finished cleanup_git && - ( - cd "$git" && - test -f file-wild#hash && - test -f file-wild\*star && - test -f file-wild@at && - test -f file-wild%percent - ) -' - -test_expect_success 'clone bare' ' - "$GITP4" clone --dest="$git" --bare //depot && - test_when_finished cleanup_git && - ( - cd "$git" && - test ! -d .git && - bare=`git config --get core.bare` && - test "$bare" = true - ) -' - -p4_add_user() { - name=$1 fullname=$2 && - p4 user -f -i <<-EOF && - User: $name - Email: $name@localhost - FullName: $fullname - EOF - p4 passwd -P secret $name -} - -p4_grant_admin() { - name=$1 && - { - p4 protect -o && - echo " admin user $name * //depot/..." - } | p4 protect -i -} - -p4_check_commit_author() { - file=$1 user=$2 && - p4 changes -m 1 //depot/$file | grep -q $user -} - -make_change_by_user() { - file=$1 name=$2 email=$3 && - echo "username: a change by $name" >>"$file" && - git add "$file" && - git commit --author "$name <$email>" -m "a change by $name" -} - -# Test username support, submitting as user 'alice' -test_expect_success 'preserve users' ' - p4_add_user alice Alice && - p4_add_user bob Bob && - p4_grant_admin alice && - "$GITP4" clone --dest="$git" //depot && - test_when_finished cleanup_git && - ( - cd "$git" && - echo "username: a change by alice" >>file1 && - echo "username: a change by bob" >>file2 && - git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 && - git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 && - git config git-p4.skipSubmitEditCheck true && - P4EDITOR=touch P4USER=alice P4PASSWD=secret "$GITP4" commit --preserve-user && - p4_check_commit_author file1 alice && - p4_check_commit_author file2 bob - ) -' - -# Test username support, submitting as bob, who lacks admin rights. Should -# not submit change to p4 (git diff should show deltas). -test_expect_success 'refuse to preserve users without perms' ' - "$GITP4" clone --dest="$git" //depot && - test_when_finished cleanup_git && + chmod 755 badp4dir/p4 && ( - cd "$git" && - git config git-p4.skipSubmitEditCheck true && - echo "username-noperms: a change by alice" >>file1 && - git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 && - P4EDITOR=touch P4USER=bob P4PASSWD=secret && - export P4EDITOR P4USER P4PASSWD && - test_must_fail "$GITP4" commit --preserve-user && - ! git diff --exit-code HEAD..p4/master - ) + PATH="$TRASH_DIRECTORY/badp4dir:$PATH" && + export PATH && + test_expect_code 1 git p4 clone --dest="$git" //depot >errs 2>&1 + ) && + cat errs && + ! test_i18ngrep Traceback errs ' -# What happens with unknown author? Without allowMissingP4Users it should fail. -test_expect_success 'preserve user where author is unknown to p4' ' - "$GITP4" clone --dest="$git" //depot && +# Hide a file from p4d, make sure we catch its complaint. This won't fail in +# p4 changes, files, or describe; just in p4 print. If P4CLIENT is unset, the +# message will include "Librarian checkout". +test_expect_success 'exit gracefully for p4 server errors' ' + test_when_finished "mv \"$db\"/depot/file1,v,hidden \"$db\"/depot/file1,v" && + mv "$db"/depot/file1,v "$db"/depot/file1,v,hidden && test_when_finished cleanup_git && - ( - cd "$git" && - git config git-p4.skipSubmitEditCheck true && - echo "username-bob: a change by bob" >>file1 && - git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 && - echo "username-unknown: a change by charlie" >>file1 && - git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 && - P4EDITOR=touch P4USER=alice P4PASSWD=secret && - export P4EDITOR P4USER P4PASSWD && - test_must_fail "$GITP4" commit --preserve-user && - ! git diff --exit-code HEAD..p4/master && - - echo "$0: repeat with allowMissingP4Users enabled" && - git config git-p4.allowMissingP4Users true && - git config git-p4.preserveUser true && - "$GITP4" commit && - git diff --exit-code HEAD..p4/master && - p4_check_commit_author file1 alice - ) + test_expect_code 1 git p4 clone --dest="$git" //depot@1 >out 2>err && + test_i18ngrep "Error from p4 print" err ' -# If we're *not* using --preserve-user, git-p4 should warn if we're submitting -# changes that are not all ours. -# Test: user in p4 and user unknown to p4. -# Test: warning disabled and user is the same. -test_expect_success 'not preserving user with mixed authorship' ' - "$GITP4" clone --dest="$git" //depot && +test_expect_success 'clone --bare should make a bare repository' ' + rm -rf "$git" && + git p4 clone --dest="$git" --bare //depot && test_when_finished cleanup_git && ( cd "$git" && - git config git-p4.skipSubmitEditCheck true && - p4_add_user derek Derek && - - make_change_by_user usernamefile3 Derek derek@localhost && - P4EDITOR=cat P4USER=alice P4PASSWD=secret && - export P4EDITOR P4USER P4PASSWD && - "$GITP4" commit |\ - grep "git author derek@localhost does not match" && - - make_change_by_user usernamefile3 Charlie charlie@localhost && - "$GITP4" commit |\ - grep "git author charlie@localhost does not match" && - - make_change_by_user usernamefile3 alice alice@localhost && - "$GITP4" commit |\ - test_must_fail grep "git author.*does not match" && - - git config git-p4.skipUserNameCheck true && - make_change_by_user usernamefile3 Charlie charlie@localhost && - "$GITP4" commit |\ - test_must_fail grep "git author.*does not match" && - - p4_check_commit_author usernamefile3 alice + test_path_is_missing .git && + git config --get --bool core.bare true && + git rev-parse --verify refs/remotes/p4/master && + git rev-parse --verify refs/remotes/p4/HEAD && + git rev-parse --verify refs/heads/master && + git rev-parse --verify HEAD ) ' -marshal_dump() { - what=$1 - "$PYTHON_PATH" -c 'import marshal, sys; d = marshal.load(sys.stdin); print d["'$what'"]' -} - # Sleep a bit so that the top-most p4 change did not happen "now". Then # import the repo and make sure that the initial import has the same time # as the top-most change. @@ -313,7 +181,7 @@ test_expect_success 'initial import time from top change time' ' p4change=$(p4 -G changes -m 1 //depot/... | marshal_dump change) && p4time=$(p4 -G changes -m 1 //depot/... | marshal_dump time) && sleep 3 && - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && @@ -323,143 +191,15 @@ test_expect_success 'initial import time from top change time' ' ) ' -# Rename a file and confirm that rename is not detected in P4. -# Rename the new file again with detectRenames option enabled and confirm that -# this is detected in P4. -# Rename the new file again adding an extra line, configure a big threshold in -# detectRenames and confirm that rename is not detected in P4. -# Repeat, this time with a smaller threshold and confirm that the rename is -# detected in P4. -test_expect_success 'detect renames' ' - "$GITP4" clone --dest="$git" //depot@all && +test_expect_success 'unresolvable host in P4PORT should display error' ' test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && ( cd "$git" && - git config git-p4.skipSubmitEditCheck true && - - git mv file1 file4 && - git commit -a -m "Rename file1 to file4" && - git diff-tree -r -M HEAD && - "$GITP4" submit && - p4 filelog //depot/file4 && - p4 filelog //depot/file4 | test_must_fail grep -q "branch from" && - - git mv file4 file5 && - git commit -a -m "Rename file4 to file5" && - git diff-tree -r -M HEAD && - git config git-p4.detectRenames true && - "$GITP4" submit && - p4 filelog //depot/file5 && - p4 filelog //depot/file5 | grep -q "branch from //depot/file4" && - - git mv file5 file6 && - echo update >>file6 && - git add file6 && - git commit -a -m "Rename file5 to file6 with changes" && - git diff-tree -r -M HEAD && - level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") && - test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 && - git config git-p4.detectRenames $(($level + 2)) && - "$GITP4" submit && - p4 filelog //depot/file6 && - p4 filelog //depot/file6 | test_must_fail grep -q "branch from" && - - git mv file6 file7 && - echo update >>file7 && - git add file7 && - git commit -a -m "Rename file6 to file7 with changes" && - git diff-tree -r -M HEAD && - level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") && - test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 && - git config git-p4.detectRenames $(($level - 2)) && - "$GITP4" submit && - p4 filelog //depot/file7 && - p4 filelog //depot/file7 | grep -q "branch from //depot/file6" - ) -' - -# Copy a file and confirm that copy is not detected in P4. -# Copy a file with detectCopies option enabled and confirm that copy is not -# detected in P4. -# Modify and copy a file with detectCopies option enabled and confirm that copy -# is detected in P4. -# Copy a file with detectCopies and detectCopiesHarder options enabled and -# confirm that copy is detected in P4. -# Modify and copy a file, configure a bigger threshold in detectCopies and -# confirm that copy is not detected in P4. -# Modify and copy a file, configure a smaller threshold in detectCopies and -# confirm that copy is detected in P4. -test_expect_success 'detect copies' ' - "$GITP4" clone --dest="$git" //depot@all && - test_when_finished cleanup_git && - ( - cd "$git" && - git config git-p4.skipSubmitEditCheck true && - - cp file2 file8 && - git add file8 && - git commit -a -m "Copy file2 to file8" && - git diff-tree -r -C HEAD && - "$GITP4" submit && - p4 filelog //depot/file8 && - p4 filelog //depot/file8 | test_must_fail grep -q "branch from" && - - cp file2 file9 && - git add file9 && - git commit -a -m "Copy file2 to file9" && - git diff-tree -r -C HEAD && - git config git-p4.detectCopies true && - "$GITP4" submit && - p4 filelog //depot/file9 && - p4 filelog //depot/file9 | test_must_fail grep -q "branch from" && - - echo "file2" >>file2 && - cp file2 file10 && - git add file2 file10 && - git commit -a -m "Modify and copy file2 to file10" && - git diff-tree -r -C HEAD && - "$GITP4" submit && - p4 filelog //depot/file10 && - p4 filelog //depot/file10 | grep -q "branch from //depot/file" && - - cp file2 file11 && - git add file11 && - git commit -a -m "Copy file2 to file11" && - git diff-tree -r -C --find-copies-harder HEAD && - src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && - test "$src" = file10 && - git config git-p4.detectCopiesHarder true && - "$GITP4" submit && - p4 filelog //depot/file11 && - p4 filelog //depot/file11 | grep -q "branch from //depot/file" && - - cp file2 file12 && - echo "some text" >>file12 && - git add file12 && - git commit -a -m "Copy file2 to file12 with changes" && - git diff-tree -r -C --find-copies-harder HEAD && - level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") && - test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 && - src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && - test "$src" = file10 && - git config git-p4.detectCopies $(($level + 2)) && - "$GITP4" submit && - p4 filelog //depot/file12 && - p4 filelog //depot/file12 | test_must_fail grep -q "branch from" && - - cp file2 file13 && - echo "different text" >>file13 && - git add file13 && - git commit -a -m "Copy file2 to file13 with changes" && - git diff-tree -r -C --find-copies-harder HEAD && - level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") && - test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 && - src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && - test "$src" = file10 && - git config git-p4.detectCopies $(($level - 2)) && - "$GITP4" submit && - p4 filelog //depot/file13 && - p4 filelog //depot/file13 | grep -q "branch from //depot/file" + P4PORT=nosuchhost:65537 && + export P4PORT && + test_expect_code 1 git p4 sync >out 2>err && + grep "connect to nosuchhost" err ) ' diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh index a25f18d36a..2bf142d09c 100755 --- a/t/t9801-git-p4-branch.sh +++ b/t/t9801-git-p4-branch.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 p4 branching tests' +test_description='git p4 tests for p4 branches' . ./lib-git-p4.sh @@ -63,7 +63,7 @@ test_expect_success 'basic p4 branches' ' test_expect_success 'import main, no branch detection' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot/main@all && + git p4 clone --dest="$git" //depot/main@all && ( cd "$git" && git log --oneline --graph --decorate --all && @@ -74,7 +74,7 @@ test_expect_success 'import main, no branch detection' ' test_expect_success 'import branch1, no branch detection' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot/branch1@all && + git p4 clone --dest="$git" //depot/branch1@all && ( cd "$git" && git log --oneline --graph --decorate --all && @@ -85,7 +85,7 @@ test_expect_success 'import branch1, no branch detection' ' test_expect_success 'import branch2, no branch detection' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot/branch2@all && + git p4 clone --dest="$git" //depot/branch2@all && ( cd "$git" && git log --oneline --graph --decorate --all && @@ -96,7 +96,7 @@ test_expect_success 'import branch2, no branch detection' ' test_expect_success 'import depot, no branch detection' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot@all && + git p4 clone --dest="$git" //depot@all && ( cd "$git" && git log --oneline --graph --decorate --all && @@ -107,7 +107,7 @@ test_expect_success 'import depot, no branch detection' ' test_expect_success 'import depot, branch detection' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" --detect-branches //depot@all && + git p4 clone --dest="$git" --detect-branches //depot@all && ( cd "$git" && @@ -132,7 +132,7 @@ test_expect_success 'import depot, branch detection, branchList branch definitio ( cd "$git" && git config git-p4.branchList main:branch1 && - "$GITP4" clone --dest=. --detect-branches //depot@all && + git p4 clone --dest=. --detect-branches //depot@all && git log --oneline --graph --decorate --all && @@ -172,9 +172,9 @@ test_expect_success 'add simple p4 branches' ' echo file1 >file1 && echo file2 >file2 && p4 add file1 file2 && - p4 submit -d "branch1" && + p4 submit -d "Create branch1" && p4 integrate //depot/branch1/... //depot/branch2/... && - p4 submit -d "branch2" && + p4 submit -d "Integrate branch2 from branch1" && echo file3 >file3 && p4 add file3 && p4 submit -d "add file3 in branch1" && @@ -182,47 +182,325 @@ test_expect_success 'add simple p4 branches' ' echo update >>file2 && p4 submit -d "update file2 in branch1" && p4 integrate //depot/branch1/... //depot/branch3/... && - p4 submit -d "branch3" + p4 submit -d "Integrate branch3 from branch1" ) ' # Configure branches through git-config and clone them. # All files are tested to make sure branches were cloned correctly. # Finally, make an update to branch1 on P4 side to check if it is imported -# correctly by git-p4. -test_expect_success 'git-p4 clone simple branches' ' +# correctly by git p4. +test_expect_success 'git p4 clone simple branches' ' test_when_finished cleanup_git && test_create_repo "$git" && ( cd "$git" && git config git-p4.branchList branch1:branch2 && git config --add git-p4.branchList branch1:branch3 && - "$GITP4" clone --dest=. --detect-branches //depot@all && + git p4 clone --dest=. --detect-branches //depot@all && git log --all --graph --decorate --stat && git reset --hard p4/depot/branch1 && test -f file1 && test -f file2 && test -f file3 && - grep -q update file2 && + grep update file2 && git reset --hard p4/depot/branch2 && test -f file1 && test -f file2 && test ! -f file3 && - test_must_fail grep -q update file2 && + ! grep update file2 && git reset --hard p4/depot/branch3 && test -f file1 && test -f file2 && test -f file3 && - grep -q update file2 && + grep update file2 && cd "$cli" && cd branch1 && p4 edit file2 && echo file2_ >>file2 && - p4 submit -d "update file2 in branch3" && + p4 submit -d "update file2 in branch1" && + cd "$git" && + git reset --hard p4/depot/branch1 && + git p4 rebase && + grep file2_ file2 + ) +' + +# Create a complex branch structure in P4 depot to check if they are correctly +# cloned. The branches are created from older changelists to check if git p4 is +# able to correctly detect them. +# The final expected structure is: +# `branch1 +# | `- file1 +# | `- file2 (updated) +# | `- file3 +# `branch2 +# | `- file1 +# | `- file2 +# `branch3 +# | `- file1 +# | `- file2 (updated) +# | `- file3 +# `branch4 +# | `- file1 +# | `- file2 +# `branch5 +# `- file1 +# `- file2 +# `- file3 +test_expect_success 'git p4 add complex branches' ' + ( + cd "$cli" && + changelist=$(p4 changes -m1 //depot/... | cut -d" " -f2) && + changelist=$(($changelist - 5)) && + p4 integrate //depot/branch1/...@$changelist //depot/branch4/... && + p4 submit -d "Integrate branch4 from branch1@${changelist}" && + changelist=$(($changelist + 2)) && + p4 integrate //depot/branch1/...@$changelist //depot/branch5/... && + p4 submit -d "Integrate branch5 from branch1@${changelist}" + ) +' + +# Configure branches through git-config and clone them. git p4 will only be able +# to clone the original structure if it is able to detect the origin changelist +# of each branch. +test_expect_success 'git p4 clone complex branches' ' + test_when_finished cleanup_git && + test_create_repo "$git" && + ( cd "$git" && + git config git-p4.branchList branch1:branch2 && + git config --add git-p4.branchList branch1:branch3 && + git config --add git-p4.branchList branch1:branch4 && + git config --add git-p4.branchList branch1:branch5 && + git p4 clone --dest=. --detect-branches //depot@all && + git log --all --graph --decorate --stat && git reset --hard p4/depot/branch1 && - "$GITP4" rebase && - grep -q file2_ file2 + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + grep update file2 && + git reset --hard p4/depot/branch2 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_missing file3 && + ! grep update file2 && + git reset --hard p4/depot/branch3 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + grep update file2 && + git reset --hard p4/depot/branch4 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_missing file3 && + ! grep update file2 && + git reset --hard p4/depot/branch5 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + ! grep update file2 && + test_path_is_missing .git/git-p4-tmp + ) +' + +# Move branch3/file3 to branch4/file3 in a single changelist +test_expect_success 'git p4 submit to two branches in a single changelist' ' + ( + cd "$cli" && + p4 integrate //depot/branch3/file3 //depot/branch4/file3 && + p4 delete //depot/branch3/file3 && + p4 submit -d "Move branch3/file3 to branch4/file3" + ) +' + +# Confirm that changes to two branches done in a single changelist +# are correctly imported by git p4 +test_expect_success 'git p4 sync changes to two branches in the same changelist' ' + test_when_finished cleanup_git && + test_create_repo "$git" && + ( + cd "$git" && + git config git-p4.branchList branch1:branch2 && + git config --add git-p4.branchList branch1:branch3 && + git config --add git-p4.branchList branch1:branch4 && + git config --add git-p4.branchList branch1:branch5 && + git p4 clone --dest=. --detect-branches //depot@all && + git log --all --graph --decorate --stat && + git reset --hard p4/depot/branch1 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + grep update file2 && + git reset --hard p4/depot/branch2 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_missing file3 && + ! grep update file2 && + git reset --hard p4/depot/branch3 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_missing file3 && + grep update file2 && + git reset --hard p4/depot/branch4 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + ! grep update file2 && + git reset --hard p4/depot/branch5 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + ! grep update file2 && + test_path_is_missing .git/git-p4-tmp + ) +' + +# Create a branch by integrating a single file +test_expect_success 'git p4 file subset branch' ' + ( + cd "$cli" && + p4 integrate //depot/branch1/file1 //depot/branch6/file1 && + p4 submit -d "Integrate file1 alone from branch1 to branch6" + ) +' + +# Check if git p4 creates a new branch containing a single file, +# instead of keeping the old files from the original branch +test_expect_failure 'git p4 clone file subset branch' ' + test_when_finished cleanup_git && + test_create_repo "$git" && + ( + cd "$git" && + git config git-p4.branchList branch1:branch2 && + git config --add git-p4.branchList branch1:branch3 && + git config --add git-p4.branchList branch1:branch4 && + git config --add git-p4.branchList branch1:branch5 && + git config --add git-p4.branchList branch1:branch6 && + git p4 clone --dest=. --detect-branches //depot@all && + git log --all --graph --decorate --stat && + git reset --hard p4/depot/branch1 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + grep update file2 && + git reset --hard p4/depot/branch2 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_missing file3 && + ! grep update file2 && + git reset --hard p4/depot/branch3 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_missing file3 && + grep update file2 && + git reset --hard p4/depot/branch4 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + ! grep update file2 && + git reset --hard p4/depot/branch5 && + test_path_is_file file1 && + test_path_is_file file2 && + test_path_is_file file3 && + ! grep update file2 && + git reset --hard p4/depot/branch6 && + test_path_is_file file1 && + test_path_is_missing file2 && + test_path_is_missing file3 + ) +' + +# From a report in http://stackoverflow.com/questions/11893688 +# where --use-client-spec caused branch prefixes not to be removed; +# every file in git appeared into a subdirectory of the branch name. +test_expect_success 'use-client-spec detect-branches setup' ' + rm -rf "$cli" && + mkdir "$cli" && + ( + cd "$cli" && + client_view "//depot/usecs/... //client/..." && + mkdir b1 && + echo b1/b1-file1 >b1/b1-file1 && + p4 add b1/b1-file1 && + p4 submit -d "b1/b1-file1" && + + p4 integrate //depot/usecs/b1/... //depot/usecs/b2/... && + p4 submit -d "b1 -> b2" && + p4 branch -i <<-EOF && + Branch: b2 + View: //depot/usecs/b1/... //depot/usecs/b2/... + EOF + + echo b2/b2-file2 >b2/b2-file2 && + p4 add b2/b2-file2 && + p4 submit -d "b2/b2-file2" + ) +' + +test_expect_success 'use-client-spec detect-branches files in top-level' ' + test_when_finished cleanup_git && + test_create_repo "$git" && + ( + cd "$git" && + git p4 sync --detect-branches --use-client-spec //depot/usecs@all && + git checkout -b master p4/usecs/b1 && + test_path_is_file b1-file1 && + test_path_is_missing b2-file2 && + test_path_is_missing b1 && + test_path_is_missing b2 && + + git checkout -b b2 p4/usecs/b2 && + test_path_is_file b1-file1 && + test_path_is_file b2-file2 && + test_path_is_missing b1 && + test_path_is_missing b2 + ) +' + +test_expect_success 'use-client-spec detect-branches skips branches setup' ' + ( + cd "$cli" && + + p4 integrate //depot/usecs/b1/... //depot/usecs/b3/... && + p4 submit -d "b1 -> b3" && + p4 branch -i <<-EOF && + Branch: b3 + View: //depot/usecs/b1/... //depot/usecs/b3/... + EOF + + echo b3/b3-file3_1 >b3/b3-file3_1 && + echo b3/b3-file3_2 >b3/b3-file3_2 && + p4 add b3/b3-file3_1 && + p4 add b3/b3-file3_2 && + p4 submit -d "b3/b3-file3_1 b3/b3-file3_2" + ) +' + +test_expect_success 'use-client-spec detect-branches skips branches' ' + client_view "//depot/usecs/... //client/..." \ + "-//depot/usecs/b3/... //client/b3/..." && + test_when_finished cleanup_git && + test_create_repo "$git" && + ( + cd "$git" && + git p4 sync --detect-branches --use-client-spec //depot/usecs@all && + test_must_fail git rev-parse refs/remotes/p4/usecs/b3 + ) +' + +test_expect_success 'use-client-spec detect-branches skips files in branches' ' + client_view "//depot/usecs/... //client/..." \ + "-//depot/usecs/b3/b3-file3_1 //client/b3/b3-file3_1" && + test_when_finished cleanup_git && + test_create_repo "$git" && + ( + cd "$git" && + git p4 sync --detect-branches --use-client-spec //depot/usecs@all && + git checkout -b master p4/usecs/b3 && + test_path_is_file b1-file1 && + test_path_is_file b3-file3_2 && + test_path_is_missing b3-file3_1 ) ' diff --git a/t/t9802-git-p4-filetype.sh b/t/t9802-git-p4-filetype.sh index 992bb8cf0b..a82744bab0 100755 --- a/t/t9802-git-p4-filetype.sh +++ b/t/t9802-git-p4-filetype.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 p4 filetype tests' +test_description='git p4 filetype tests' . ./lib-git-p4.sh @@ -8,6 +8,123 @@ test_expect_success 'start p4d' ' start_p4d ' +# +# This series of tests checks newline handling Both p4 and +# git store newlines as \n, and have options to choose how +# newlines appear in checked-out files. +# +test_expect_success 'p4 client newlines, unix' ' + ( + cd "$cli" && + p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i && + printf "unix\ncrlf\n" >f-unix && + printf "unix\r\ncrlf\r\n" >f-unix-as-crlf && + p4 add -t text f-unix && + p4 submit -d f-unix && + + # LineEnd: unix; should be no change after sync + cp f-unix f-unix-orig && + p4 sync -f && + test_cmp f-unix-orig f-unix && + + # make sure stored in repo as unix newlines + # use sed to eat python-appended newline + p4 -G print //depot/f-unix | marshal_dump data 2 |\ + sed \$d >f-unix-p4-print && + test_cmp f-unix-orig f-unix-p4-print && + + # switch to win, make sure lf -> crlf + p4 client -o | sed "/LineEnd/s/:.*/:win/" | p4 client -i && + p4 sync -f && + test_cmp f-unix-as-crlf f-unix + ) +' + +test_expect_success 'p4 client newlines, win' ' + ( + cd "$cli" && + p4 client -o | sed "/LineEnd/s/:.*/:win/" | p4 client -i && + printf "win\r\ncrlf\r\n" >f-win && + printf "win\ncrlf\n" >f-win-as-lf && + p4 add -t text f-win && + p4 submit -d f-win && + + # LineEnd: win; should be no change after sync + cp f-win f-win-orig && + p4 sync -f && + test_cmp f-win-orig f-win && + + # make sure stored in repo as unix newlines + # use sed to eat python-appened newline + p4 -G print //depot/f-win | marshal_dump data 2 |\ + sed \$d >f-win-p4-print && + test_cmp f-win-as-lf f-win-p4-print && + + # switch to unix, make sure lf -> crlf + p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i && + p4 sync -f && + test_cmp f-win-as-lf f-win + ) +' + +test_expect_success 'ensure blobs store only lf newlines' ' + test_when_finished cleanup_git && + ( + cd "$git" && + git init && + git p4 sync //depot@all && + + # verify the files in .git are stored only with newlines + o=$(git ls-tree p4/master -- f-unix | cut -f1 | cut -d\ -f3) && + git cat-file blob $o >f-unix-blob && + test_cmp "$cli"/f-unix-orig f-unix-blob && + + o=$(git ls-tree p4/master -- f-win | cut -f1 | cut -d\ -f3) && + git cat-file blob $o >f-win-blob && + test_cmp "$cli"/f-win-as-lf f-win-blob && + + rm f-unix-blob f-win-blob + ) +' + +test_expect_success 'gitattributes setting eol=lf produces lf newlines' ' + test_when_finished cleanup_git && + ( + # checkout the files and make sure core.eol works as planned + cd "$git" && + git init && + echo "* eol=lf" >.gitattributes && + git p4 sync //depot@all && + git checkout -b master p4/master && + test_cmp "$cli"/f-unix-orig f-unix && + test_cmp "$cli"/f-win-as-lf f-win + ) +' + +test_expect_success 'gitattributes setting eol=crlf produces crlf newlines' ' + test_when_finished cleanup_git && + ( + # checkout the files and make sure core.eol works as planned + cd "$git" && + git init && + echo "* eol=crlf" >.gitattributes && + git p4 sync //depot@all && + git checkout -b master p4/master && + test_cmp "$cli"/f-unix-as-crlf f-unix && + test_cmp "$cli"/f-win-orig f-win + ) +' + +test_expect_success 'crlf cleanup' ' + ( + cd "$cli" && + rm f-unix-orig f-unix-as-crlf && + rm f-win-orig f-win-as-lf && + p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i && + p4 sync -f + ) +' + test_expect_success 'utf-16 file create' ' ( cd "$cli" && @@ -37,7 +154,7 @@ test_expect_success 'utf-16 file create' ' test_expect_success 'utf-16 file test' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot@all && + git p4 clone --dest="$git" //depot@all && ( cd "$git" && @@ -84,7 +201,7 @@ test_expect_success 'keyword file test' ' build_smush && test_when_finished rm -f k_smush.py ko_smush.py && test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot@all && + git p4 clone --dest="$git" //depot@all && ( cd "$git" && @@ -94,7 +211,7 @@ test_expect_success 'keyword file test' ' "$PYTHON_PATH" "$TRASH_DIRECTORY/ko_smush.py" <"$cli/k-text-ko" >cli-k-text-ko-smush && test_cmp cli-k-text-ko-smush k-text-ko && - # utf16, even though p4 expands keywords, git-p4 does not + # utf16, even though p4 expands keywords, git p4 does not # try to undo that test_cmp "$cli/k-utf16-k" k-utf16-k && test_cmp "$cli/k-utf16-ko" k-utf16-ko @@ -105,12 +222,13 @@ build_gendouble() { cat >gendouble.py <<-\EOF import sys import struct - import array - s = array.array("c", '\0' * 26) - struct.pack_into(">L", s, 0, 0x00051607) # AppleDouble - struct.pack_into(">L", s, 4, 0x00020000) # version 2 - s.tofile(sys.stdout) + s = struct.pack(">LL18s", + 0x00051607, # AppleDouble + 0x00020000, # version 2 + "" # pad to 26 bytes + ) + sys.stdout.write(s) EOF } @@ -125,7 +243,7 @@ test_expect_success 'ignore apple' ' p4 submit -d appledouble ) && test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot@all && + git p4 clone --dest="$git" //depot@all && ( cd "$git" && test ! -f double.png diff --git a/t/t9803-git-p4-shell-metachars.sh b/t/t9803-git-p4-shell-metachars.sh index db04375a13..fbacff34fe 100755 --- a/t/t9803-git-p4-shell-metachars.sh +++ b/t/t9803-git-p4-shell-metachars.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 transparency to shell metachars in filenames' +test_description='git p4 transparency to shell metachars in filenames' . ./lib-git-p4.sh @@ -18,7 +18,7 @@ test_expect_success 'init depot' ' ' test_expect_success 'shell metachars in filenames' ' - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && @@ -28,7 +28,7 @@ test_expect_success 'shell metachars in filenames' ' echo f2 >"file with spaces" && git add "file with spaces" && git commit -m "add files" && - P4EDITOR=touch "$GITP4" submit + P4EDITOR=touch git p4 submit ) && ( cd "$cli" && @@ -39,7 +39,7 @@ test_expect_success 'shell metachars in filenames' ' ' test_expect_success 'deleting with shell metachars' ' - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && @@ -47,7 +47,7 @@ test_expect_success 'deleting with shell metachars' ' git rm foo\$bar && git rm file\ with\ spaces && git commit -m "remove files" && - P4EDITOR=touch "$GITP4" submit + P4EDITOR=touch git p4 submit ) && ( cd "$cli" && @@ -57,6 +57,54 @@ test_expect_success 'deleting with shell metachars' ' ) ' +# Create a branch with a shell metachar in its name +# +# 1. //depot/main +# 2. //depot/branch$3 + +test_expect_success 'branch with shell char' ' + test_when_finished cleanup_git && + test_create_repo "$git" && + ( + cd "$cli" && + + mkdir -p main && + + echo f1 >main/f1 && + p4 add main/f1 && + p4 submit -d "main/f1" && + + p4 integrate //depot/main/... //depot/branch\$3/... && + p4 submit -d "integrate main to branch\$3" && + + echo f1 >branch\$3/shell_char_branch_file && + p4 add branch\$3/shell_char_branch_file && + p4 submit -d "branch\$3/shell_char_branch_file" && + + p4 branch -i <<-EOF && + Branch: branch\$3 + View: //depot/main/... //depot/branch\$3/... + EOF + + p4 edit main/f1 && + echo "a change" >> main/f1 && + p4 submit -d "a change" main/f1 && + + p4 integrate -b branch\$3 && + p4 resolve -am branch\$3/... && + p4 submit -d "integrate main to branch\$3" && + + cd "$git" && + + git config git-p4.branchList main:branch\$3 && + git p4 clone --dest=. --detect-branches //depot@all && + git log --all --graph --decorate --stat && + git reset --hard p4/depot/branch\$3 && + test -f shell_char_branch_file && + test -f f1 + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9804-git-p4-label.sh b/t/t9804-git-p4-label.sh new file mode 100755 index 0000000000..e30f80e617 --- /dev/null +++ b/t/t9804-git-p4-label.sh @@ -0,0 +1,115 @@ +#!/bin/sh + +test_description='git p4 label tests' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +# Basic p4 label tests. +# +# Note: can't have more than one label per commit - others +# are silently discarded. +# +test_expect_success 'basic p4 labels' ' + test_when_finished cleanup_git && + ( + cd "$cli" && + mkdir -p main && + + echo f1 >main/f1 && + p4 add main/f1 && + p4 submit -d "main/f1" && + + echo f2 >main/f2 && + p4 add main/f2 && + p4 submit -d "main/f2" && + + echo f3 >main/file_with_\$metachar && + p4 add main/file_with_\$metachar && + p4 submit -d "file with metachar" && + + p4 tag -l tag_f1_only main/f1 && + p4 tag -l tag_with\$_shell_char main/... && + + echo f4 >main/f4 && + p4 add main/f4 && + p4 submit -d "main/f4" && + + p4 label -i <<-EOF && + Label: long_label + Description: + A Label first line + A Label second line + View: //depot/... + EOF + + p4 tag -l long_label ... && + + p4 labels ... && + + git p4 clone --dest="$git" --detect-labels //depot@all && + cd "$git" && + + git tag && + git tag >taglist && + test_line_count = 3 taglist && + + cd main && + git checkout tag_tag_f1_only && + ! test -f f2 && + git checkout tag_tag_with\$_shell_char && + test -f f1 && test -f f2 && test -f file_with_\$metachar && + + git show tag_long_label | grep -q "A Label second line" + ) +' + +# Test some label corner cases: +# +# - two tags on the same file; both should be available +# - a tag that is only on one file; this kind of tag +# cannot be imported (at least not easily). + +test_expect_failure 'two labels on the same changelist' ' + test_when_finished cleanup_git && + ( + cd "$cli" && + mkdir -p main && + + p4 edit main/f1 main/f2 && + echo "hello world" >main/f1 && + echo "not in the tag" >main/f2 && + p4 submit -d "main/f[12]: testing two labels" && + + p4 tag -l tag_f1_1 main/... && + p4 tag -l tag_f1_2 main/... && + + p4 labels ... && + + git p4 clone --dest="$git" --detect-labels //depot@all && + cd "$git" && + + git tag | grep tag_f1 && + git tag | grep -q tag_f1_1 && + git tag | grep -q tag_f1_2 && + + cd main && + + git checkout tag_tag_f1_1 && + ls && + test -f f1 && + + git checkout tag_tag_f1_2 && + ls && + test -f f1 + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9805-git-p4-skip-submit-edit.sh b/t/t9805-git-p4-skip-submit-edit.sh index df929e0555..ff2cc79701 100755 --- a/t/t9805-git-p4-skip-submit-edit.sh +++ b/t/t9805-git-p4-skip-submit-edit.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 skipSubmitEdit config variables' +test_description='git p4 skipSubmitEdit config variables' . ./lib-git-p4.sh @@ -19,33 +19,33 @@ test_expect_success 'init depot' ' # this works because EDITOR is set to : test_expect_success 'no config, unedited, say yes' ' - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && echo line >>file1 && git commit -a -m "change 2" && - echo y | "$GITP4" submit && + echo y | git p4 submit && p4 changes //depot/... >wc && test_line_count = 2 wc ) ' test_expect_success 'no config, unedited, say no' ' - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && echo line >>file1 && git commit -a -m "change 3 (not really)" && - printf "bad response\nn\n" | "$GITP4" submit && + printf "bad response\nn\n" | test_expect_code 1 git p4 submit && p4 changes //depot/... >wc && test_line_count = 2 wc ) ' test_expect_success 'skipSubmitEdit' ' - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && @@ -54,21 +54,21 @@ test_expect_success 'skipSubmitEdit' ' git config core.editor /bin/false && echo line >>file1 && git commit -a -m "change 3" && - "$GITP4" submit && + git p4 submit && p4 changes //depot/... >wc && test_line_count = 3 wc ) ' test_expect_success 'skipSubmitEditCheck' ' - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && git config git-p4.skipSubmitEditCheck true && echo line >>file1 && git commit -a -m "change 4" && - "$GITP4" submit && + git p4 submit && p4 changes //depot/... >wc && test_line_count = 4 wc ) @@ -76,22 +76,21 @@ test_expect_success 'skipSubmitEditCheck' ' # check the normal case, where the template really is edited test_expect_success 'no config, edited' ' - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && test_when_finished cleanup_git && - ed="$TRASH_DIRECTORY/ed.sh" && - test_when_finished "rm \"$ed\"" && - cat >"$ed" <<-EOF && + test_when_finished "rm ed.sh" && + cat >ed.sh <<-EOF && #!$SHELL_PATH sleep 1 touch "\$1" exit 0 EOF - chmod 755 "$ed" && + chmod 755 ed.sh && ( cd "$git" && echo line >>file1 && git commit -a -m "change 5" && - EDITOR="\"$ed\"" "$GITP4" submit && + P4EDITOR="" EDITOR="\"$TRASH_DIRECTORY/ed.sh\"" git p4 submit && p4 changes //depot/... >wc && test_line_count = 5 wc ) diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index 1f1952a657..254d428b73 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 options' +test_description='git p4 options' . ./lib-git-p4.sh @@ -24,25 +24,112 @@ test_expect_success 'init depot' ' ' test_expect_success 'clone no --git-dir' ' - test_must_fail "$GITP4" clone --git-dir=xx //depot + test_must_fail git p4 clone --git-dir=xx //depot ' -test_expect_success 'clone --branch' ' - "$GITP4" clone --branch=refs/remotes/p4/sb --dest="$git" //depot && +test_expect_success 'clone --branch should checkout master' ' + git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && - git ls-files >files && - test_line_count = 0 files && - test_path_is_file .git/refs/remotes/p4/sb + git rev-parse refs/remotes/p4/sb >sb && + git rev-parse refs/heads/master >master && + test_cmp sb master && + git rev-parse HEAD >head && + test_cmp sb head + ) +' + +test_expect_success 'sync when no master branch prints a nice error' ' + test_when_finished cleanup_git && + git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot@2 && + ( + cd "$git" && + test_must_fail git p4 sync 2>err && + grep "Error: no branch refs/remotes/p4/master" err + ) +' + +test_expect_success 'sync --branch builds the full ref name correctly' ' + test_when_finished cleanup_git && + ( + cd "$git" && + git init && + + git p4 sync --branch=b1 //depot && + git rev-parse --verify refs/remotes/p4/b1 && + git p4 sync --branch=p4/b2 //depot && + git rev-parse --verify refs/remotes/p4/b2 && + + git p4 sync --import-local --branch=h1 //depot && + git rev-parse --verify refs/heads/p4/h1 && + git p4 sync --import-local --branch=p4/h2 //depot && + git rev-parse --verify refs/heads/p4/h2 && + + git p4 sync --branch=refs/stuff //depot && + git rev-parse --verify refs/stuff + ) +' + +# engages --detect-branches code, which will do filename filtering so +# no sync to either b1 or b2 +test_expect_success 'sync when two branches but no master should noop' ' + test_when_finished cleanup_git && + ( + cd "$git" && + git init && + git p4 sync --branch=refs/remotes/p4/b1 //depot@2 && + git p4 sync --branch=refs/remotes/p4/b2 //depot@2 && + git p4 sync && + git show -s --format=%s refs/remotes/p4/b1 >show && + grep "Initial import" show && + git show -s --format=%s refs/remotes/p4/b2 >show && + grep "Initial import" show + ) +' + +test_expect_success 'sync --branch updates specific branch, no detection' ' + test_when_finished cleanup_git && + ( + cd "$git" && + git init && + git p4 sync --branch=b1 //depot@2 && + git p4 sync --branch=b2 //depot@2 && + git p4 sync --branch=b2 && + git show -s --format=%s refs/remotes/p4/b1 >show && + grep "Initial import" show && + git show -s --format=%s refs/remotes/p4/b2 >show && + grep "change 3" show + ) +' + +# allows using the refname "p4" as a short name for p4/master +test_expect_success 'clone creates HEAD symbolic reference' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git rev-parse --verify refs/remotes/p4/master >master && + git rev-parse --verify p4 >p4 && + test_cmp master p4 + ) +' + +test_expect_success 'clone --branch creates HEAD symbolic reference' ' + git p4 clone --branch=refs/remotes/p4/sb --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git rev-parse --verify refs/remotes/p4/sb >sb && + git rev-parse --verify p4 >p4 && + test_cmp sb p4 ) ' test_expect_success 'clone --changesfile' ' - cf="$TRASH_DIRECTORY/cf" && - test_when_finished "rm \"$cf\"" && - printf "1\n3\n" >"$cf" && - "$GITP4" clone --changesfile="$cf" --dest="$git" //depot && + test_when_finished "rm cf" && + printf "1\n3\n" >cf && + git p4 clone --changesfile="$TRASH_DIRECTORY/cf" --dest="$git" //depot && test_when_finished cleanup_git && ( cd "$git" && @@ -55,17 +142,16 @@ test_expect_success 'clone --changesfile' ' ' test_expect_success 'clone --changesfile, @all' ' - cf="$TRASH_DIRECTORY/cf" && - test_when_finished "rm \"$cf\"" && - printf "1\n3\n" >"$cf" && - test_must_fail "$GITP4" clone --changesfile="$cf" --dest="$git" //depot@all + test_when_finished "rm cf" && + printf "1\n3\n" >cf && + test_must_fail git p4 clone --changesfile="$TRASH_DIRECTORY/cf" --dest="$git" //depot@all ' # imports both master and p4/master in refs/heads # requires --import-local on sync to find p4 refs/heads # does not update master on sync, just p4/master test_expect_success 'clone/sync --import-local' ' - "$GITP4" clone --import-local --dest="$git" //depot@1,2 && + git p4 clone --import-local --dest="$git" //depot@1,2 && test_when_finished cleanup_git && ( cd "$git" && @@ -73,9 +159,9 @@ test_expect_success 'clone/sync --import-local' ' test_line_count = 2 lines && git log --oneline refs/heads/p4/master >lines && test_line_count = 2 lines && - test_must_fail "$GITP4" sync && + test_must_fail git p4 sync && - "$GITP4" sync --import-local && + git p4 sync --import-local && git log --oneline refs/heads/master >lines && test_line_count = 2 lines && git log --oneline refs/heads/p4/master >lines && @@ -84,7 +170,7 @@ test_expect_success 'clone/sync --import-local' ' ' test_expect_success 'clone --max-changes' ' - "$GITP4" clone --dest="$git" --max-changes 2 //depot@all && + git p4 clone --dest="$git" --max-changes 2 //depot@all && test_when_finished cleanup_git && ( cd "$git" && @@ -101,7 +187,7 @@ test_expect_success 'clone --keep-path' ' p4 add sub/dir/f4 && p4 submit -d "change 4" ) && - "$GITP4" clone --dest="$git" --keep-path //depot/sub/dir@all && + git p4 clone --dest="$git" --keep-path //depot/sub/dir@all && test_when_finished cleanup_git && ( cd "$git" && @@ -109,7 +195,7 @@ test_expect_success 'clone --keep-path' ' test_path_is_file sub/dir/f4 ) && cleanup_git && - "$GITP4" clone --dest="$git" //depot/sub/dir@all && + git p4 clone --dest="$git" //depot/sub/dir@all && ( cd "$git" && test_path_is_file f4 && @@ -126,40 +212,60 @@ test_expect_success 'clone --use-client-spec' ' ( # big usage message exec >/dev/null && - test_must_fail "$GITP4" clone --dest="$git" --use-client-spec + test_must_fail git p4 clone --dest="$git" --use-client-spec ) && + # build a different client cli2="$TRASH_DIRECTORY/cli2" && mkdir -p "$cli2" && test_when_finished "rmdir \"$cli2\"" && + test_when_finished cleanup_git && ( - cd "$cli2" && - p4 client -i <<-EOF - Client: client2 - Description: client2 - Root: $cli2 - View: //depot/sub/... //client2/bus/... - EOF - ) && - P4CLIENT=client2 && + # group P4CLIENT and cli changes in a sub-shell + P4CLIENT=client2 && + cli="$cli2" && + client_view "//depot/sub/... //client2/bus/..." && + git p4 clone --dest="$git" --use-client-spec //depot/... && + ( + cd "$git" && + test_path_is_file bus/dir/f4 && + test_path_is_missing file1 + ) && + cleanup_git && + # same thing again, this time with variable instead of option + ( + cd "$git" && + git init && + git config git-p4.useClientSpec true && + git p4 sync //depot/... && + git checkout -b master p4/master && + test_path_is_file bus/dir/f4 && + test_path_is_missing file1 + ) + ) +' + +test_expect_success 'submit works with no p4/master' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" --use-client-spec //depot/... && + git p4 clone --branch=b1 //depot@1,2 --destination="$git" && ( cd "$git" && - test_path_is_file bus/dir/f4 && - test_path_is_file file1 - ) && - cleanup_git && + test_commit submit-1-branch && + git config git-p4.skipSubmitEdit true && + git p4 submit --branch=b1 + ) +' - # same thing again, this time with variable instead of option - mkdir "$git" && +# The sync/rebase part post-submit will engage detect-branches +# machinery which will not do anything in this particular test. +test_expect_success 'submit works with two branches' ' + test_when_finished cleanup_git && + git p4 clone --branch=b1 //depot@1,2 --destination="$git" && ( cd "$git" && - git init && - git config git-p4.useClientSpec true && - "$GITP4" sync //depot/... && - git checkout -b master p4/master && - test_path_is_file bus/dir/f4 && - test_path_is_file file1 + git p4 sync --branch=b2 //depot@1,3 && + test_commit submit-2-branches && + git config git-p4.skipSubmitEdit true && + git p4 submit ) ' diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh index b1f61e3db5..1fb7bc7cf9 100755 --- a/t/t9807-git-p4-submit.sh +++ b/t/t9807-git-p4-submit.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 submit' +test_description='git p4 submit' . ./lib-git-p4.sh @@ -17,9 +17,19 @@ test_expect_success 'init depot' ' ) ' +test_expect_failure 'is_cli_file_writeable function' ' + ( + cd "$cli" && + echo a >a && + is_cli_file_writeable a && + ! is_cli_file_writeable file1 && + rm a + ) +' + test_expect_success 'submit with no client dir' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && ( cd "$git" && echo file2 >file2 && @@ -27,64 +37,381 @@ test_expect_success 'submit with no client dir' ' git commit -m "git commit 2" && rm -rf "$cli" && git config git-p4.skipSubmitEdit true && - "$GITP4" submit + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file file1 && + test_path_is_file file2 ) ' # make two commits, but tell it to apply only from HEAD^ test_expect_success 'submit --origin' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && ( cd "$git" && test_commit "file3" && test_commit "file4" && git config git-p4.skipSubmitEdit true && - "$GITP4" submit --origin=HEAD^ + git p4 submit --origin=HEAD^ ) && ( cd "$cli" && - p4 sync && test_path_is_missing "file3.t" && test_path_is_file "file4.t" ) ' +test_expect_success 'submit --dry-run' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + test_commit "dry-run1" && + test_commit "dry-run2" && + git p4 submit --dry-run >out && + test_i18ngrep "Would apply" out + ) && + ( + cd "$cli" && + test_path_is_missing "dry-run1.t" && + test_path_is_missing "dry-run2.t" + ) +' + +test_expect_success 'submit --dry-run --export-labels' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo dry-run1 >dry-run1 && + git add dry-run1 && + git commit -m "dry-run1" dry-run1 && + git config git-p4.skipSubmitEdit true && + git p4 submit && + echo dry-run2 >dry-run2 && + git add dry-run2 && + git commit -m "dry-run2" dry-run2 && + git tag -m "dry-run-tag1" dry-run-tag1 HEAD^ && + git p4 submit --dry-run --export-labels >out && + test_i18ngrep "Would create p4 label" out + ) && + ( + cd "$cli" && + test_path_is_file "dry-run1" && + test_path_is_missing "dry-run2" + ) +' + test_expect_success 'submit with allowSubmit' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && ( cd "$git" && test_commit "file5" && git config git-p4.skipSubmitEdit true && git config git-p4.allowSubmit "nobranch" && - test_must_fail "$GITP4" submit && + test_must_fail git p4 submit && git config git-p4.allowSubmit "nobranch,master" && - "$GITP4" submit + git p4 submit ) ' test_expect_success 'submit with master branch name from argv' ' test_when_finished cleanup_git && - "$GITP4" clone --dest="$git" //depot && + git p4 clone --dest="$git" //depot && ( cd "$git" && test_commit "file6" && git config git-p4.skipSubmitEdit true && - test_must_fail "$GITP4" submit nobranch && + test_must_fail git p4 submit nobranch && git branch otherbranch && git reset --hard HEAD^ && test_commit "file7" && - "$GITP4" submit otherbranch + git p4 submit otherbranch ) && ( cd "$cli" && - p4 sync && test_path_is_file "file6.t" && test_path_is_missing "file7.t" ) ' +# +# Basic submit tests, the five handled cases +# + +test_expect_success 'submit modify' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + echo line >>file1 && + git add file1 && + git commit -m file1 && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file file1 && + test_line_count = 2 file1 + ) +' + +test_expect_success 'submit add' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + echo file13 >file13 && + git add file13 && + git commit -m file13 && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file file13 + ) +' + +test_expect_success 'submit delete' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + git rm file4.t && + git commit -m "delete file4.t" && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing file4.t + ) +' + +test_expect_success 'submit copy' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.detectCopies true && + git config git-p4.detectCopiesHarder true && + cp file5.t file5.ta && + git add file5.ta && + git commit -m "copy to file5.ta" && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file file5.ta && + ! is_cli_file_writeable file5.ta + ) +' + +test_expect_success 'submit rename' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.detectRenames true && + git mv file6.t file6.ta && + git commit -m "rename file6.t to file6.ta" && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing file6.t && + test_path_is_file file6.ta && + ! is_cli_file_writeable file6.ta + ) +' + +# +# Converting git commit message to p4 change description, including +# parsing out the optional Jobs: line. +# +test_expect_success 'simple one-line description' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo desc2 >desc2 && + git add desc2 && + cat >msg <<-EOF && + One-line description line for desc2. + EOF + git commit -F - <msg && + git config git-p4.skipSubmitEdit true && + git p4 submit && + change=$(p4 -G changes -m 1 //depot/... | \ + marshal_dump change) && + # marshal_dump always adds a newline + p4 -G describe $change | marshal_dump desc | sed \$d >pmsg && + test_cmp msg pmsg + ) +' + +test_expect_success 'description with odd formatting' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo desc3 >desc3 && + git add desc3 && + ( + printf "subject line\n\n\tExtra tab\nline.\n\n" && + printf "Description:\n\tBogus description marker\n\n" && + # git commit eats trailing newlines; only use one + printf "Files:\n\tBogus descs marker\n" + ) >msg && + git commit -F - <msg && + git config git-p4.skipSubmitEdit true && + git p4 submit && + change=$(p4 -G changes -m 1 //depot/... | \ + marshal_dump change) && + # marshal_dump always adds a newline + p4 -G describe $change | marshal_dump desc | sed \$d >pmsg && + test_cmp msg pmsg + ) +' + +make_job() { + name="$1" && + tab="$(printf \\t)" && + p4 job -o | \ + sed -e "/^Job:/s/.*/Job: $name/" \ + -e "/^Description/{ n; s/.*/$tab job text/; }" | \ + p4 job -i +} + +test_expect_success 'description with Jobs section at end' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo desc4 >desc4 && + git add desc4 && + echo 6060842 >jobname && + ( + printf "subject line\n\n\tExtra tab\nline.\n\n" && + printf "Files:\n\tBogus files marker\n" && + printf "Junk: 3164175\n" && + printf "Jobs: $(cat jobname)\n" + ) >msg && + git commit -F - <msg && + git config git-p4.skipSubmitEdit true && + # build a job + make_job $(cat jobname) && + git p4 submit && + change=$(p4 -G changes -m 1 //depot/... | \ + marshal_dump change) && + # marshal_dump always adds a newline + p4 -G describe $change | marshal_dump desc | sed \$d >pmsg && + # make sure Jobs line and all following is gone + sed "/^Jobs:/,\$d" msg >jmsg && + test_cmp jmsg pmsg && + # make sure p4 knows about job + p4 -G describe $change | marshal_dump job0 >job0 && + test_cmp jobname job0 + ) +' + +test_expect_success 'description with Jobs and values on separate lines' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo desc5 >desc5 && + git add desc5 && + echo PROJ-6060842 >jobname1 && + echo PROJ-6060847 >jobname2 && + ( + printf "subject line\n\n\tExtra tab\nline.\n\n" && + printf "Files:\n\tBogus files marker\n" && + printf "Junk: 3164175\n" && + printf "Jobs:\n" && + printf "\t$(cat jobname1)\n" && + printf "\t$(cat jobname2)\n" + ) >msg && + git commit -F - <msg && + git config git-p4.skipSubmitEdit true && + # build two jobs + make_job $(cat jobname1) && + make_job $(cat jobname2) && + git p4 submit && + change=$(p4 -G changes -m 1 //depot/... | \ + marshal_dump change) && + # marshal_dump always adds a newline + p4 -G describe $change | marshal_dump desc | sed \$d >pmsg && + # make sure Jobs line and all following is gone + sed "/^Jobs:/,\$d" msg >jmsg && + test_cmp jmsg pmsg && + # make sure p4 knows about the two jobs + p4 -G describe $change >change && + ( + marshal_dump job0 <change && + marshal_dump job1 <change + ) | sort >jobs && + cat jobname1 jobname2 | sort >expected && + test_cmp expected jobs + ) +' + +test_expect_success 'description with Jobs section and bogus following text' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo desc6 >desc6 && + git add desc6 && + echo 6060843 >jobname && + ( + printf "subject line\n\n\tExtra tab\nline.\n\n" && + printf "Files:\n\tBogus files marker\n" && + printf "Junk: 3164175\n" && + printf "Jobs: $(cat jobname)\n" && + printf "MoreJunk: 3711\n" + ) >msg && + git commit -F - <msg && + git config git-p4.skipSubmitEdit true && + # build a job + make_job $(cat jobname) && + test_must_fail git p4 submit 2>err && + test_i18ngrep "Unknown field name" err + ) && + ( + cd "$cli" && + p4 revert desc6 && + rm desc6 + ) +' + +test_expect_success 'submit --prepare-p4-only' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo prep-only-add >prep-only-add && + git add prep-only-add && + git commit -m "prep only add" && + git p4 submit --prepare-p4-only >out && + test_i18ngrep "prepared for submission" out && + test_i18ngrep "must be deleted" out + ) && + ( + cd "$cli" && + test_path_is_file prep-only-add && + p4 fstat -T action prep-only-add | grep -w add + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9808-git-p4-chdir.sh b/t/t9808-git-p4-chdir.sh index f0022839c7..11d2b5102c 100755 --- a/t/t9808-git-p4-chdir.sh +++ b/t/t9808-git-p4-chdir.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 relative chdir' +test_description='git p4 relative chdir' . ./lib-git-p4.sh @@ -21,24 +21,65 @@ test_expect_success 'init depot' ' # environment variable is set test_expect_success 'P4CONFIG and absolute dir clone' ' printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config && - test_when_finished "rm \"$TRASH_DIRECTORY/p4config\"" && + test_when_finished "rm p4config" && test_when_finished cleanup_git && ( P4CONFIG=p4config && export P4CONFIG && sane_unset P4PORT P4CLIENT && - "$GITP4" clone --verbose --dest="$git" //depot + git p4 clone --verbose --dest="$git" //depot ) ' # same thing, but with relative directory name, note missing $ on --dest test_expect_success 'P4CONFIG and relative dir clone' ' printf "P4PORT=$P4PORT\nP4CLIENT=$P4CLIENT\n" >p4config && - test_when_finished "rm \"$TRASH_DIRECTORY/p4config\"" && + test_when_finished "rm p4config" && test_when_finished cleanup_git && ( P4CONFIG=p4config && export P4CONFIG && sane_unset P4PORT P4CLIENT && - "$GITP4" clone --verbose --dest="git" //depot + git p4 clone --verbose --dest="git" //depot + ) +' + +# Common setup using .p4config to set P4CLIENT and P4PORT breaks +# if clone destination is relative. Make sure that chdir() expands +# the relative path in --dest to absolute. +test_expect_success 'p4 client root would be relative due to clone --dest' ' + test_when_finished cleanup_git && + ( + echo P4PORT=$P4PORT >git/.p4config && + P4CONFIG=.p4config && + export P4CONFIG && + unset P4PORT && + git p4 clone --dest="git" //depot + ) +' + +# When the p4 client Root is a symlink, make sure chdir() does not use +# getcwd() to convert it to a physical path. +test_expect_success SYMLINKS 'p4 client root symlink should stay symbolic' ' + physical="$TRASH_DIRECTORY/physical" && + symbolic="$TRASH_DIRECTORY/symbolic" && + test_when_finished "rm -rf \"$physical\"" && + test_when_finished "rm \"$symbolic\"" && + mkdir -p "$physical" && + ln -s "$physical" "$symbolic" && + test_when_finished cleanup_git && + ( + P4CLIENT=client-sym && + p4 client -i <<-EOF && + Client: $P4CLIENT + Description: $P4CLIENT + Root: $symbolic + LineEnd: unix + View: //depot/... //$P4CLIENT/... + EOF + git p4 clone --dest="$git" //depot && + cd "$git" && + test_commit file2 && + git config git-p4.skipSubmitEdit true && + git p4 submit ) ' diff --git a/t/t9809-git-p4-client-view.sh b/t/t9809-git-p4-client-view.sh index b0c6d4391d..77f63492d9 100755 --- a/t/t9809-git-p4-client-view.sh +++ b/t/t9809-git-p4-client-view.sh @@ -1,6 +1,6 @@ #!/bin/sh -test_description='git-p4 client view' +test_description='git p4 client view' . ./lib-git-p4.sh @@ -9,23 +9,6 @@ test_expect_success 'start p4d' ' ' # -# Construct a client with this list of View lines -# -client_view() { - ( - cat <<-EOF && - Client: client - Description: client - Root: $cli - View: - EOF - for arg ; do - printf "\t$arg\n" - done - ) | p4 client -i -} - -# # Verify these files exist, exactly. Caller creates # a list of files in file "files". # @@ -96,19 +79,25 @@ test_expect_success 'init depot' ' test_expect_success 'unsupported view wildcard %%n' ' client_view "//depot/%%%%1/sub/... //client/sub/%%%%1/..." && test_when_finished cleanup_git && - test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot + test_must_fail git p4 clone --use-client-spec --dest="$git" //depot ' test_expect_success 'unsupported view wildcard *' ' client_view "//depot/*/bar/... //client/*/bar/..." && test_when_finished cleanup_git && - test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot + test_must_fail git p4 clone --use-client-spec --dest="$git" //depot ' -test_expect_success 'wildcard ... only supported at end of spec' ' +test_expect_success 'wildcard ... only supported at end of spec 1' ' client_view "//depot/.../file11 //client/.../file11" && test_when_finished cleanup_git && - test_must_fail "$GITP4" clone --use-client-spec --dest="$git" //depot + test_must_fail git p4 clone --use-client-spec --dest="$git" //depot +' + +test_expect_success 'wildcard ... only supported at end of spec 2' ' + client_view "//depot/.../a/... //client/.../a/..." && + test_when_finished cleanup_git && + test_must_fail git p4 clone --use-client-spec --dest="$git" //depot ' test_expect_success 'basic map' ' @@ -116,7 +105,7 @@ test_expect_success 'basic map' ' files="cli1/file11 cli1/file12" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' @@ -124,7 +113,7 @@ test_expect_success 'client view with no mappings' ' client_view && client_verify && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify ' @@ -133,7 +122,7 @@ test_expect_success 'single file map' ' files="file11" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' @@ -144,7 +133,7 @@ test_expect_success 'later mapping takes precedence (entire repo)' ' cli2/dir2/file21 cli2/dir2/file22" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' @@ -154,7 +143,7 @@ test_expect_success 'later mapping takes precedence (partial repo)' ' files="file21 file22" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' @@ -170,7 +159,7 @@ test_expect_success 'depot path matching rejected client path' ' files="cli12/file21 cli12/file22" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' @@ -181,7 +170,7 @@ test_expect_success 'exclusion wildcard, client rhs same (odd)' ' "-//depot/dir2/... //client/..." && client_verify && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify ' @@ -191,7 +180,7 @@ test_expect_success 'exclusion wildcard, client rhs different (normal)' ' files="dir1/file11 dir1/file12" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' @@ -201,17 +190,17 @@ test_expect_success 'exclusion single file' ' files="dir1/file11 dir1/file12 dir2/file21" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' test_expect_success 'overlay wildcard' ' client_view "//depot/dir1/... //client/cli/..." \ - "+//depot/dir2/... //client/cli/...\n" && + "+//depot/dir2/... //client/cli/..." && files="cli/file11 cli/file12 cli/file21 cli/file22" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' @@ -221,7 +210,7 @@ test_expect_success 'overlay single file' ' files="cli/file11 cli/file12 cli/file21" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' @@ -232,7 +221,7 @@ test_expect_success 'exclusion with later inclusion' ' files="dir1/file11 dir1/file12 dir2incl/file21 dir2incl/file22" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify $files ' @@ -240,7 +229,7 @@ test_expect_success 'quotes on rhs only' ' client_view "//depot/dir1/... \"//client/cdir 1/...\"" && client_verify "cdir 1/file11" "cdir 1/file12" && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify "cdir 1/file11" "cdir 1/file12" ' @@ -252,7 +241,7 @@ test_expect_success 'quotes on rhs only' ' test_expect_success 'clone --use-client-spec sets useClientSpec' ' client_view "//depot/... //client/..." && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && ( cd "$git" && git config --bool git-p4.useClientSpec >actual && @@ -267,7 +256,7 @@ test_expect_success 'subdir clone' ' files="dir1/file11 dir1/file12 dir2/file21 dir2/file22" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 && + git p4 clone --use-client-spec --dest="$git" //depot/dir1 && git_verify dir1/file11 dir1/file12 ' @@ -277,14 +266,14 @@ test_expect_success 'subdir clone' ' test_expect_success 'subdir clone, submit modify' ' client_view "//depot/... //client/..." && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 && + git p4 clone --use-client-spec --dest="$git" //depot/dir1 && ( cd "$git" && git config git-p4.skipSubmitEdit true && echo line >>dir1/file12 && git add dir1/file12 && git commit -m dir1/file12 && - "$GITP4" submit + git p4 submit ) && ( cd "$cli" && @@ -296,14 +285,14 @@ test_expect_success 'subdir clone, submit modify' ' test_expect_success 'subdir clone, submit add' ' client_view "//depot/... //client/..." && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 && + git p4 clone --use-client-spec --dest="$git" //depot/dir1 && ( cd "$git" && git config git-p4.skipSubmitEdit true && echo file13 >dir1/file13 && git add dir1/file13 && git commit -m dir1/file13 && - "$GITP4" submit + git p4 submit ) && ( cd "$cli" && @@ -314,13 +303,13 @@ test_expect_success 'subdir clone, submit add' ' test_expect_success 'subdir clone, submit delete' ' client_view "//depot/... //client/..." && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 && + git p4 clone --use-client-spec --dest="$git" //depot/dir1 && ( cd "$git" && git config git-p4.skipSubmitEdit true && git rm dir1/file12 && git commit -m "delete dir1/file12" && - "$GITP4" submit + git p4 submit ) && ( cd "$cli" && @@ -331,7 +320,7 @@ test_expect_success 'subdir clone, submit delete' ' test_expect_success 'subdir clone, submit copy' ' client_view "//depot/... //client/..." && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 && + git p4 clone --use-client-spec --dest="$git" //depot/dir1 && ( cd "$git" && git config git-p4.skipSubmitEdit true && @@ -339,37 +328,77 @@ test_expect_success 'subdir clone, submit copy' ' cp dir1/file11 dir1/file11a && git add dir1/file11a && git commit -m "copy to dir1/file11a" && - "$GITP4" submit + git p4 submit ) && ( cd "$cli" && - test_path_is_file dir1/file11a + test_path_is_file dir1/file11a && + ! is_cli_file_writeable dir1/file11a ) ' test_expect_success 'subdir clone, submit rename' ' client_view "//depot/... //client/..." && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot/dir1 && + git p4 clone --use-client-spec --dest="$git" //depot/dir1 && ( cd "$git" && git config git-p4.skipSubmitEdit true && git config git-p4.detectRenames true && git mv dir1/file13 dir1/file13a && git commit -m "rename dir1/file13 to dir1/file13a" && - "$GITP4" submit + git p4 submit ) && ( cd "$cli" && test_path_is_missing dir1/file13 && - test_path_is_file dir1/file13a + test_path_is_file dir1/file13a && + ! is_cli_file_writeable dir1/file13a + ) +' + +# see t9800 for the non-client-spec case, and the rest of the wildcard tests +test_expect_success 'wildcard files submit back to p4, client-spec case' ' + client_view "//depot/... //client/..." && + test_when_finished cleanup_git && + git p4 clone --use-client-spec --dest="$git" //depot/dir1 && + ( + cd "$git" && + echo git-wild-hash >dir1/git-wild#hash && + if test_have_prereq NOT_MINGW NOT_CYGWIN + then + echo git-wild-star >dir1/git-wild\*star + fi && + echo git-wild-at >dir1/git-wild@at && + echo git-wild-percent >dir1/git-wild%percent && + git add dir1/git-wild* && + git commit -m "add some wildcard filenames" && + git config git-p4.skipSubmitEditCheck true && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file dir1/git-wild#hash && + if test_have_prereq NOT_MINGW NOT_CYGWIN + then + test_path_is_file dir1/git-wild\*star + fi && + test_path_is_file dir1/git-wild@at && + test_path_is_file dir1/git-wild%percent + ) && + ( + # delete these carefully, cannot just do "p4 delete" + # on files with wildcards; but git-p4 knows how + cd "$git" && + git rm dir1/git-wild* && + git commit -m "clean up the wildcards" && + git p4 submit ) ' test_expect_success 'reinit depot' ' ( cd "$cli" && - p4 sync -f && rm files && p4 delete */* && p4 submit -d "delete all files" && @@ -378,6 +407,393 @@ test_expect_success 'reinit depot' ' ' # +# What happens when two files of the same name are overlayed together? +# The last-listed file should take preference. +# +# //depot +# - dir1 +# - file11 +# - file12 +# - filecollide +# - dir2 +# - file21 +# - file22 +# - filecollide +# +test_expect_success 'overlay collision setup' ' + client_view "//depot/... //client/..." && + ( + cd "$cli" && + p4 sync && + echo dir1/filecollide >dir1/filecollide && + p4 add dir1/filecollide && + p4 submit -d dir1/filecollide && + echo dir2/filecollide >dir2/filecollide && + p4 add dir2/filecollide && + p4 submit -d dir2/filecollide + ) +' + +test_expect_success 'overlay collision 1 to 2' ' + client_view "//depot/dir1/... //client/..." \ + "+//depot/dir2/... //client/..." && + files="file11 file12 file21 file22 filecollide" && + echo dir2/filecollide >actual && + client_verify $files && + test_cmp actual "$cli"/filecollide && + test_when_finished cleanup_git && + git p4 clone --use-client-spec --dest="$git" //depot && + git_verify $files && + test_cmp actual "$git"/filecollide +' + +test_expect_failure 'overlay collision 2 to 1' ' + client_view "//depot/dir2/... //client/..." \ + "+//depot/dir1/... //client/..." && + files="file11 file12 file21 file22 filecollide" && + echo dir1/filecollide >actual && + client_verify $files && + test_cmp actual "$cli"/filecollide && + test_when_finished cleanup_git && + git p4 clone --use-client-spec --dest="$git" //depot && + git_verify $files && + test_cmp actual "$git"/filecollide +' + +test_expect_success 'overlay collision delete 2' ' + client_view "//depot/... //client/..." && + ( + cd "$cli" && + p4 sync && + p4 delete dir2/filecollide && + p4 submit -d "remove dir2/filecollide" + ) +' + +# no filecollide, got deleted with dir2 +test_expect_failure 'overlay collision 1 to 2, but 2 deleted' ' + client_view "//depot/dir1/... //client/..." \ + "+//depot/dir2/... //client/..." && + files="file11 file12 file21 file22" && + client_verify $files && + test_when_finished cleanup_git && + git p4 clone --use-client-spec --dest="$git" //depot && + git_verify $files +' + +test_expect_success 'overlay collision update 1' ' + client_view "//depot/dir1/... //client/dir1/..." && + ( + cd "$cli" && + p4 sync && + p4 open dir1/filecollide && + echo dir1/filecollide update >dir1/filecollide && + p4 submit -d "update dir1/filecollide" + ) +' + +# still no filecollide, dir2 still wins with the deletion even though the +# change to dir1 is more recent +test_expect_failure 'overlay collision 1 to 2, but 2 deleted, then 1 updated' ' + client_view "//depot/dir1/... //client/..." \ + "+//depot/dir2/... //client/..." && + files="file11 file12 file21 file22" && + client_verify $files && + test_when_finished cleanup_git && + git p4 clone --use-client-spec --dest="$git" //depot && + git_verify $files +' + +test_expect_success 'overlay collision delete filecollides' ' + client_view "//depot/... //client/..." && + ( + cd "$cli" && + p4 sync && + p4 delete dir1/filecollide dir2/filecollide && + p4 submit -d "remove filecollides" + ) +' + +# +# Overlays as part of sync, rather than initial checkout: +# 1. add a file in dir1 +# 2. sync to include it +# 3. add same file in dir2 +# 4. sync, make sure content switches as dir2 has priority +# 5. add another file in dir1 +# 6. sync +# 7. add/delete same file in dir2 +# 8. sync, make sure it disappears, again dir2 wins +# 9. cleanup +# +# //depot +# - dir1 +# - file11 +# - file12 +# - colA +# - colB +# - dir2 +# - file21 +# - file22 +# - colA +# - colB +# +test_expect_success 'overlay sync: add colA in dir1' ' + client_view "//depot/dir1/... //client/dir1/..." && + ( + cd "$cli" && + p4 sync && + echo dir1/colA >dir1/colA && + p4 add dir1/colA && + p4 submit -d dir1/colA + ) +' + +test_expect_success 'overlay sync: initial git checkout' ' + client_view "//depot/dir1/... //client/..." \ + "+//depot/dir2/... //client/..." && + files="file11 file12 file21 file22 colA" && + echo dir1/colA >actual && + client_verify $files && + test_cmp actual "$cli"/colA && + git p4 clone --use-client-spec --dest="$git" //depot && + git_verify $files && + test_cmp actual "$git"/colA +' + +test_expect_success 'overlay sync: add colA in dir2' ' + client_view "//depot/dir2/... //client/dir2/..." && + ( + cd "$cli" && + p4 sync && + echo dir2/colA >dir2/colA && + p4 add dir2/colA && + p4 submit -d dir2/colA + ) +' + +test_expect_success 'overlay sync: colA content switch' ' + client_view "//depot/dir1/... //client/..." \ + "+//depot/dir2/... //client/..." && + files="file11 file12 file21 file22 colA" && + echo dir2/colA >actual && + client_verify $files && + test_cmp actual "$cli"/colA && + ( + cd "$git" && + git p4 sync --use-client-spec && + git merge --ff-only p4/master + ) && + git_verify $files && + test_cmp actual "$git"/colA +' + +test_expect_success 'overlay sync: add colB in dir1' ' + client_view "//depot/dir1/... //client/dir1/..." && + ( + cd "$cli" && + p4 sync && + echo dir1/colB >dir1/colB && + p4 add dir1/colB && + p4 submit -d dir1/colB + ) +' + +test_expect_success 'overlay sync: colB appears' ' + client_view "//depot/dir1/... //client/..." \ + "+//depot/dir2/... //client/..." && + files="file11 file12 file21 file22 colA colB" && + echo dir1/colB >actual && + client_verify $files && + test_cmp actual "$cli"/colB && + ( + cd "$git" && + git p4 sync --use-client-spec && + git merge --ff-only p4/master + ) && + git_verify $files && + test_cmp actual "$git"/colB +' + +test_expect_success 'overlay sync: add/delete colB in dir2' ' + client_view "//depot/dir2/... //client/dir2/..." && + ( + cd "$cli" && + p4 sync && + echo dir2/colB >dir2/colB && + p4 add dir2/colB && + p4 submit -d dir2/colB && + p4 delete dir2/colB && + p4 submit -d "delete dir2/colB" + ) +' + +test_expect_success 'overlay sync: colB disappears' ' + client_view "//depot/dir1/... //client/..." \ + "+//depot/dir2/... //client/..." && + files="file11 file12 file21 file22 colA" && + client_verify $files && + test_when_finished cleanup_git && + ( + cd "$git" && + git p4 sync --use-client-spec && + git merge --ff-only p4/master + ) && + git_verify $files +' + +test_expect_success 'overlay sync: cleanup' ' + client_view "//depot/... //client/..." && + ( + cd "$cli" && + p4 sync && + p4 delete dir1/colA dir2/colA dir1/colB && + p4 submit -d "remove overlay sync files" + ) +' + +# +# Overlay tests again, but swapped so dir1 has priority. +# 1. add a file in dir1 +# 2. sync to include it +# 3. add same file in dir2 +# 4. sync, make sure content does not switch +# 5. add another file in dir1 +# 6. sync +# 7. add/delete same file in dir2 +# 8. sync, make sure it is still there +# 9. cleanup +# +# //depot +# - dir1 +# - file11 +# - file12 +# - colA +# - colB +# - dir2 +# - file21 +# - file22 +# - colA +# - colB +# +test_expect_success 'overlay sync swap: add colA in dir1' ' + client_view "//depot/dir1/... //client/dir1/..." && + ( + cd "$cli" && + p4 sync && + echo dir1/colA >dir1/colA && + p4 add dir1/colA && + p4 submit -d dir1/colA + ) +' + +test_expect_success 'overlay sync swap: initial git checkout' ' + client_view "//depot/dir2/... //client/..." \ + "+//depot/dir1/... //client/..." && + files="file11 file12 file21 file22 colA" && + echo dir1/colA >actual && + client_verify $files && + test_cmp actual "$cli"/colA && + git p4 clone --use-client-spec --dest="$git" //depot && + git_verify $files && + test_cmp actual "$git"/colA +' + +test_expect_success 'overlay sync swap: add colA in dir2' ' + client_view "//depot/dir2/... //client/dir2/..." && + ( + cd "$cli" && + p4 sync && + echo dir2/colA >dir2/colA && + p4 add dir2/colA && + p4 submit -d dir2/colA + ) +' + +test_expect_failure 'overlay sync swap: colA no content switch' ' + client_view "//depot/dir2/... //client/..." \ + "+//depot/dir1/... //client/..." && + files="file11 file12 file21 file22 colA" && + echo dir1/colA >actual && + client_verify $files && + test_cmp actual "$cli"/colA && + ( + cd "$git" && + git p4 sync --use-client-spec && + git merge --ff-only p4/master + ) && + git_verify $files && + test_cmp actual "$git"/colA +' + +test_expect_success 'overlay sync swap: add colB in dir1' ' + client_view "//depot/dir1/... //client/dir1/..." && + ( + cd "$cli" && + p4 sync && + echo dir1/colB >dir1/colB && + p4 add dir1/colB && + p4 submit -d dir1/colB + ) +' + +test_expect_success 'overlay sync swap: colB appears' ' + client_view "//depot/dir2/... //client/..." \ + "+//depot/dir1/... //client/..." && + files="file11 file12 file21 file22 colA colB" && + echo dir1/colB >actual && + client_verify $files && + test_cmp actual "$cli"/colB && + ( + cd "$git" && + git p4 sync --use-client-spec && + git merge --ff-only p4/master + ) && + git_verify $files && + test_cmp actual "$git"/colB +' + +test_expect_success 'overlay sync swap: add/delete colB in dir2' ' + client_view "//depot/dir2/... //client/dir2/..." && + ( + cd "$cli" && + p4 sync && + echo dir2/colB >dir2/colB && + p4 add dir2/colB && + p4 submit -d dir2/colB && + p4 delete dir2/colB && + p4 submit -d "delete dir2/colB" + ) +' + +test_expect_failure 'overlay sync swap: colB no change' ' + client_view "//depot/dir2/... //client/..." \ + "+//depot/dir1/... //client/..." && + files="file11 file12 file21 file22 colA colB" && + echo dir1/colB >actual && + client_verify $files && + test_cmp actual "$cli"/colB && + test_when_finished cleanup_git && + ( + cd "$git" && + git p4 sync --use-client-spec && + git merge --ff-only p4/master + ) && + git_verify $files && + test_cmp actual "$cli"/colB +' + +test_expect_success 'overlay sync swap: cleanup' ' + client_view "//depot/... //client/..." && + ( + cd "$cli" && + p4 sync && + p4 delete dir1/colA dir2/colA dir1/colB && + p4 submit -d "remove overlay sync files" + ) +' + +# # Rename directories to test quoting in depot-side mappings # //depot # - "dir 1" @@ -408,7 +824,7 @@ test_expect_success 'quotes on lhs only' ' files="cdir1/file11 cdir1/file12" && client_verify $files && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && client_verify $files ' @@ -416,7 +832,7 @@ test_expect_success 'quotes on both sides' ' client_view "\"//depot/dir 1/...\" \"//client/cdir 1/...\"" && client_verify "cdir 1/file11" "cdir 1/file12" && test_when_finished cleanup_git && - "$GITP4" clone --use-client-spec --dest="$git" //depot && + git p4 clone --use-client-spec --dest="$git" //depot && git_verify "cdir 1/file11" "cdir 1/file12" ' diff --git a/t/t9810-git-p4-rcs.sh b/t/t9810-git-p4-rcs.sh new file mode 100755 index 0000000000..8134ab439b --- /dev/null +++ b/t/t9810-git-p4-rcs.sh @@ -0,0 +1,367 @@ +#!/bin/sh + +test_description='git p4 rcs keywords' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +# +# Make one file with keyword lines at the top, and +# enough plain text to be able to test modifications +# far away from the keywords. +# +test_expect_success 'init depot' ' + ( + cd "$cli" && + cat <<-\EOF >filek && + $Id$ + /* $Revision$ */ + # $Change$ + line4 + line5 + line6 + line7 + line8 + EOF + sed "s/Revision/Revision: do not scrub me/" <filek >fileko && + sed "s/Id/Id: do not scrub me/" <fileko >file_text && + p4 add -t text+k filek && + p4 submit -d "filek" && + p4 add -t text+ko fileko && + p4 submit -d "fileko" && + p4 add -t text file_text && + p4 submit -d "file_text" + ) +' + +# +# Generate these in a function to make it easy to use single quote marks. +# +write_scrub_scripts () { + cat >"$TRASH_DIRECTORY/scrub_k.py" <<-\EOF && + import re, sys + sys.stdout.write(re.sub(r'(?i)\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$]*\$', r'$\1$', sys.stdin.read())) + EOF + cat >"$TRASH_DIRECTORY/scrub_ko.py" <<-\EOF + import re, sys + sys.stdout.write(re.sub(r'(?i)\$(Id|Header):[^$]*\$', r'$\1$', sys.stdin.read())) + EOF +} + +test_expect_success 'scrub scripts' ' + write_scrub_scripts +' + +# +# Compare $cli/file to its scrubbed version, should be different. +# Compare scrubbed $cli/file to $git/file, should be same. +# +scrub_k_check () { + file="$1" && + scrub="$TRASH_DIRECTORY/$file" && + "$PYTHON_PATH" "$TRASH_DIRECTORY/scrub_k.py" <"$git/$file" >"$scrub" && + ! test_cmp "$cli/$file" "$scrub" && + test_cmp "$git/$file" "$scrub" && + rm "$scrub" +} +scrub_ko_check () { + file="$1" && + scrub="$TRASH_DIRECTORY/$file" && + "$PYTHON_PATH" "$TRASH_DIRECTORY/scrub_ko.py" <"$git/$file" >"$scrub" && + ! test_cmp "$cli/$file" "$scrub" && + test_cmp "$git/$file" "$scrub" && + rm "$scrub" +} + +# +# Modify far away from keywords. If no RCS lines show up +# in the diff, there is no conflict. +# +test_expect_success 'edit far away from RCS lines' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + sed "s/^line7/line7 edit/" <filek >filek.tmp && + mv -f filek.tmp filek && + git commit -m "filek line7 edit" filek && + git p4 submit && + scrub_k_check filek + ) +' + +# +# Modify near the keywords. This will require RCS scrubbing. +# +test_expect_success 'edit near RCS lines' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.attemptRCSCleanup true && + sed "s/^line4/line4 edit/" <filek >filek.tmp && + mv -f filek.tmp filek && + git commit -m "filek line4 edit" filek && + git p4 submit && + scrub_k_check filek + ) +' + +# +# Modify the keywords themselves. This also will require RCS scrubbing. +# +test_expect_success 'edit keyword lines' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.attemptRCSCleanup true && + sed "/Revision/d" <filek >filek.tmp && + mv -f filek.tmp filek && + git commit -m "filek remove Revision line" filek && + git p4 submit && + scrub_k_check filek + ) +' + +# +# Scrubbing text+ko files should not alter all keywords, just Id, Header. +# +test_expect_success 'scrub ko files differently' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.attemptRCSCleanup true && + sed "s/^line4/line4 edit/" <fileko >fileko.tmp && + mv -f fileko.tmp fileko && + git commit -m "fileko line4 edit" fileko && + git p4 submit && + scrub_ko_check fileko && + ! scrub_k_check fileko + ) +' + +# hack; git p4 submit should do it on its own +test_expect_success 'cleanup after failure' ' + ( + cd "$cli" && + p4 revert ... + ) +' + +# perl $File:: bug check +test_expect_success 'ktext expansion should not expand multi-line $File::' ' + ( + cd "$cli" && + cat >lv.pm <<-\EOF + my $wanted = sub { my $f = $File::Find::name; + if ( -f && $f =~ /foo/ ) { + EOF + p4 add -t ktext lv.pm && + p4 submit -d "lv.pm" + ) && + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + test_cmp "$cli/lv.pm" lv.pm + ) +' + +# +# Do not scrub anything but +k or +ko files. Sneak a change into +# the cli file so that submit will get a conflict. Make sure that +# scrubbing doesn't make a mess of things. +# +# This might happen only if the git repo is behind the p4 repo at +# submit time, and there is a conflict. +# +test_expect_success 'do not scrub plain text' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.attemptRCSCleanup true && + sed "s/^line4/line4 edit/" <file_text >file_text.tmp && + mv -f file_text.tmp file_text && + git commit -m "file_text line4 edit" file_text && + ( + cd "$cli" && + p4 open file_text && + sed "s/^line5/line5 p4 edit/" <file_text >file_text.tmp && + mv -f file_text.tmp file_text && + p4 submit -d "file5 p4 edit" + ) && + echo s | test_expect_code 1 git p4 submit && + ( + # make sure the file is not left open + cd "$cli" && + ! p4 fstat -T action file_text + ) + ) +' + +# hack; git p4 submit should do it on its own +test_expect_success 'cleanup after failure 2' ' + ( + cd "$cli" && + p4 revert ... + ) +' + +create_kw_file () { + cat <<\EOF >"$1" +/* A file + Id: $Id$ + Revision: $Revision$ + File: $File$ + */ +int main(int argc, const char **argv) { + return 0; +} +EOF +} + +test_expect_success 'add kwfile' ' + ( + cd "$cli" && + echo file1 >file1 && + p4 add file1 && + p4 submit -d "file 1" && + create_kw_file kwfile1.c && + p4 add kwfile1.c && + p4 submit -d "Add rcw kw file" kwfile1.c + ) +' + +p4_append_to_file () { + f="$1" && + p4 edit -t ktext "$f" && + echo "/* $(date) */" >>"$f" && + p4 submit -d "appending a line in p4" +} + +# Create some files with RCS keywords. If they get modified +# elsewhere then the version number gets bumped which then +# results in a merge conflict if we touch the RCS kw lines, +# even though the change itself would otherwise apply cleanly. +test_expect_success 'cope with rcs keyword expansion damage' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.attemptRCSCleanup true && + (cd "$cli" && p4_append_to_file kwfile1.c) && + old_lines=$(wc -l <kwfile1.c) && + perl -n -i -e "print unless m/Revision:/" kwfile1.c && + new_lines=$(wc -l <kwfile1.c) && + test $new_lines = $(($old_lines - 1)) && + + git add kwfile1.c && + git commit -m "Zap an RCS kw line" && + git p4 submit && + git p4 rebase && + git diff p4/master && + git p4 commit && + echo "try modifying in both" && + cd "$cli" && + p4 edit kwfile1.c && + echo "line from p4" >>kwfile1.c && + p4 submit -d "add a line in p4" kwfile1.c && + cd "$git" && + echo "line from git at the top" | cat - kwfile1.c >kwfile1.c.new && + mv kwfile1.c.new kwfile1.c && + git commit -m "Add line in git at the top" kwfile1.c && + git p4 rebase && + git p4 submit + ) +' + +test_expect_success 'cope with rcs keyword file deletion' ' + test_when_finished cleanup_git && + ( + cd "$cli" && + echo "\$Revision\$" >kwdelfile.c && + p4 add -t ktext kwdelfile.c && + p4 submit -d "Add file to be deleted" && + cat kwdelfile.c && + grep 1 kwdelfile.c + ) && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + grep Revision kwdelfile.c && + git rm -f kwdelfile.c && + git commit -m "Delete a file containing RCS keywords" && + git config git-p4.skipSubmitEdit true && + git config git-p4.attemptRCSCleanup true && + git p4 submit + ) && + ( + cd "$cli" && + p4 sync && + ! test -f kwdelfile.c + ) +' + +# If you add keywords in git of the form $Header$ then everything should +# work fine without any special handling. +test_expect_success 'Add keywords in git which match the default p4 values' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo "NewKW: \$Revision\$" >>kwfile1.c && + git add kwfile1.c && + git commit -m "Adding RCS keywords in git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.attemptRCSCleanup true && + git p4 submit + ) && + ( + cd "$cli" && + p4 sync && + test -f kwfile1.c && + grep "NewKW.*Revision.*[0-9]" kwfile1.c + + ) +' + +# If you add keywords in git of the form $Header:#1$ then things will fail +# unless git-p4 takes steps to scrub the *git* commit. +# +test_expect_failure 'Add keywords in git which do not match the default p4 values' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo "NewKW2: \$Revision:1\$" >>kwfile1.c && + git add kwfile1.c && + git commit -m "Adding RCS keywords in git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.attemptRCSCleanup true && + git p4 submit + ) && + ( + cd "$cli" && + p4 sync && + grep "NewKW2.*Revision.*[0-9]" kwfile1.c + + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9811-git-p4-label-import.sh b/t/t9811-git-p4-label-import.sh new file mode 100755 index 0000000000..095238fffe --- /dev/null +++ b/t/t9811-git-p4-label-import.sh @@ -0,0 +1,222 @@ +#!/bin/sh + +test_description='git p4 label tests' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +# Basic p4 label import tests. +# +test_expect_success 'basic p4 labels' ' + test_when_finished cleanup_git && + ( + cd "$cli" && + mkdir -p main && + + echo f1 >main/f1 && + p4 add main/f1 && + p4 submit -d "main/f1" && + + echo f2 >main/f2 && + p4 add main/f2 && + p4 submit -d "main/f2" && + + echo f3 >main/file_with_\$metachar && + p4 add main/file_with_\$metachar && + p4 submit -d "file with metachar" && + + p4 tag -l TAG_F1_ONLY main/f1 && + p4 tag -l TAG_WITH\$_SHELL_CHAR main/... && + p4 tag -l this_tag_will_be\ skipped main/... && + + echo f4 >main/f4 && + p4 add main/f4 && + p4 submit -d "main/f4" && + + p4 label -i <<-EOF && + Label: TAG_LONG_LABEL + Description: + A Label first line + A Label second line + View: //depot/... + EOF + + p4 tag -l TAG_LONG_LABEL ... && + + p4 labels ... && + + git p4 clone --dest="$git" //depot@all && + cd "$git" && + git config git-p4.labelImportRegexp ".*TAG.*" && + git p4 sync --import-labels --verbose && + + git tag && + git tag >taglist && + test_line_count = 3 taglist && + + cd main && + git checkout TAG_F1_ONLY && + ! test -f f2 && + git checkout TAG_WITH\$_SHELL_CHAR && + test -f f1 && test -f f2 && test -f file_with_\$metachar && + + git show TAG_LONG_LABEL | grep -q "A Label second line" + ) +' +# Test some label corner cases: +# +# - two tags on the same file; both should be available +# - a tag that is only on one file; this kind of tag +# cannot be imported (at least not easily). + +test_expect_success 'two labels on the same changelist' ' + test_when_finished cleanup_git && + ( + cd "$cli" && + mkdir -p main && + + p4 edit main/f1 main/f2 && + echo "hello world" >main/f1 && + echo "not in the tag" >main/f2 && + p4 submit -d "main/f[12]: testing two labels" && + + p4 tag -l TAG_F1_1 main/... && + p4 tag -l TAG_F1_2 main/... && + + p4 labels ... && + + git p4 clone --dest="$git" //depot@all && + cd "$git" && + git p4 sync --import-labels && + + git tag | grep TAG_F1 && + git tag | grep -q TAG_F1_1 && + git tag | grep -q TAG_F1_2 && + + cd main && + + git checkout TAG_F1_1 && + ls && + test -f f1 && + + git checkout TAG_F1_2 && + ls && + test -f f1 + ) +' + +# Export some git tags to p4 +test_expect_success 'export git tags to p4' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot@all && + ( + cd "$git" && + git tag -m "A tag created in git:xyzzy" GIT_TAG_1 && + echo "hello world" >main/f10 && + git add main/f10 && + git commit -m "Adding file for export test" && + git config git-p4.skipSubmitEdit true && + git p4 submit && + git tag -m "Another git tag" GIT_TAG_2 && + git tag LIGHTWEIGHT_TAG && + git p4 rebase --import-labels --verbose && + git p4 submit --export-labels --verbose + ) && + ( + cd "$cli" && + p4 sync ... && + p4 labels ... | grep GIT_TAG_1 && + p4 labels ... | grep GIT_TAG_2 && + p4 labels ... | grep LIGHTWEIGHT_TAG && + p4 label -o GIT_TAG_1 | grep "tag created in git:xyzzy" && + p4 sync ...@GIT_TAG_1 && + ! test -f main/f10 + p4 sync ...@GIT_TAG_2 && + test -f main/f10 + ) +' + +# Export a tag from git where an affected file is deleted later on +# Need to create git tags after rebase, since only then can the +# git commits be mapped to p4 changelists. +test_expect_success 'export git tags to p4 with deletion' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot@all && + ( + cd "$git" && + git p4 sync --import-labels && + echo "deleted file" >main/deleted_file && + git add main/deleted_file && + git commit -m "create deleted file" && + git rm main/deleted_file && + echo "new file" >main/f11 && + git add main/f11 && + git commit -m "delete the deleted file" && + git config git-p4.skipSubmitEdit true && + git p4 submit && + git p4 rebase --import-labels --verbose && + git tag -m "tag on deleted file" GIT_TAG_ON_DELETED HEAD~1 && + git tag -m "tag after deletion" GIT_TAG_AFTER_DELETION HEAD && + git p4 submit --export-labels --verbose + ) && + ( + cd "$cli" && + p4 sync ... && + p4 sync ...@GIT_TAG_ON_DELETED && + test -f main/deleted_file && + p4 sync ...@GIT_TAG_AFTER_DELETION && + ! test -f main/deleted_file && + echo "checking label contents" && + p4 label -o GIT_TAG_ON_DELETED | grep "tag on deleted file" + ) +' + +# Create a tag in git that cannot be exported to p4 +test_expect_success 'tag that cannot be exported' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot@all && + ( + cd "$git" && + git checkout -b a_branch && + echo "hello" >main/f12 && + git add main/f12 && + git commit -m "adding f12" && + git tag -m "tag on a_branch" GIT_TAG_ON_A_BRANCH && + git checkout master && + git p4 submit --export-labels + ) && + ( + cd "$cli" && + p4 sync ... && + !(p4 labels | grep GIT_TAG_ON_A_BRANCH) + ) +' + +test_expect_success 'use git config to enable import/export of tags' ' + git p4 clone --verbose --dest="$git" //depot@all && + ( + cd "$git" && + git config git-p4.exportLabels true && + git config git-p4.importLabels true && + git tag CFG_A_GIT_TAG && + git p4 rebase --verbose && + git p4 submit --verbose && + git tag && + git tag | grep TAG_F1_1 + ) && + ( + cd "$cli" && + p4 labels && + p4 labels | grep CFG_A_GIT_TAG + ) +' + + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9812-git-p4-wildcards.sh b/t/t9812-git-p4-wildcards.sh new file mode 100755 index 0000000000..67633257f3 --- /dev/null +++ b/t/t9812-git-p4-wildcards.sh @@ -0,0 +1,168 @@ +#!/bin/sh + +test_description='git p4 wildcards' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +test_expect_success 'add p4 files with wildcards in the names' ' + ( + cd "$cli" && + printf "file2\nhas\nsome\nrandom\ntext\n" >file2 && + p4 add file2 && + echo file-wild-hash >file-wild#hash && + if test_have_prereq NOT_MINGW NOT_CYGWIN + then + echo file-wild-star >file-wild\*star + fi && + echo file-wild-at >file-wild@at && + echo file-wild-percent >file-wild%percent && + p4 add -f file-wild* && + p4 submit -d "file wildcards" + ) +' + +test_expect_success 'wildcard files git p4 clone' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + test -f file-wild#hash && + if test_have_prereq NOT_MINGW NOT_CYGWIN + then + test -f file-wild\*star + fi && + test -f file-wild@at && + test -f file-wild%percent + ) +' + +test_expect_success 'wildcard files submit back to p4, add' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo git-wild-hash >git-wild#hash && + if test_have_prereq NOT_MINGW NOT_CYGWIN + then + echo git-wild-star >git-wild\*star + fi && + echo git-wild-at >git-wild@at && + echo git-wild-percent >git-wild%percent && + git add git-wild* && + git commit -m "add some wildcard filenames" && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file git-wild#hash && + if test_have_prereq NOT_MINGW NOT_CYGWIN + then + test_path_is_file git-wild\*star + fi && + test_path_is_file git-wild@at && + test_path_is_file git-wild%percent + ) +' + +test_expect_success 'wildcard files submit back to p4, modify' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo new-line >>git-wild#hash && + if test_have_prereq NOT_MINGW NOT_CYGWIN + then + echo new-line >>git-wild\*star + fi && + echo new-line >>git-wild@at && + echo new-line >>git-wild%percent && + git add git-wild* && + git commit -m "modify the wildcard files" && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_line_count = 2 git-wild#hash && + if test_have_prereq NOT_MINGW NOT_CYGWIN + then + test_line_count = 2 git-wild\*star + fi && + test_line_count = 2 git-wild@at && + test_line_count = 2 git-wild%percent + ) +' + +test_expect_success 'wildcard files submit back to p4, copy' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + cp file2 git-wild-cp#hash && + git add git-wild-cp#hash && + cp git-wild#hash file-wild-3 && + git add file-wild-3 && + git commit -m "wildcard copies" && + git config git-p4.detectCopies true && + git config git-p4.detectCopiesHarder true && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file git-wild-cp#hash && + test_path_is_file file-wild-3 + ) +' + +test_expect_success 'wildcard files submit back to p4, rename' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git mv git-wild@at file-wild-4 && + git mv file-wild-3 git-wild-cp%percent && + git commit -m "wildcard renames" && + git config git-p4.detectRenames true && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing git-wild@at && + test_path_is_file git-wild-cp%percent + ) +' + +test_expect_success 'wildcard files submit back to p4, delete' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git rm git-wild* && + git commit -m "delete the wildcard files" && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing git-wild#hash && + if test_have_prereq NOT_MINGW NOT_CYGWIN + then + test_path_is_missing git-wild\*star + fi && + test_path_is_missing git-wild@at && + test_path_is_missing git-wild%percent + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh new file mode 100755 index 0000000000..f2e85e518b --- /dev/null +++ b/t/t9813-git-p4-preserve-users.sh @@ -0,0 +1,153 @@ +#!/bin/sh + +test_description='git p4 preserve users' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +test_expect_success 'create files' ' + ( + cd "$cli" && + p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i && + echo file1 >file1 && + echo file2 >file2 && + p4 add file1 file2 && + p4 submit -d "add files" + ) +' + +p4_add_user() { + name=$1 fullname=$2 && + p4 user -f -i <<-EOF && + User: $name + Email: $name@localhost + FullName: $fullname + EOF + p4 passwd -P secret $name +} + +p4_grant_admin() { + name=$1 && + { + p4 protect -o && + echo " admin user $name * //depot/..." + } | p4 protect -i +} + +p4_check_commit_author() { + file=$1 user=$2 && + p4 changes -m 1 //depot/$file | grep -q $user +} + +make_change_by_user() { + file=$1 name=$2 email=$3 && + echo "username: a change by $name" >>"$file" && + git add "$file" && + git commit --author "$name <$email>" -m "a change by $name" +} + +# Test username support, submitting as user 'alice' +test_expect_success 'preserve users' ' + p4_add_user alice Alice && + p4_add_user bob Bob && + p4_grant_admin alice && + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + echo "username: a change by alice" >>file1 && + echo "username: a change by bob" >>file2 && + git commit --author "Alice <alice@localhost>" -m "a change by alice" file1 && + git commit --author "Bob <bob@localhost>" -m "a change by bob" file2 && + git config git-p4.skipSubmitEditCheck true && + P4EDITOR=touch P4USER=alice P4PASSWD=secret git p4 commit --preserve-user && + p4_check_commit_author file1 alice && + p4_check_commit_author file2 bob + ) +' + +# Test username support, submitting as bob, who lacks admin rights. Should +# not submit change to p4 (git diff should show deltas). +test_expect_success 'refuse to preserve users without perms' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEditCheck true && + echo "username-noperms: a change by alice" >>file1 && + git commit --author "Alice <alice@localhost>" -m "perms: a change by alice" file1 && + P4EDITOR=touch P4USER=bob P4PASSWD=secret && + export P4EDITOR P4USER P4PASSWD && + test_must_fail git p4 commit --preserve-user && + ! git diff --exit-code HEAD..p4/master + ) +' + +# What happens with unknown author? Without allowMissingP4Users it should fail. +test_expect_success 'preserve user where author is unknown to p4' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEditCheck true && + echo "username-bob: a change by bob" >>file1 && + git commit --author "Bob <bob@localhost>" -m "preserve: a change by bob" file1 && + echo "username-unknown: a change by charlie" >>file1 && + git commit --author "Charlie <charlie@localhost>" -m "preserve: a change by charlie" file1 && + P4EDITOR=touch P4USER=alice P4PASSWD=secret && + export P4EDITOR P4USER P4PASSWD && + test_must_fail git p4 commit --preserve-user && + ! git diff --exit-code HEAD..p4/master && + + echo "$0: repeat with allowMissingP4Users enabled" && + git config git-p4.allowMissingP4Users true && + git config git-p4.preserveUser true && + git p4 commit && + git diff --exit-code HEAD..p4/master && + p4_check_commit_author file1 alice + ) +' + +# If we're *not* using --preserve-user, git-p4 should warn if we're submitting +# changes that are not all ours. +# Test: user in p4 and user unknown to p4. +# Test: warning disabled and user is the same. +test_expect_success 'not preserving user with mixed authorship' ' + git p4 clone --dest="$git" //depot && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEditCheck true && + p4_add_user derek Derek && + + make_change_by_user usernamefile3 Derek derek@localhost && + P4EDITOR=cat P4USER=alice P4PASSWD=secret && + export P4EDITOR P4USER P4PASSWD && + git p4 commit |\ + grep "git author derek@localhost does not match" && + + make_change_by_user usernamefile3 Charlie charlie@localhost && + git p4 commit |\ + grep "git author charlie@localhost does not match" && + + make_change_by_user usernamefile3 alice alice@localhost && + git p4 commit |\ + test_must_fail grep "git author.*does not match" && + + git config git-p4.skipUserNameCheck true && + make_change_by_user usernamefile3 Charlie charlie@localhost && + git p4 commit |\ + test_must_fail grep "git author.*does not match" && + + p4_check_commit_author usernamefile3 alice + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh new file mode 100755 index 0000000000..be802e0e16 --- /dev/null +++ b/t/t9814-git-p4-rename.sh @@ -0,0 +1,241 @@ +#!/bin/sh + +test_description='git p4 rename' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +# We rely on this behavior to detect for p4 move availability. +test_expect_success 'p4 help unknown returns 1' ' + ( + cd "$cli" && + ( + p4 help client >errs 2>&1 + echo $? >retval + ) + echo 0 >expected && + test_cmp expected retval && + rm retval && + ( + p4 help nosuchcommand >errs 2>&1 + echo $? >retval + ) + echo 1 >expected && + test_cmp expected retval && + rm retval + ) +' + +test_expect_success 'create files' ' + ( + cd "$cli" && + p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i && + cat >file1 <<-EOF && + A large block of text + in file1 that will generate + enough context so that rename + and copy detection will find + something interesting to do. + EOF + cat >file2 <<-EOF && + /* + * This blob looks a bit + * different. + */ + int main(int argc, char **argv) + { + char text[200]; + + strcpy(text, "copy/rename this"); + printf("text is %s\n", text); + return 0; + } + EOF + p4 add file1 file2 && + p4 submit -d "add files" + ) +' + +# Rename a file and confirm that rename is not detected in P4. +# Rename the new file again with detectRenames option enabled and confirm that +# this is detected in P4. +# Rename the new file again adding an extra line, configure a big threshold in +# detectRenames and confirm that rename is not detected in P4. +# Repeat, this time with a smaller threshold and confirm that the rename is +# detected in P4. +test_expect_success 'detect renames' ' + git p4 clone --dest="$git" //depot@all && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + + git mv file1 file4 && + git commit -a -m "Rename file1 to file4" && + git diff-tree -r -M HEAD && + git p4 submit && + p4 filelog //depot/file4 >filelog && + ! grep " from //depot" filelog && + + git mv file4 file5 && + git commit -a -m "Rename file4 to file5" && + git diff-tree -r -M HEAD && + git config git-p4.detectRenames true && + git p4 submit && + p4 filelog //depot/file5 >filelog && + grep " from //depot/file4" filelog && + + git mv file5 file6 && + echo update >>file6 && + git add file6 && + git commit -a -m "Rename file5 to file6 with changes" && + git diff-tree -r -M HEAD && + level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") && + test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 && + git config git-p4.detectRenames $(($level + 2)) && + git p4 submit && + p4 filelog //depot/file6 >filelog && + ! grep " from //depot" filelog && + + git mv file6 file7 && + echo update >>file7 && + git add file7 && + git commit -a -m "Rename file6 to file7 with changes" && + git diff-tree -r -M HEAD && + level=$(git diff-tree -r -M HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/R0*//") && + test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 && + git config git-p4.detectRenames $(($level - 2)) && + git p4 submit && + p4 filelog //depot/file7 >filelog && + grep " from //depot/file6" filelog + ) +' + +# Copy a file and confirm that copy is not detected in P4. +# Copy a file with detectCopies option enabled and confirm that copy is not +# detected in P4. +# Modify and copy a file with detectCopies option enabled and confirm that copy +# is detected in P4. +# Copy a file with detectCopies and detectCopiesHarder options enabled and +# confirm that copy is detected in P4. +# Modify and copy a file, configure a bigger threshold in detectCopies and +# confirm that copy is not detected in P4. +# Modify and copy a file, configure a smaller threshold in detectCopies and +# confirm that copy is detected in P4. +test_expect_success 'detect copies' ' + git p4 clone --dest="$git" //depot@all && + test_when_finished cleanup_git && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + + cp file2 file8 && + git add file8 && + git commit -a -m "Copy file2 to file8" && + git diff-tree -r -C HEAD && + git p4 submit && + p4 filelog //depot/file8 && + p4 filelog //depot/file8 | test_must_fail grep -q "branch from" && + + cp file2 file9 && + git add file9 && + git commit -a -m "Copy file2 to file9" && + git diff-tree -r -C HEAD && + git config git-p4.detectCopies true && + git p4 submit && + p4 filelog //depot/file9 && + p4 filelog //depot/file9 | test_must_fail grep -q "branch from" && + + echo "file2" >>file2 && + cp file2 file10 && + git add file2 file10 && + git commit -a -m "Modify and copy file2 to file10" && + git diff-tree -r -C HEAD && + git p4 submit && + p4 filelog //depot/file10 && + p4 filelog //depot/file10 | grep -q "branch from //depot/file" && + + cp file2 file11 && + git add file11 && + git commit -a -m "Copy file2 to file11" && + git diff-tree -r -C --find-copies-harder HEAD && + src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && + test "$src" = file10 && + git config git-p4.detectCopiesHarder true && + git p4 submit && + p4 filelog //depot/file11 && + p4 filelog //depot/file11 | grep -q "branch from //depot/file" && + + cp file2 file12 && + echo "some text" >>file12 && + git add file12 && + git commit -a -m "Copy file2 to file12 with changes" && + git diff-tree -r -C --find-copies-harder HEAD && + level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") && + test -n "$level" && test "$level" -gt 0 && test "$level" -lt 98 && + src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && + test "$src" = file10 -o "$src" = file11 && + git config git-p4.detectCopies $(($level + 2)) && + git p4 submit && + p4 filelog //depot/file12 && + p4 filelog //depot/file12 | test_must_fail grep -q "branch from" && + + cp file2 file13 && + echo "different text" >>file13 && + git add file13 && + git commit -a -m "Copy file2 to file13 with changes" && + git diff-tree -r -C --find-copies-harder HEAD && + level=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f1 | cut -d" " -f5 | sed "s/C0*//") && + test -n "$level" && test "$level" -gt 2 && test "$level" -lt 100 && + src=$(git diff-tree -r -C --find-copies-harder HEAD | sed 1d | cut -f2) && + test "$src" = file10 -o "$src" = file11 -o "$src" = file12 && + git config git-p4.detectCopies $(($level - 2)) && + git p4 submit && + p4 filelog //depot/file13 && + p4 filelog //depot/file13 | grep -q "branch from //depot/file" + ) +' + +# See if configurables can be set, and in particular if the run.move.allow +# variable exists, which allows admins to disable the "p4 move" command. +test_expect_success 'p4 configure command and run.move.allow are available' ' + p4 configure show run.move.allow >out ; retval=$? && + test $retval = 0 && + { + egrep ^run.move.allow: out && + test_set_prereq P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW || + true + } || true +' + +# If move can be disabled, turn it off and test p4 move handling +test_expect_success P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW \ + 'do not use p4 move when administratively disabled' ' + test_when_finished "p4 configure set run.move.allow=1" && + p4 configure set run.move.allow=0 && + ( + cd "$cli" && + echo move-disallow-file >move-disallow-file && + p4 add move-disallow-file && + p4 submit -d "add move-disallow-file" + ) && + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + git config git-p4.detectRenames true && + git mv move-disallow-file move-disallow-file-moved && + git commit -m "move move-disallow-file" && + git p4 submit + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9815-git-p4-submit-fail.sh b/t/t9815-git-p4-submit-fail.sh new file mode 100755 index 0000000000..1243d96092 --- /dev/null +++ b/t/t9815-git-p4-submit-fail.sh @@ -0,0 +1,432 @@ +#!/bin/sh + +test_description='git p4 submit failure handling' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +test_expect_success 'init depot' ' + ( + cd "$cli" && + p4 client -o | sed "/LineEnd/s/:.*/:unix/" | p4 client -i && + echo line1 >file1 && + p4 add file1 && + p4 submit -d "line1 in file1" + ) +' + +test_expect_success 'conflict on one commit' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line2 >>file1 && + p4 submit -d "line2 in file1" + ) && + ( + # now this commit should cause a conflict + cd "$git" && + git config git-p4.skipSubmitEdit true && + echo line3 >>file1 && + git add file1 && + git commit -m "line3 in file1 will conflict" && + test_expect_code 1 git p4 submit >out && + test_i18ngrep "No commits applied" out + ) +' + +test_expect_success 'conflict on second of two commits' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line3 >>file1 && + p4 submit -d "line3 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this commit is okay + test_commit "first_commit_okay" && + # now this submit should cause a conflict + echo line4 >>file1 && + git add file1 && + git commit -m "line4 in file1 will conflict" && + test_expect_code 1 git p4 submit >out && + test_i18ngrep "Applied only the commits" out + ) +' + +test_expect_success 'conflict on first of two commits, skip' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line4 >>file1 && + p4 submit -d "line4 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this submit should cause a conflict + echo line5 >>file1 && + git add file1 && + git commit -m "line5 in file1 will conflict" && + # but this commit is okay + test_commit "okay_commit_after_skip" && + echo s | test_expect_code 1 git p4 submit >out && + test_i18ngrep "Applied only the commits" out + ) +' + +test_expect_success 'conflict on first of two commits, quit' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line7 >>file1 && + p4 submit -d "line7 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this submit should cause a conflict + echo line8 >>file1 && + git add file1 && + git commit -m "line8 in file1 will conflict" && + # but this commit is okay + test_commit "okay_commit_after_quit" && + echo q | test_expect_code 1 git p4 submit >out && + test_i18ngrep "No commits applied" out + ) +' + +test_expect_success 'conflict cli and config options' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git p4 submit --conflict=ask && + git p4 submit --conflict=skip && + git p4 submit --conflict=quit && + test_expect_code 2 git p4 submit --conflict=foo && + test_expect_code 2 git p4 submit --conflict && + git config git-p4.conflict foo && + test_expect_code 1 git p4 submit && + git config --unset git-p4.conflict && + git p4 submit + ) +' + +test_expect_success 'conflict on first of two commits, --conflict=skip' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line9 >>file1 && + p4 submit -d "line9 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this submit should cause a conflict + echo line10 >>file1 && + git add file1 && + git commit -m "line10 in file1 will conflict" && + # but this commit is okay + test_commit "okay_commit_after_auto_skip" && + test_expect_code 1 git p4 submit --conflict=skip >out && + test_i18ngrep "Applied only the commits" out + ) +' + +test_expect_success 'conflict on first of two commits, --conflict=quit' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 open file1 && + echo line11 >>file1 && + p4 submit -d "line11 in file1" + ) && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # this submit should cause a conflict + echo line12 >>file1 && + git add file1 && + git commit -m "line12 in file1 will conflict" && + # but this commit is okay + test_commit "okay_commit_after_auto_quit" && + test_expect_code 1 git p4 submit --conflict=quit >out && + test_i18ngrep "No commits applied" out + ) +' + +# +# Cleanup after submit fail, all cases. Some modifications happen +# before trying to apply the patch. Make sure these are unwound +# properly. Put each one in a diff along with something that will +# obviously conflict. Make sure it is back to normal after. +# + +test_expect_success 'cleanup edit p4 populate' ' + ( + cd "$cli" && + echo text file >text && + p4 add text && + echo text+x file >text+x && + chmod 755 text+x && + p4 add text+x && + p4 submit -d "populate p4" + ) +' + +setup_conflict() { + # clone before modifying file1 to force it to conflict + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + # ticks outside subshells + test_tick && + ( + cd "$cli" && + p4 open file1 && + echo $test_tick >>file1 && + p4 submit -d "$test_tick in file1" + ) && + test_tick && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + # easy conflict + echo $test_tick >>file1 && + git add file1 + # caller will add more and submit + ) +} + +test_expect_success 'cleanup edit after submit fail' ' + setup_conflict && + ( + cd "$git" && + echo another line >>text && + git add text && + git commit -m "conflict" && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + # make sure it is not open + ! p4 fstat -T action text + ) +' + +test_expect_success 'cleanup add after submit fail' ' + setup_conflict && + ( + cd "$git" && + echo new file >textnew && + git add textnew && + git commit -m "conflict" && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + # make sure it is not there + # and that p4 thinks it is not added + # P4 returns 0 both for "not there but added" and + # "not there", so grep. + test_path_is_missing textnew && + p4 fstat -T action textnew 2>&1 | grep "no such file" + ) +' + +test_expect_success 'cleanup delete after submit fail' ' + setup_conflict && + ( + cd "$git" && + git rm text+x && + git commit -m "conflict" && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + # make sure it is there + test_path_is_file text+x && + ! p4 fstat -T action text+x + ) +' + +test_expect_success 'cleanup copy after submit fail' ' + setup_conflict && + ( + cd "$git" && + cp text text2 && + git add text2 && + git commit -m "conflict" && + git config git-p4.detectCopies true && + git config git-p4.detectCopiesHarder true && + # make sure setup is okay + git diff-tree -r -C --find-copies-harder HEAD | grep text2 | grep C100 && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing text2 && + p4 fstat -T action text2 2>&1 | grep "no such file" + ) +' + +test_expect_success 'cleanup rename after submit fail' ' + setup_conflict && + ( + cd "$git" && + git mv text text2 && + git commit -m "conflict" && + git config git-p4.detectRenames true && + # make sure setup is okay + git diff-tree -r -M HEAD | grep text2 | grep R100 && + test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing text2 && + p4 fstat -T action text2 2>&1 | grep "no such file" + ) +' + +# +# Cleanup after deciding not to submit during editTemplate. This +# involves unwinding more work, because files have been added, deleted +# and chmod-ed now. Same approach as above. +# + +test_expect_success 'cleanup edit after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo line >>text && + git add text && + git commit -m text && + echo n | test_expect_code 1 git p4 submit && + git reset --hard HEAD^ + ) && + ( + cd "$cli" && + ! p4 fstat -T action text && + test_cmp "$git"/text text + ) +' + +test_expect_success 'cleanup add after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + echo line >textnew && + git add textnew && + git commit -m textnew && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing textnew && + p4 fstat -T action textnew 2>&1 | grep "no such file" + ) +' + +test_expect_success 'cleanup delete after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git rm text && + git commit -m "rm text" && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file text && + ! p4 fstat -T action text + ) +' + +test_expect_success 'cleanup copy after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + cp text text2 && + git add text2 && + git commit -m text2 && + git config git-p4.detectCopies true && + git config git-p4.detectCopiesHarder true && + git diff-tree -r -C --find-copies-harder HEAD | grep text2 | grep C100 && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing text2 && + p4 fstat -T action text2 2>&1 | grep "no such file" + ) +' + +test_expect_success 'cleanup rename after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git mv text text2 && + git commit -m text2 && + git config git-p4.detectRenames true && + git diff-tree -r -M HEAD | grep text2 | grep R100 && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_missing text2 && + p4 fstat -T action text2 2>&1 | grep "no such file" + test_path_is_file text && + ! p4 fstat -T action text + ) +' + +test_expect_success 'cleanup chmod after submit cancel' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + test_chmod +x text && + test_chmod -x text+x && + git add text text+x && + git commit -m "chmod texts" && + echo n | test_expect_code 1 git p4 submit + ) && + ( + cd "$cli" && + test_path_is_file text && + ! p4 fstat -T action text && + test_path_is_file text+x && + ! p4 fstat -T action text+x && + if test_have_prereq NOT_CYGWIN + then + stat --format=%A text | egrep ^-r-- && + stat --format=%A text+x | egrep ^-r-x + fi + ) +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh new file mode 100755 index 0000000000..2d4beb5e50 --- /dev/null +++ b/t/t9902-completion.sh @@ -0,0 +1,562 @@ +#!/bin/sh +# +# Copyright (c) 2012 Felipe Contreras +# + +test_description='test bash completion' + +. ./lib-bash.sh + +complete () +{ + # do nothing + return 0 +} + +# Be careful when updating this list: +# +# (1) The build tree may have build artifact from different branch, or +# the user's $PATH may have a random executable that may begin +# with "git-check" that are not part of the subcommands this build +# will ship, e.g. "check-ignore". The tests for completion for +# subcommand names tests how "check" is expanded; we limit the +# possible candidates to "checkout" and "check-attr" to make sure +# "check-attr", which is known by the filter function as a +# subcommand to be thrown out, while excluding other random files +# that happen to begin with "check" to avoid letting them get in +# the way. +# +# (2) A test makes sure that common subcommands are included in the +# completion for "git <TAB>", and a plumbing is excluded. "add", +# "filter-branch" and "ls-files" are listed for this. + +GIT_TESTING_COMMAND_COMPLETION='add checkout check-attr filter-branch ls-files' + +. "$GIT_BUILD_DIR/contrib/completion/git-completion.bash" + +# We don't need this function to actually join words or do anything special. +# Also, it's cleaner to avoid touching bash's internal completion variables. +# So let's override it with a minimal version for testing purposes. +_get_comp_words_by_ref () +{ + while [ $# -gt 0 ]; do + case "$1" in + cur) + cur=${_words[_cword]} + ;; + prev) + prev=${_words[_cword-1]} + ;; + words) + words=("${_words[@]}") + ;; + cword) + cword=$_cword + ;; + esac + shift + done +} + +print_comp () +{ + local IFS=$'\n' + echo "${COMPREPLY[*]}" > out +} + +run_completion () +{ + local -a COMPREPLY _words + local _cword + _words=( $1 ) + test "${1: -1}" = ' ' && _words[${#_words[@]}+1]='' + (( _cword = ${#_words[@]} - 1 )) + __git_wrap__git_main && print_comp +} + +# Test high-level completion +# Arguments are: +# 1: typed text so far (cur) +# 2: expected completion +test_completion () +{ + if test $# -gt 1 + then + printf '%s\n' "$2" >expected + else + sed -e 's/Z$//' >expected + fi && + run_completion "$1" && + test_cmp expected out +} + +# Test __gitcomp. +# The first argument is the typed text so far (cur); the rest are +# passed to __gitcomp. Expected output comes is read from the +# standard input, like test_completion(). +test_gitcomp () +{ + local -a COMPREPLY && + sed -e 's/Z$//' >expected && + cur="$1" && + shift && + __gitcomp "$@" && + print_comp && + test_cmp expected out +} + +# Test __gitcomp_nl +# Arguments are: +# 1: current word (cur) +# -: the rest are passed to __gitcomp_nl +test_gitcomp_nl () +{ + local -a COMPREPLY && + sed -e 's/Z$//' >expected && + cur="$1" && + shift && + __gitcomp_nl "$@" && + print_comp && + test_cmp expected out +} + +invalid_variable_name='${foo.bar}' + +actual="$TRASH_DIRECTORY/actual" + +test_expect_success 'setup for __gitdir tests' ' + mkdir -p subdir/subsubdir && + git init otherrepo +' + +test_expect_success '__gitdir - from command line (through $__git_dir)' ' + echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && + ( + __git_dir="$TRASH_DIRECTORY/otherrepo/.git" && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - repo as argument' ' + echo "otherrepo/.git" >expected && + __gitdir "otherrepo" >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - remote as argument' ' + echo "remote" >expected && + __gitdir "remote" >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - .git directory in cwd' ' + echo ".git" >expected && + __gitdir >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - .git directory in parent' ' + echo "$(pwd -P)/.git" >expected && + ( + cd subdir/subsubdir && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - cwd is a .git directory' ' + echo "." >expected && + ( + cd .git && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - parent is a .git directory' ' + echo "$(pwd -P)/.git" >expected && + ( + cd .git/refs/heads && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' ' + echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && + ( + GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && + export GIT_DIR && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' ' + echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && + ( + GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && + export GIT_DIR && + cd subdir && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - non-existing $GIT_DIR' ' + ( + GIT_DIR="$TRASH_DIRECTORY/non-existing" && + export GIT_DIR && + test_must_fail __gitdir + ) +' + +test_expect_success '__gitdir - gitfile in cwd' ' + echo "$(pwd -P)/otherrepo/.git" >expected && + echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git && + test_when_finished "rm -f subdir/.git" && + ( + cd subdir && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - gitfile in parent' ' + echo "$(pwd -P)/otherrepo/.git" >expected && + echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git && + test_when_finished "rm -f subdir/.git" && + ( + cd subdir/subsubdir && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' ' + echo "$(pwd -P)/otherrepo/.git" >expected && + mkdir otherrepo/dir && + test_when_finished "rm -rf otherrepo/dir" && + ln -s otherrepo/dir link && + test_when_finished "rm -f link" && + ( + cd link && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__gitdir - not a git repository' ' + ( + cd subdir/subsubdir && + GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" && + export GIT_CEILING_DIRECTORIES && + test_must_fail __gitdir + ) +' + +test_expect_success '__gitcomp - trailing space - options' ' + test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message= + --reset-author" <<-EOF + --reuse-message=Z + --reedit-message=Z + --reset-author Z + EOF +' + +test_expect_success '__gitcomp - trailing space - config keys' ' + test_gitcomp "br" "branch. branch.autosetupmerge + branch.autosetuprebase browser." <<-\EOF + branch.Z + branch.autosetupmerge Z + branch.autosetuprebase Z + browser.Z + EOF +' + +test_expect_success '__gitcomp - option parameter' ' + test_gitcomp "--strategy=re" "octopus ours recursive resolve subtree" \ + "" "re" <<-\EOF + recursive Z + resolve Z + EOF +' + +test_expect_success '__gitcomp - prefix' ' + test_gitcomp "branch.me" "remote merge mergeoptions rebase" \ + "branch.maint." "me" <<-\EOF + branch.maint.merge Z + branch.maint.mergeoptions Z + EOF +' + +test_expect_success '__gitcomp - suffix' ' + test_gitcomp "branch.me" "master maint next pu" "branch." \ + "ma" "." <<-\EOF + branch.master.Z + branch.maint.Z + EOF +' + +test_expect_success '__gitcomp - doesnt fail because of invalid variable name' ' + __gitcomp "$invalid_variable_name" +' + +read -r -d "" refs <<-\EOF +maint +master +next +pu +EOF + +test_expect_success '__gitcomp_nl - trailing space' ' + test_gitcomp_nl "m" "$refs" <<-EOF + maint Z + master Z + EOF +' + +test_expect_success '__gitcomp_nl - prefix' ' + test_gitcomp_nl "--fixup=m" "$refs" "--fixup=" "m" <<-EOF + --fixup=maint Z + --fixup=master Z + EOF +' + +test_expect_success '__gitcomp_nl - suffix' ' + test_gitcomp_nl "branch.ma" "$refs" "branch." "ma" "." <<-\EOF + branch.maint.Z + branch.master.Z + EOF +' + +test_expect_success '__gitcomp_nl - no suffix' ' + test_gitcomp_nl "ma" "$refs" "" "ma" "" <<-\EOF + maintZ + masterZ + EOF +' + +test_expect_success '__gitcomp_nl - doesnt fail because of invalid variable name' ' + __gitcomp_nl "$invalid_variable_name" +' + +test_expect_success 'basic' ' + run_completion "git " && + # built-in + grep -q "^add \$" out && + # script + grep -q "^filter-branch \$" out && + # plumbing + ! grep -q "^ls-files \$" out && + + run_completion "git f" && + ! grep -q -v "^f" out +' + +test_expect_success 'double dash "git" itself' ' + test_completion "git --" <<-\EOF + --paginate Z + --no-pager Z + --git-dir= + --bare Z + --version Z + --exec-path Z + --exec-path= + --html-path Z + --man-path Z + --info-path Z + --work-tree= + --namespace= + --no-replace-objects Z + --help Z + EOF +' + +test_expect_success 'double dash "git checkout"' ' + test_completion "git checkout --" <<-\EOF + --quiet Z + --ours Z + --theirs Z + --track Z + --no-track Z + --merge Z + --conflict= + --orphan Z + --patch Z + EOF +' + +test_expect_success 'general options' ' + test_completion "git --ver" "--version " && + test_completion "git --hel" "--help " && + test_completion "git --exe" <<-\EOF && + --exec-path Z + --exec-path= + EOF + test_completion "git --htm" "--html-path " && + test_completion "git --pag" "--paginate " && + test_completion "git --no-p" "--no-pager " && + test_completion "git --git" "--git-dir=" && + test_completion "git --wor" "--work-tree=" && + test_completion "git --nam" "--namespace=" && + test_completion "git --bar" "--bare " && + test_completion "git --inf" "--info-path " && + test_completion "git --no-r" "--no-replace-objects " +' + +test_expect_success 'general options plus command' ' + test_completion "git --version check" "checkout " && + test_completion "git --paginate check" "checkout " && + test_completion "git --git-dir=foo check" "checkout " && + test_completion "git --bare check" "checkout " && + test_completion "git --exec-path=foo check" "checkout " && + test_completion "git --html-path check" "checkout " && + test_completion "git --no-pager check" "checkout " && + test_completion "git --work-tree=foo check" "checkout " && + test_completion "git --namespace=foo check" "checkout " && + test_completion "git --paginate check" "checkout " && + test_completion "git --info-path check" "checkout " && + test_completion "git --no-replace-objects check" "checkout " +' + +test_expect_success 'git --help completion' ' + test_completion "git --help ad" "add " && + test_completion "git --help core" "core-tutorial " +' + +test_expect_success 'setup for ref completion' ' + echo content >file1 && + echo more >file2 && + git add . && + git commit -m one && + git branch mybranch && + git tag mytag +' + +test_expect_success 'checkout completes ref names' ' + test_completion "git checkout m" <<-\EOF + master Z + mybranch Z + mytag Z + EOF +' + +test_expect_success 'show completes all refs' ' + test_completion "git show m" <<-\EOF + master Z + mybranch Z + mytag Z + EOF +' + +test_expect_success '<ref>: completes paths' ' + test_completion "git show mytag:f" <<-\EOF + file1 Z + file2 Z + EOF +' + +test_expect_success 'complete tree filename with spaces' ' + echo content >"name with spaces" && + git add . && + git commit -m spaces && + test_completion "git show HEAD:nam" <<-\EOF + name with spaces Z + EOF +' + +test_expect_success 'complete tree filename with metacharacters' ' + echo content >"name with \${meta}" && + git add . && + git commit -m meta && + test_completion "git show HEAD:nam" <<-\EOF + name with ${meta} Z + name with spaces Z + EOF +' + +test_expect_success 'send-email' ' + test_completion "git send-email --cov" "--cover-letter " && + test_completion "git send-email ma" "master " +' + +test_expect_success 'complete files' ' + git init tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + echo "expected" > .gitignore && + echo "out" >> .gitignore && + + git add .gitignore && + test_completion "git commit " ".gitignore" && + + git commit -m ignore && + + touch new && + test_completion "git add " "new" && + + git add new && + git commit -a -m new && + test_completion "git add " "" && + + git mv new modified && + echo modify > modified && + test_completion "git add " "modified" && + + touch untracked && + + : TODO .gitignore should not be here && + test_completion "git rm " <<-\EOF && + .gitignore + modified + EOF + + test_completion "git clean " "untracked" && + + : TODO .gitignore should not be here && + test_completion "git mv " <<-\EOF && + .gitignore + modified + EOF + + mkdir dir && + touch dir/file-in-dir && + git add dir/file-in-dir && + git commit -m dir && + + mkdir untracked-dir && + + : TODO .gitignore should not be here && + test_completion "git mv modified " <<-\EOF && + .gitignore + dir + modified + untracked + untracked-dir + EOF + + test_completion "git commit " "modified" && + + : TODO .gitignore should not be here && + test_completion "git ls-files " <<-\EOF + .gitignore + dir + modified + EOF + + touch momified && + test_completion "git add mom" "momified" +' + +test_expect_failure 'complete with tilde expansion' ' + git init tmp && cd tmp && + test_when_finished "cd .. && rm -rf tmp" && + + touch ~/tmp/file && + + test_completion "git add ~/tmp/" "~/tmp/file" +' + +test_done diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh new file mode 100755 index 0000000000..59f875e830 --- /dev/null +++ b/t/t9903-bash-prompt.sh @@ -0,0 +1,591 @@ +#!/bin/sh +# +# Copyright (c) 2012 SZEDER Gábor +# + +test_description='test git-specific bash prompt functions' + +. ./lib-bash.sh + +. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh" + +actual="$TRASH_DIRECTORY/actual" +c_red='\\[\\e[31m\\]' +c_green='\\[\\e[32m\\]' +c_lblue='\\[\\e[1;34m\\]' +c_clear='\\[\\e[0m\\]' + +test_expect_success 'setup for prompt tests' ' + git init otherrepo && + echo 1 >file && + git add file && + test_tick && + git commit -m initial && + git tag -a -m msg1 t1 && + git checkout -b b1 && + echo 2 >file && + git commit -m "second b1" file && + echo 3 >file && + git commit -m "third b1" file && + git tag -a -m msg2 t2 && + git checkout -b b2 master && + echo 0 >file && + git commit -m "second b2" file && + echo 00 >file && + git commit -m "another b2" file && + echo 000 >file && + git commit -m "yet another b2" file && + git checkout master +' + +test_expect_success 'prompt - branch name' ' + printf " (master)" >expected && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success SYMLINKS 'prompt - branch name - symlink symref' ' + printf " (master)" >expected && + test_when_finished "git checkout master" && + test_config core.preferSymlinkRefs true && + git checkout master && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - unborn branch' ' + printf " (unborn)" >expected && + git checkout --orphan unborn && + test_when_finished "git checkout master" && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +repo_with_newline='repo +with +newline' + +if mkdir "$repo_with_newline" 2>/dev/null +then + test_set_prereq FUNNYNAMES +else + say 'Your filesystem does not allow newlines in filenames.' +fi + +test_expect_success FUNNYNAMES 'prompt - with newline in path' ' + printf " (master)" >expected && + git init "$repo_with_newline" && + test_when_finished "rm -rf \"$repo_with_newline\"" && + mkdir "$repo_with_newline"/subdir && + ( + cd "$repo_with_newline/subdir" && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - detached head' ' + printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected && + test_config core.abbrev 13 && + git checkout b1^ && + test_when_finished "git checkout master" && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - describe detached head - contains' ' + printf " ((t2~1))" >expected && + git checkout b1^ && + test_when_finished "git checkout master" && + ( + GIT_PS1_DESCRIBE_STYLE=contains && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - describe detached head - branch' ' + printf " ((b1~1))" >expected && + git checkout b1^ && + test_when_finished "git checkout master" && + ( + GIT_PS1_DESCRIBE_STYLE=branch && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - describe detached head - describe' ' + printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected && + git checkout b1^ && + test_when_finished "git checkout master" && + ( + GIT_PS1_DESCRIBE_STYLE=describe && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - describe detached head - default' ' + printf " ((t2))" >expected && + git checkout --detach b1 && + test_when_finished "git checkout master" && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - inside .git directory' ' + printf " (GIT_DIR!)" >expected && + ( + cd .git && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - deep inside .git directory' ' + printf " (GIT_DIR!)" >expected && + ( + cd .git/refs/heads && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - inside bare repository' ' + printf " (BARE:master)" >expected && + git init --bare bare.git && + test_when_finished "rm -rf bare.git" && + ( + cd bare.git && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - interactive rebase' ' + printf " (b1|REBASE-i 2/3)" >expected + write_script fake_editor.sh <<-\EOF && + echo "exec echo" >"$1" + echo "edit $(git log -1 --format="%h")" >>"$1" + echo "exec echo" >>"$1" + EOF + test_when_finished "rm -f fake_editor.sh" && + test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" && + git checkout b1 && + test_when_finished "git checkout master" && + git rebase -i HEAD^ && + test_when_finished "git rebase --abort" + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - rebase merge' ' + printf " (b2|REBASE-m 1/3)" >expected && + git checkout b2 && + test_when_finished "git checkout master" && + test_must_fail git rebase --merge b1 b2 && + test_when_finished "git rebase --abort" && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - rebase' ' + printf " (b2|REBASE 1/3)" >expected && + git checkout b2 && + test_when_finished "git checkout master" && + test_must_fail git rebase b1 b2 && + test_when_finished "git rebase --abort" && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - merge' ' + printf " (b1|MERGING)" >expected && + git checkout b1 && + test_when_finished "git checkout master" && + test_must_fail git merge b2 && + test_when_finished "git reset --hard" && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - cherry-pick' ' + printf " (master|CHERRY-PICKING)" >expected && + test_must_fail git cherry-pick b1 && + test_when_finished "git reset --hard" && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bisect' ' + printf " (master|BISECTING)" >expected && + git bisect start && + test_when_finished "git bisect reset" && + __git_ps1 >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - clean' ' + printf " (master)" >expected && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - dirty worktree' ' + printf " (master *)" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - dirty index' ' + printf " (master +)" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + git add -u && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - dirty index and worktree' ' + printf " (master *+)" >expected && + echo "dirty index" >file && + test_when_finished "git reset --hard" && + git add -u && + echo "dirty worktree" >file && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - before root commit' ' + printf " (master #)" >expected && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + cd otherrepo && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' ' + printf " (master)" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + test_config bash.showDirtyState false && + ( + sane_unset GIT_PS1_SHOWDIRTYSTATE && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' ' + printf " (master)" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + test_config bash.showDirtyState true && + ( + sane_unset GIT_PS1_SHOWDIRTYSTATE && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' ' + printf " (master)" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + test_config bash.showDirtyState false && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' ' + printf " (master *)" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + test_config bash.showDirtyState true && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' ' + printf " (GIT_DIR!)" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + cd .git && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - stash status indicator - no stash' ' + printf " (master)" >expected && + ( + GIT_PS1_SHOWSTASHSTATE=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - stash status indicator - stash' ' + printf " (master $)" >expected && + echo 2 >file && + git stash && + test_when_finished "git stash drop" && + git pack-refs --all && + ( + GIT_PS1_SHOWSTASHSTATE=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - stash status indicator - not shown inside .git directory' ' + printf " (GIT_DIR!)" >expected && + echo 2 >file && + git stash && + test_when_finished "git stash drop" && + ( + GIT_PS1_SHOWSTASHSTATE=y && + cd .git && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - untracked files status indicator - no untracked files' ' + printf " (master)" >expected && + ( + GIT_PS1_SHOWUNTRACKEDFILES=y && + cd otherrepo && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - untracked files status indicator - untracked files' ' + printf " (master %%)" >expected && + ( + GIT_PS1_SHOWUNTRACKEDFILES=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' ' + printf " (master)" >expected && + test_config bash.showUntrackedFiles false && + ( + sane_unset GIT_PS1_SHOWUNTRACKEDFILES && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' ' + printf " (master)" >expected && + test_config bash.showUntrackedFiles true && + ( + sane_unset GIT_PS1_SHOWUNTRACKEDFILES && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' ' + printf " (master)" >expected && + test_config bash.showUntrackedFiles false && + ( + GIT_PS1_SHOWUNTRACKEDFILES=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' ' + printf " (master %%)" >expected && + test_config bash.showUntrackedFiles true && + ( + GIT_PS1_SHOWUNTRACKEDFILES=y && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' ' + printf " (GIT_DIR!)" >expected && + ( + GIT_PS1_SHOWUNTRACKEDFILES=y && + cd .git && + __git_ps1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - format string starting with dash' ' + printf -- "-master" >expected && + __git_ps1 "-%s" >"$actual" && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - pc mode' ' + printf "BEFORE: (master):AFTER" >expected && + printf "" >expected_output && + ( + __git_ps1 "BEFORE:" ":AFTER" >"$actual" && + test_cmp expected_output "$actual" && + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bash color pc mode - branch name' ' + printf "BEFORE: (${c_green}master${c_clear}):AFTER" >expected && + ( + GIT_PS1_SHOWCOLORHINTS=y && + __git_ps1 "BEFORE:" ":AFTER" >"$actual" + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bash color pc mode - detached head' ' + printf "BEFORE: (${c_red}(%s...)${c_clear}):AFTER" $(git log -1 --format="%h" b1^) >expected && + git checkout b1^ && + test_when_finished "git checkout master" && + ( + GIT_PS1_SHOWCOLORHINTS=y && + __git_ps1 "BEFORE:" ":AFTER" && + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' ' + printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_clear}):AFTER" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + GIT_PS1_SHOWCOLORHINTS=y && + __git_ps1 "BEFORE:" ":AFTER" && + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' ' + printf "BEFORE: (${c_green}master${c_clear} ${c_green}+${c_clear}):AFTER" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + git add -u && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + GIT_PS1_SHOWCOLORHINTS=y && + __git_ps1 "BEFORE:" ":AFTER" && + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' ' + printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER" >expected && + echo "dirty index" >file && + test_when_finished "git reset --hard" && + git add -u && + echo "dirty worktree" >file && + ( + GIT_PS1_SHOWCOLORHINTS=y && + GIT_PS1_SHOWDIRTYSTATE=y && + __git_ps1 "BEFORE:" ":AFTER" && + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' ' + printf "BEFORE: (${c_green}master${c_clear} ${c_green}#${c_clear}):AFTER" >expected && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + GIT_PS1_SHOWCOLORHINTS=y && + cd otherrepo && + __git_ps1 "BEFORE:" ":AFTER" && + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bash color pc mode - inside .git directory' ' + printf "BEFORE: (${c_green}GIT_DIR!${c_clear}):AFTER" >expected && + echo "dirty" >file && + test_when_finished "git reset --hard" && + ( + GIT_PS1_SHOWDIRTYSTATE=y && + GIT_PS1_SHOWCOLORHINTS=y && + cd .git && + __git_ps1 "BEFORE:" ":AFTER" && + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bash color pc mode - stash status indicator' ' + printf "BEFORE: (${c_green}master${c_clear} ${c_lblue}\$${c_clear}):AFTER" >expected && + echo 2 >file && + git stash && + test_when_finished "git stash drop" && + ( + GIT_PS1_SHOWSTASHSTATE=y && + GIT_PS1_SHOWCOLORHINTS=y && + __git_ps1 "BEFORE:" ":AFTER" && + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - bash color pc mode - untracked files status indicator' ' + printf "BEFORE: (${c_green}master${c_clear} ${c_red}%%${c_clear}):AFTER" >expected && + ( + GIT_PS1_SHOWUNTRACKEDFILES=y && + GIT_PS1_SHOWCOLORHINTS=y && + __git_ps1 "BEFORE:" ":AFTER" && + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'prompt - zsh color pc mode' ' + printf "BEFORE: (%%F{green}master%%f):AFTER" >expected && + ( + ZSH_VERSION=5.0.0 && + GIT_PS1_SHOWCOLORHINTS=y && + __git_ps1 "BEFORE:" ":AFTER" >"$actual" + printf "%s" "$PS1" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_done diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh new file mode 100644 index 0000000000..2f79146e6c --- /dev/null +++ b/t/test-lib-functions.sh @@ -0,0 +1,782 @@ +#!/bin/sh +# +# Copyright (c) 2005 Junio C Hamano +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see http://www.gnu.org/licenses/ . + +# The semantics of the editor variables are that of invoking +# sh -c "$EDITOR \"$@\"" files ... +# +# If our trash directory contains shell metacharacters, they will be +# interpreted if we just set $EDITOR directly, so do a little dance with +# environment variables to work around this. +# +# In particular, quoting isn't enough, as the path may contain the same quote +# that we're using. +test_set_editor () { + FAKE_EDITOR="$1" + export FAKE_EDITOR + EDITOR='"$FAKE_EDITOR"' + export EDITOR +} + +test_decode_color () { + awk ' + function name(n) { + if (n == 0) return "RESET"; + if (n == 1) return "BOLD"; + if (n == 30) return "BLACK"; + if (n == 31) return "RED"; + if (n == 32) return "GREEN"; + if (n == 33) return "YELLOW"; + if (n == 34) return "BLUE"; + if (n == 35) return "MAGENTA"; + if (n == 36) return "CYAN"; + if (n == 37) return "WHITE"; + if (n == 40) return "BLACK"; + if (n == 41) return "BRED"; + if (n == 42) return "BGREEN"; + if (n == 43) return "BYELLOW"; + if (n == 44) return "BBLUE"; + if (n == 45) return "BMAGENTA"; + if (n == 46) return "BCYAN"; + if (n == 47) return "BWHITE"; + } + { + while (match($0, /\033\[[0-9;]*m/) != 0) { + printf "%s<", substr($0, 1, RSTART-1); + codes = substr($0, RSTART+2, RLENGTH-3); + if (length(codes) == 0) + printf "%s", name(0) + else { + n = split(codes, ary, ";"); + sep = ""; + for (i = 1; i <= n; i++) { + printf "%s%s", sep, name(ary[i]); + sep = ";" + } + } + printf ">"; + $0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1); + } + print + } + ' +} + +nul_to_q () { + perl -pe 'y/\000/Q/' +} + +q_to_nul () { + perl -pe 'y/Q/\000/' +} + +q_to_cr () { + tr Q '\015' +} + +q_to_tab () { + tr Q '\011' +} + +qz_to_tab_space () { + tr QZ '\011\040' +} + +append_cr () { + sed -e 's/$/Q/' | tr Q '\015' +} + +remove_cr () { + tr '\015' Q | sed -e 's/Q$//' +} + +# In some bourne shell implementations, the "unset" builtin returns +# nonzero status when a variable to be unset was not set in the first +# place. +# +# Use sane_unset when that should not be considered an error. + +sane_unset () { + unset "$@" + return 0 +} + +test_tick () { + if test -z "${test_tick+set}" + then + test_tick=1112911993 + else + test_tick=$(($test_tick + 60)) + fi + GIT_COMMITTER_DATE="$test_tick -0700" + GIT_AUTHOR_DATE="$test_tick -0700" + export GIT_COMMITTER_DATE GIT_AUTHOR_DATE +} + +# Stop execution and start a shell. This is useful for debugging tests and +# only makes sense together with "-v". +# +# Be sure to remove all invocations of this command before submitting. + +test_pause () { + if test "$verbose" = t; then + "$SHELL_PATH" <&6 >&3 2>&4 + else + error >&5 "test_pause requires --verbose" + fi +} + +# Call test_commit with the arguments "<message> [<file> [<contents> [<tag>]]]" +# +# This will commit a file with the given contents and the given commit +# message, and tag the resulting commit with the given tag name. +# +# <file>, <contents>, and <tag> all default to <message>. + +test_commit () { + notick= && + signoff= && + while test $# != 0 + do + case "$1" in + --notick) + notick=yes + ;; + --signoff) + signoff="$1" + ;; + *) + break + ;; + esac + shift + done && + file=${2:-"$1.t"} && + echo "${3-$1}" > "$file" && + git add "$file" && + if test -z "$notick" + then + test_tick + fi && + git commit $signoff -m "$1" && + git tag "${4:-$1}" +} + +# Call test_merge with the arguments "<message> <commit>", where <commit> +# can be a tag pointing to the commit-to-merge. + +test_merge () { + test_tick && + git merge -m "$1" "$2" && + git tag "$1" +} + +# This function helps systems where core.filemode=false is set. +# Use it instead of plain 'chmod +x' to set or unset the executable bit +# of a file in the working directory and add it to the index. + +test_chmod () { + chmod "$@" && + git update-index --add "--chmod=$@" +} + +# Unset a configuration variable, but don't fail if it doesn't exist. +test_unconfig () { + git config --unset-all "$@" + config_status=$? + case "$config_status" in + 5) # ok, nothing to unset + config_status=0 + ;; + esac + return $config_status +} + +# Set git config, automatically unsetting it after the test is over. +test_config () { + test_when_finished "test_unconfig '$1'" && + git config "$@" +} + +test_config_global () { + test_when_finished "test_unconfig --global '$1'" && + git config --global "$@" +} + +write_script () { + { + echo "#!${2-"$SHELL_PATH"}" && + cat + } >"$1" && + chmod +x "$1" +} + +# Use test_set_prereq to tell that a particular prerequisite is available. +# The prerequisite can later be checked for in two ways: +# +# - Explicitly using test_have_prereq. +# +# - Implicitly by specifying the prerequisite tag in the calls to +# test_expect_{success,failure,code}. +# +# The single parameter is the prerequisite tag (a simple word, in all +# capital letters by convention). + +test_set_prereq () { + satisfied_prereq="$satisfied_prereq$1 " +} +satisfied_prereq=" " +lazily_testable_prereq= lazily_tested_prereq= + +# Usage: test_lazy_prereq PREREQ 'script' +test_lazy_prereq () { + lazily_testable_prereq="$lazily_testable_prereq$1 " + eval test_prereq_lazily_$1=\$2 +} + +test_run_lazy_prereq_ () { + script=' +mkdir -p "$TRASH_DIRECTORY/prereq-test-dir" && +( + cd "$TRASH_DIRECTORY/prereq-test-dir" &&'"$2"' +)' + say >&3 "checking prerequisite: $1" + say >&3 "$script" + test_eval_ "$script" + eval_ret=$? + rm -rf "$TRASH_DIRECTORY/prereq-test-dir" + if test "$eval_ret" = 0; then + say >&3 "prerequisite $1 ok" + else + say >&3 "prerequisite $1 not satisfied" + fi + return $eval_ret +} + +test_have_prereq () { + # prerequisites can be concatenated with ',' + save_IFS=$IFS + IFS=, + set -- $* + IFS=$save_IFS + + total_prereq=0 + ok_prereq=0 + missing_prereq= + + for prerequisite + do + case "$prerequisite" in + !*) + negative_prereq=t + prerequisite=${prerequisite#!} + ;; + *) + negative_prereq= + esac + + case " $lazily_tested_prereq " in + *" $prerequisite "*) + ;; + *) + case " $lazily_testable_prereq " in + *" $prerequisite "*) + eval "script=\$test_prereq_lazily_$prerequisite" && + if test_run_lazy_prereq_ "$prerequisite" "$script" + then + test_set_prereq $prerequisite + fi + lazily_tested_prereq="$lazily_tested_prereq$prerequisite " + esac + ;; + esac + + total_prereq=$(($total_prereq + 1)) + case "$satisfied_prereq" in + *" $prerequisite "*) + satisfied_this_prereq=t + ;; + *) + satisfied_this_prereq= + esac + + case "$satisfied_this_prereq,$negative_prereq" in + t,|,t) + ok_prereq=$(($ok_prereq + 1)) + ;; + *) + # Keep a list of missing prerequisites; restore + # the negative marker if necessary. + prerequisite=${negative_prereq:+!}$prerequisite + if test -z "$missing_prereq" + then + missing_prereq=$prerequisite + else + missing_prereq="$prerequisite,$missing_prereq" + fi + esac + done + + test $total_prereq = $ok_prereq +} + +test_declared_prereq () { + case ",$test_prereq," in + *,$1,*) + return 0 + ;; + esac + return 1 +} + +test_expect_failure () { + test_start_ + test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= + test "$#" = 2 || + error "bug in the test script: not 2 or 3 parameters to test-expect-failure" + export test_prereq + if ! test_skip "$@" + then + say >&3 "checking known breakage: $2" + if test_run_ "$2" expecting_failure + then + test_known_broken_ok_ "$1" + else + test_known_broken_failure_ "$1" + fi + fi + test_finish_ +} + +test_expect_success () { + test_start_ + test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= + test "$#" = 2 || + error "bug in the test script: not 2 or 3 parameters to test-expect-success" + export test_prereq + if ! test_skip "$@" + then + say >&3 "expecting success: $2" + if test_run_ "$2" + then + test_ok_ "$1" + else + test_failure_ "$@" + fi + fi + test_finish_ +} + +# test_external runs external test scripts that provide continuous +# test output about their progress, and succeeds/fails on +# zero/non-zero exit code. It outputs the test output on stdout even +# in non-verbose mode, and announces the external script with "# run +# <n>: ..." before running it. When providing relative paths, keep in +# mind that all scripts run in "trash directory". +# Usage: test_external description command arguments... +# Example: test_external 'Perl API' perl ../path/to/test.pl +test_external () { + test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq= + test "$#" = 3 || + error >&5 "bug in the test script: not 3 or 4 parameters to test_external" + descr="$1" + shift + export test_prereq + if ! test_skip "$descr" "$@" + then + # Announce the script to reduce confusion about the + # test output that follows. + say_color "" "# run $test_count: $descr ($*)" + # Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG + # to be able to use them in script + export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG + # Run command; redirect its stderr to &4 as in + # test_run_, but keep its stdout on our stdout even in + # non-verbose mode. + "$@" 2>&4 + if [ "$?" = 0 ] + then + if test $test_external_has_tap -eq 0; then + test_ok_ "$descr" + else + say_color "" "# test_external test $descr was ok" + test_success=$(($test_success + 1)) + fi + else + if test $test_external_has_tap -eq 0; then + test_failure_ "$descr" "$@" + else + say_color error "# test_external test $descr failed: $@" + test_failure=$(($test_failure + 1)) + fi + fi + fi +} + +# Like test_external, but in addition tests that the command generated +# no output on stderr. +test_external_without_stderr () { + # The temporary file has no (and must have no) security + # implications. + tmp=${TMPDIR:-/tmp} + stderr="$tmp/git-external-stderr.$$.tmp" + test_external "$@" 4> "$stderr" + [ -f "$stderr" ] || error "Internal error: $stderr disappeared." + descr="no stderr: $1" + shift + say >&3 "# expecting no stderr from previous command" + if [ ! -s "$stderr" ]; then + rm "$stderr" + + if test $test_external_has_tap -eq 0; then + test_ok_ "$descr" + else + say_color "" "# test_external_without_stderr test $descr was ok" + test_success=$(($test_success + 1)) + fi + else + if [ "$verbose" = t ]; then + output=`echo; echo "# Stderr is:"; cat "$stderr"` + else + output= + fi + # rm first in case test_failure exits. + rm "$stderr" + if test $test_external_has_tap -eq 0; then + test_failure_ "$descr" "$@" "$output" + else + say_color error "# test_external_without_stderr test $descr failed: $@: $output" + test_failure=$(($test_failure + 1)) + fi + fi +} + +# debugging-friendly alternatives to "test [-f|-d|-e]" +# The commands test the existence or non-existence of $1. $2 can be +# given to provide a more precise diagnosis. +test_path_is_file () { + if ! [ -f "$1" ] + then + echo "File $1 doesn't exist. $*" + false + fi +} + +test_path_is_dir () { + if ! [ -d "$1" ] + then + echo "Directory $1 doesn't exist. $*" + false + fi +} + +test_path_is_missing () { + if [ -e "$1" ] + then + echo "Path exists:" + ls -ld "$1" + if [ $# -ge 1 ]; then + echo "$*" + fi + false + fi +} + +# test_line_count checks that a file has the number of lines it +# ought to. For example: +# +# test_expect_success 'produce exactly one line of output' ' +# do something >output && +# test_line_count = 1 output +# ' +# +# is like "test $(wc -l <output) = 1" except that it passes the +# output through when the number of lines is wrong. + +test_line_count () { + if test $# != 3 + then + error "bug in the test script: not 3 parameters to test_line_count" + elif ! test $(wc -l <"$3") "$1" "$2" + then + echo "test_line_count: line count for $3 !$1 $2" + cat "$3" + return 1 + fi +} + +# This is not among top-level (test_expect_success | test_expect_failure) +# but is a prefix that can be used in the test script, like: +# +# test_expect_success 'complain and die' ' +# do something && +# do something else && +# test_must_fail git checkout ../outerspace +# ' +# +# Writing this as "! git checkout ../outerspace" is wrong, because +# the failure could be due to a segv. We want a controlled failure. + +test_must_fail () { + "$@" + exit_code=$? + if test $exit_code = 0; then + echo >&2 "test_must_fail: command succeeded: $*" + return 1 + elif test $exit_code -gt 129 -a $exit_code -le 192; then + echo >&2 "test_must_fail: died by signal: $*" + return 1 + elif test $exit_code = 127; then + echo >&2 "test_must_fail: command not found: $*" + return 1 + elif test $exit_code = 126; then + echo >&2 "test_must_fail: valgrind error: $*" + return 1 + fi + return 0 +} + +# Similar to test_must_fail, but tolerates success, too. This is +# meant to be used in contexts like: +# +# test_expect_success 'some command works without configuration' ' +# test_might_fail git config --unset all.configuration && +# do something +# ' +# +# Writing "git config --unset all.configuration || :" would be wrong, +# because we want to notice if it fails due to segv. + +test_might_fail () { + "$@" + exit_code=$? + if test $exit_code -gt 129 -a $exit_code -le 192; then + echo >&2 "test_might_fail: died by signal: $*" + return 1 + elif test $exit_code = 127; then + echo >&2 "test_might_fail: command not found: $*" + return 1 + fi + return 0 +} + +# Similar to test_must_fail and test_might_fail, but check that a +# given command exited with a given exit code. Meant to be used as: +# +# test_expect_success 'Merge with d/f conflicts' ' +# test_expect_code 1 git merge "merge msg" B master +# ' + +test_expect_code () { + want_code=$1 + shift + "$@" + exit_code=$? + if test $exit_code = $want_code + then + return 0 + fi + + echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*" + return 1 +} + +# test_cmp is a helper function to compare actual and expected output. +# You can use it like: +# +# test_expect_success 'foo works' ' +# echo expected >expected && +# foo >actual && +# test_cmp expected actual +# ' +# +# This could be written as either "cmp" or "diff -u", but: +# - cmp's output is not nearly as easy to read as diff -u +# - not all diff versions understand "-u" + +test_cmp() { + $GIT_TEST_CMP "$@" +} + +# Check if the file expected to be empty is indeed empty, and barfs +# otherwise. + +test_must_be_empty () { + if test -s "$1" + then + echo "'$1' is not empty, it contains:" + cat "$1" + return 1 + fi +} + +# Tests that its two parameters refer to the same revision +test_cmp_rev () { + git rev-parse --verify "$1" >expect.rev && + git rev-parse --verify "$2" >actual.rev && + test_cmp expect.rev actual.rev +} + +# Print a sequence of numbers or letters in increasing order. This is +# similar to GNU seq(1), but the latter might not be available +# everywhere (and does not do letters). It may be used like: +# +# for i in `test_seq 100`; do +# for j in `test_seq 10 20`; do +# for k in `test_seq a z`; do +# echo $i-$j-$k +# done +# done +# done + +test_seq () { + case $# in + 1) set 1 "$@" ;; + 2) ;; + *) error "bug in the test script: not 1 or 2 parameters to test_seq" ;; + esac + perl -le 'print for $ARGV[0]..$ARGV[1]' -- "$@" +} + +# This function can be used to schedule some commands to be run +# unconditionally at the end of the test to restore sanity: +# +# test_expect_success 'test core.capslock' ' +# git config core.capslock true && +# test_when_finished "git config --unset core.capslock" && +# hello world +# ' +# +# That would be roughly equivalent to +# +# test_expect_success 'test core.capslock' ' +# git config core.capslock true && +# hello world +# git config --unset core.capslock +# ' +# +# except that the greeting and config --unset must both succeed for +# the test to pass. +# +# Note that under --immediate mode, no clean-up is done to help diagnose +# what went wrong. + +test_when_finished () { + test_cleanup="{ $* + } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup" +} + +# Most tests can use the created repository, but some may 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" + repo="$1" + mkdir -p "$repo" + ( + cd "$repo" || error "Cannot setup test environment" + "$GIT_EXEC_PATH/git-init" "--template=$GIT_BUILD_DIR/templates/blt/" >&3 2>&4 || + error "cannot run git init -- have you built things yet?" + mv .git/hooks .git/hooks-disabled + ) || exit +} + +# This function helps on symlink challenged file systems when it is not +# important that the file system entry is a symbolic link. +# Use test_ln_s_add instead of "ln -s x y && git add y" to add a +# symbolic link entry y to the index. + +test_ln_s_add () { + if test_have_prereq SYMLINKS + then + ln -s "$1" "$2" && + git update-index --add "$2" + else + printf '%s' "$1" >"$2" && + ln_s_obj=$(git hash-object -w "$2") && + git update-index --add --cacheinfo 120000 $ln_s_obj "$2" + fi +} + +perl () { + command "$PERL_PATH" "$@" +} + +# The following mingw_* functions obey POSIX shell syntax, but are actually +# bash scripts, and are meant to be used only with bash on Windows. + +# A test_cmp function that treats LF and CRLF equal and avoids to fork +# diff when possible. +mingw_test_cmp () { + # Read text into shell variables and compare them. If the results + # are different, use regular diff to report the difference. + local test_cmp_a= test_cmp_b= + + # When text came from stdin (one argument is '-') we must feed it + # to diff. + local stdin_for_diff= + + # Since it is difficult to detect the difference between an + # empty input file and a failure to read the files, we go straight + # to diff if one of the inputs is empty. + if test -s "$1" && test -s "$2" + then + # regular case: both files non-empty + mingw_read_file_strip_cr_ test_cmp_a <"$1" + mingw_read_file_strip_cr_ test_cmp_b <"$2" + elif test -s "$1" && test "$2" = - + then + # read 2nd file from stdin + mingw_read_file_strip_cr_ test_cmp_a <"$1" + mingw_read_file_strip_cr_ test_cmp_b + stdin_for_diff='<<<"$test_cmp_b"' + elif test "$1" = - && test -s "$2" + then + # read 1st file from stdin + mingw_read_file_strip_cr_ test_cmp_a + mingw_read_file_strip_cr_ test_cmp_b <"$2" + stdin_for_diff='<<<"$test_cmp_a"' + fi + test -n "$test_cmp_a" && + test -n "$test_cmp_b" && + test "$test_cmp_a" = "$test_cmp_b" || + eval "diff -u \"\$@\" $stdin_for_diff" +} + +# $1 is the name of the shell variable to fill in +mingw_read_file_strip_cr_ () { + # Read line-wise using LF as the line separator + # and use IFS to strip CR. + local line + while : + do + if IFS=$'\r' read -r -d $'\n' line + then + # good + line=$line$'\n' + else + # we get here at EOF, but also if the last line + # was not terminated by LF; in the latter case, + # some text was read + if test -z "$line" + then + # EOF, really + break + fi + fi + eval "$1=\$$1\$line" + done +} diff --git a/t/test-lib.sh b/t/test-lib.sh index c0d04c494a..b25249ec4c 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -15,6 +15,38 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/ . +# Keep the original TERM for say_color +ORIGINAL_TERM=$TERM + +# Test the binaries we have just built. The tests are kept in +# t/ subdirectory and are run in 'trash directory' subdirectory. +if test -z "$TEST_DIRECTORY" +then + # We allow tests to override this, in case they want to run tests + # outside of t/, e.g. for running tests on the test library + # itself. + TEST_DIRECTORY=$(pwd) +fi +if test -z "$TEST_OUTPUT_DIRECTORY" +then + # Similarly, override this to store the test-results subdir + # elsewhere + TEST_OUTPUT_DIRECTORY=$TEST_DIRECTORY +fi +GIT_BUILD_DIR="$TEST_DIRECTORY"/.. + +################################################################ +# It appears that people try to run tests without building... +"$GIT_BUILD_DIR/git" >/dev/null +if test $? != 1 +then + echo >&2 'error: you do not seem to have built git yet.' + exit 1 +fi + +. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS +export PERL_PATH SHELL_PATH + # if --tee was passed, write the output not only to the terminal, but # additionally to the file test-results/$BASENAME.out, too. case "$GIT_TEST_TEE_STARTED, $* " in @@ -22,18 +54,15 @@ done,*) # do not redirect again ;; *' --tee '*|*' --va'*) - mkdir -p test-results - BASE=test-results/$(basename "$0" .sh) - (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1; + mkdir -p "$TEST_OUTPUT_DIRECTORY/test-results" + BASE="$TEST_OUTPUT_DIRECTORY/test-results/$(basename "$0" .sh)" + (GIT_TEST_TEE_STARTED=done ${SHELL_PATH} "$0" "$@" 2>&1; echo $? > $BASE.exit) | tee $BASE.out test "$(cat $BASE.exit)" = 0 exit ;; esac -# Keep the original TERM for say_color -ORIGINAL_TERM=$TERM - # For repeatability, reset the environment to known value. LANG=C LC_ALL=C @@ -46,7 +75,7 @@ EDITOR=: # /usr/xpg4/bin/sh and /bin/ksh to bail out. So keep the unsets # deriving from the command substitution clustered with the other # ones. -unset VISUAL EMAIL LANGUAGE $(perl -e ' +unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e ' my @env = keys %ENV; my $ok = join("|", qw( TRACE @@ -56,33 +85,60 @@ unset VISUAL EMAIL LANGUAGE $(perl -e ' .*_TEST PROVE VALGRIND + UNZIP + PERF_ )); my @vars = grep(/^GIT_/ && !/^GIT_($ok)/o, @env); print join("\n", @vars); ') +unset XDG_CONFIG_HOME +unset GITPERLLIB GIT_AUTHOR_EMAIL=author@example.com GIT_AUTHOR_NAME='A U Thor' GIT_COMMITTER_EMAIL=committer@example.com GIT_COMMITTER_NAME='C O Mitter' GIT_MERGE_VERBOSITY=5 -export GIT_MERGE_VERBOSITY +GIT_MERGE_AUTOEDIT=no +export GIT_MERGE_VERBOSITY GIT_MERGE_AUTOEDIT export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME export EDITOR +# Add libc MALLOC and MALLOC_PERTURB test +# only if we are not executing the test with valgrind +if expr " $GIT_TEST_OPTS " : ".* --valgrind " >/dev/null || + test -n "$TEST_NO_MALLOC_CHECK" +then + setup_malloc_check () { + : nothing + } + teardown_malloc_check () { + : nothing + } +else + setup_malloc_check () { + MALLOC_CHECK_=3 MALLOC_PERTURB_=165 + export MALLOC_CHECK_ MALLOC_PERTURB_ + } + teardown_malloc_check () { + unset MALLOC_CHECK_ MALLOC_PERTURB_ + } +fi + # Protect ourselves from common misconfiguration to export # CDPATH into the environment unset CDPATH unset GREP_OPTIONS +unset UNZIP case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in - 1|2|true) - echo "* warning: Some tests will not work if GIT_TRACE" \ - "is set as to trace on STDERR ! *" - echo "* warning: Please set GIT_TRACE to something" \ - "other than 1, 2 or true ! *" - ;; +1|2|true) + echo "* warning: Some tests will not work if GIT_TRACE" \ + "is set as to trace on STDERR ! *" + echo "* warning: Please set GIT_TRACE to something" \ + "other than 1, 2 or true ! *" + ;; esac # Convenience @@ -98,6 +154,8 @@ _z40=0000000000000000000000000000000000000000 LF=' ' +export _x05 _x40 _z40 LF + # Each test should start with something like this, after copyright notices: # # test_description='Description of this test... @@ -127,6 +185,9 @@ do help=t; shift ;; -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose) verbose=t; shift ;; + --verbose-only=*) + verbose_only=$(expr "z$1" : 'z[^=]*=\(.*\)') + shift ;; -q|--q|--qu|--qui|--quie|--quiet) # Ignore --quiet under a TAP::Harness. Saying how many tests # passed without the ok/not ok details is always an error. @@ -136,7 +197,14 @@ do --no-color) color=; shift ;; --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) - valgrind=t; verbose=t; shift ;; + valgrind=memcheck + shift ;; + --valgrind=*) + valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)') + shift ;; + --valgrind-only=*) + valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)') + shift ;; --tee) shift ;; # was handled already --root=*) @@ -147,17 +215,34 @@ do esac done -if test -n "$color"; then +if test -n "$valgrind_only" +then + test -z "$valgrind" && valgrind=memcheck + test -z "$verbose" && verbose_only="$valgrind_only" +elif test -n "$valgrind" +then + verbose=t +fi + +if test -n "$color" +then say_color () { ( TERM=$ORIGINAL_TERM export TERM case "$1" in - error) tput bold; tput setaf 1;; # bold red - skip) tput bold; tput setaf 2;; # bold green - pass) tput setaf 2;; # green - info) tput setaf 3;; # brown - *) test -n "$quiet" && return;; + error) + tput bold; tput setaf 1;; # bold red + skip) + tput setaf 4;; # blue + warn) + tput setaf 3;; # brown/yellow + pass) + tput setaf 2;; # green + info) + tput setaf 6;; # cyan + *) + test -n "$quiet" && return;; esac shift printf "%s" "$*" @@ -169,7 +254,7 @@ else say_color() { test -z "$1" && test -n "$quiet" && return shift - echo "$*" + printf "%s\n" "$*" } fi @@ -223,238 +308,12 @@ die () { GIT_EXIT_OK= trap 'die' EXIT -# The semantics of the editor variables are that of invoking -# sh -c "$EDITOR \"$@\"" files ... -# -# If our trash directory contains shell metacharacters, they will be -# interpreted if we just set $EDITOR directly, so do a little dance with -# environment variables to work around this. -# -# In particular, quoting isn't enough, as the path may contain the same quote -# that we're using. -test_set_editor () { - FAKE_EDITOR="$1" - export FAKE_EDITOR - EDITOR='"$FAKE_EDITOR"' - export EDITOR -} - -test_decode_color () { - awk ' - function name(n) { - if (n == 0) return "RESET"; - if (n == 1) return "BOLD"; - if (n == 30) return "BLACK"; - if (n == 31) return "RED"; - if (n == 32) return "GREEN"; - if (n == 33) return "YELLOW"; - if (n == 34) return "BLUE"; - if (n == 35) return "MAGENTA"; - if (n == 36) return "CYAN"; - if (n == 37) return "WHITE"; - if (n == 40) return "BLACK"; - if (n == 41) return "BRED"; - if (n == 42) return "BGREEN"; - if (n == 43) return "BYELLOW"; - if (n == 44) return "BBLUE"; - if (n == 45) return "BMAGENTA"; - if (n == 46) return "BCYAN"; - if (n == 47) return "BWHITE"; - } - { - while (match($0, /\033\[[0-9;]*m/) != 0) { - printf "%s<", substr($0, 1, RSTART-1); - codes = substr($0, RSTART+2, RLENGTH-3); - if (length(codes) == 0) - printf "%s", name(0) - else { - n = split(codes, ary, ";"); - sep = ""; - for (i = 1; i <= n; i++) { - printf "%s%s", sep, name(ary[i]); - sep = ";" - } - } - printf ">"; - $0 = substr($0, RSTART + RLENGTH, length($0) - RSTART - RLENGTH + 1); - } - print - } - ' -} - -nul_to_q () { - perl -pe 'y/\000/Q/' -} - -q_to_nul () { - perl -pe 'y/Q/\000/' -} - -q_to_cr () { - tr Q '\015' -} - -q_to_tab () { - tr Q '\011' -} - -append_cr () { - sed -e 's/$/Q/' | tr Q '\015' -} - -remove_cr () { - tr '\015' Q | sed -e 's/Q$//' -} - -# In some bourne shell implementations, the "unset" builtin returns -# nonzero status when a variable to be unset was not set in the first -# place. -# -# Use sane_unset when that should not be considered an error. - -sane_unset () { - unset "$@" - return 0 -} - -test_tick () { - if test -z "${test_tick+set}" - then - test_tick=1112911993 - else - test_tick=$(($test_tick + 60)) - fi - GIT_COMMITTER_DATE="$test_tick -0700" - GIT_AUTHOR_DATE="$test_tick -0700" - export GIT_COMMITTER_DATE GIT_AUTHOR_DATE -} - -# Call test_commit with the arguments "<message> [<file> [<contents>]]" -# -# This will commit a file with the given contents and the given commit -# message. It will also add a tag with <message> as name. -# -# Both <file> and <contents> default to <message>. - -test_commit () { - file=${2:-"$1.t"} - echo "${3-$1}" > "$file" && - git add "$file" && - test_tick && - git commit -m "$1" && - git tag "$1" -} - -# Call test_merge with the arguments "<message> <commit>", where <commit> -# can be a tag pointing to the commit-to-merge. - -test_merge () { - test_tick && - git merge -m "$1" "$2" && - git tag "$1" -} - -# This function helps systems where core.filemode=false is set. -# Use it instead of plain 'chmod +x' to set or unset the executable bit -# of a file in the working directory and add it to the index. - -test_chmod () { - chmod "$@" && - git update-index --add "--chmod=$@" -} - -# Unset a configuration variable, but don't fail if it doesn't exist. -test_unconfig () { - git config --unset-all "$@" - config_status=$? - case "$config_status" in - 5) # ok, nothing to unset - config_status=0 - ;; - esac - return $config_status -} - -# Set git config, automatically unsetting it after the test is over. -test_config () { - test_when_finished "test_unconfig '$1'" && - git config "$@" -} - - -test_config_global () { - test_when_finished "test_unconfig --global '$1'" && - git config --global "$@" -} - -write_script () { - { - echo "#!${2-"$SHELL_PATH"}" && - cat - } >"$1" && - chmod +x "$1" -} - -# Use test_set_prereq to tell that a particular prerequisite is available. -# The prerequisite can later be checked for in two ways: -# -# - Explicitly using test_have_prereq. -# -# - Implicitly by specifying the prerequisite tag in the calls to -# test_expect_{success,failure,code}. -# -# The single parameter is the prerequisite tag (a simple word, in all -# capital letters by convention). - -test_set_prereq () { - satisfied="$satisfied$1 " -} -satisfied=" " - -test_have_prereq () { - # prerequisites can be concatenated with ',' - save_IFS=$IFS - IFS=, - set -- $* - IFS=$save_IFS - - total_prereq=0 - ok_prereq=0 - missing_prereq= - - for prerequisite - do - total_prereq=$(($total_prereq + 1)) - case $satisfied in - *" $prerequisite "*) - ok_prereq=$(($ok_prereq + 1)) - ;; - *) - # Keep a list of missing prerequisites - if test -z "$missing_prereq" - then - missing_prereq=$prerequisite - else - missing_prereq="$prerequisite,$missing_prereq" - fi - esac - done - - test $total_prereq = $ok_prereq -} - -test_declared_prereq () { - case ",$test_prereq," in - *,$1,*) - return 0 - ;; - esac - return 1 -} +# The user-facing functions are loaded from a separate file so that +# test_perf subshells can have them too +. "$TEST_DIRECTORY/test-lib-functions.sh" # You are not expected to call test_ok_ and test_failure_ directly, use -# the text_expect_* functions instead. +# the test_expect_* functions instead. test_ok_ () { test_success=$(($test_success + 1)) @@ -463,7 +322,7 @@ test_ok_ () { test_failure_ () { test_failure=$(($test_failure + 1)) - say_color error "not ok - $test_count $1" + say_color error "not ok $test_count - $1" shift echo "$@" | sed -e 's/^/# /' test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; } @@ -471,18 +330,77 @@ test_failure_ () { test_known_broken_ok_ () { test_fixed=$(($test_fixed+1)) - say_color "" "ok $test_count - $@ # TODO known breakage" + say_color error "ok $test_count - $@ # TODO known breakage vanished" } test_known_broken_failure_ () { test_broken=$(($test_broken+1)) - say_color skip "not ok $test_count - $@ # TODO known breakage" + say_color warn "not ok $test_count - $@ # TODO known breakage" } test_debug () { test "$debug" = "" || eval "$1" } +match_pattern_list () { + arg="$1" + shift + test -z "$*" && return 1 + for pattern_ + do + case "$arg" in + $pattern_) + return 0 + esac + done + return 1 +} + +maybe_teardown_verbose () { + test -z "$verbose_only" && return + exec 4>/dev/null 3>/dev/null + verbose= +} + +last_verbose=t +maybe_setup_verbose () { + test -z "$verbose_only" && return + if match_pattern_list $test_count $verbose_only + then + exec 4>&2 3>&1 + # Emit a delimiting blank line when going from + # non-verbose to verbose. Within verbose mode the + # delimiter is printed by test_expect_*. The choice + # of the initial $last_verbose is such that before + # test 1, we do not print it. + test -z "$last_verbose" && echo >&3 "" + verbose=t + else + exec 4>/dev/null 3>/dev/null + verbose= + fi + last_verbose=$verbose +} + +maybe_teardown_valgrind () { + test -z "$GIT_VALGRIND" && return + GIT_VALGRIND_ENABLED= +} + +maybe_setup_valgrind () { + test -z "$GIT_VALGRIND" && return + if test -z "$valgrind_only" + then + GIT_VALGRIND_ENABLED=t + return + fi + GIT_VALGRIND_ENABLED= + if match_pattern_list $test_count $valgrind_only + then + GIT_VALGRIND_ENABLED=t + fi +} + test_eval_ () { # This is a separate function because some tests use # "return" to end a test_expect_success block early. @@ -492,30 +410,42 @@ test_eval_ () { test_run_ () { test_cleanup=: expecting_failure=$2 + setup_malloc_check test_eval_ "$1" eval_ret=$? + teardown_malloc_check if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure" then + setup_malloc_check test_eval_ "$test_cleanup" + teardown_malloc_check fi - if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then + if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE" + then echo "" fi return "$eval_ret" } -test_skip () { +test_start_ () { test_count=$(($test_count+1)) + maybe_setup_verbose + maybe_setup_valgrind +} + +test_finish_ () { + echo >&3 "" + maybe_teardown_valgrind + maybe_teardown_verbose +} + +test_skip () { to_skip= - for skp in $GIT_SKIP_TESTS - do - case $this_test.$test_count in - $skp) - to_skip=t - break - esac - done + if match_pattern_list $this_test.$test_count $GIT_SKIP_TESTS + then + to_skip=t + fi if test -z "$to_skip" && test -n "$test_prereq" && ! test_have_prereq "$test_prereq" then @@ -539,320 +469,22 @@ test_skip () { esac } -test_expect_failure () { - test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= - test "$#" = 2 || - error "bug in the test script: not 2 or 3 parameters to test-expect-failure" - export test_prereq - if ! test_skip "$@" - then - say >&3 "checking known breakage: $2" - if test_run_ "$2" expecting_failure - then - test_known_broken_ok_ "$1" - else - test_known_broken_failure_ "$1" - fi - fi - echo >&3 "" -} - -test_expect_success () { - test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq= - test "$#" = 2 || - error "bug in the test script: not 2 or 3 parameters to test-expect-success" - export test_prereq - if ! test_skip "$@" - then - say >&3 "expecting success: $2" - if test_run_ "$2" - then - test_ok_ "$1" - else - test_failure_ "$@" - fi - fi - echo >&3 "" -} - -# test_external runs external test scripts that provide continuous -# test output about their progress, and succeeds/fails on -# zero/non-zero exit code. It outputs the test output on stdout even -# in non-verbose mode, and announces the external script with "# run -# <n>: ..." before running it. When providing relative paths, keep in -# mind that all scripts run in "trash directory". -# Usage: test_external description command arguments... -# Example: test_external 'Perl API' perl ../path/to/test.pl -test_external () { - test "$#" = 4 && { test_prereq=$1; shift; } || test_prereq= - test "$#" = 3 || - error >&5 "bug in the test script: not 3 or 4 parameters to test_external" - descr="$1" - shift - export test_prereq - if ! test_skip "$descr" "$@" - then - # Announce the script to reduce confusion about the - # test output that follows. - say_color "" "# run $test_count: $descr ($*)" - # Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG - # to be able to use them in script - export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG - # Run command; redirect its stderr to &4 as in - # test_run_, but keep its stdout on our stdout even in - # non-verbose mode. - "$@" 2>&4 - if [ "$?" = 0 ] - then - if test $test_external_has_tap -eq 0; then - test_ok_ "$descr" - else - say_color "" "# test_external test $descr was ok" - test_success=$(($test_success + 1)) - fi - else - if test $test_external_has_tap -eq 0; then - test_failure_ "$descr" "$@" - else - say_color error "# test_external test $descr failed: $@" - test_failure=$(($test_failure + 1)) - fi - fi - fi -} - -# Like test_external, but in addition tests that the command generated -# no output on stderr. -test_external_without_stderr () { - # The temporary file has no (and must have no) security - # implications. - tmp=${TMPDIR:-/tmp} - stderr="$tmp/git-external-stderr.$$.tmp" - test_external "$@" 4> "$stderr" - [ -f "$stderr" ] || error "Internal error: $stderr disappeared." - descr="no stderr: $1" - shift - say >&3 "# expecting no stderr from previous command" - if [ ! -s "$stderr" ]; then - rm "$stderr" - - if test $test_external_has_tap -eq 0; then - test_ok_ "$descr" - else - say_color "" "# test_external_without_stderr test $descr was ok" - test_success=$(($test_success + 1)) - fi - else - if [ "$verbose" = t ]; then - output=`echo; echo "# Stderr is:"; cat "$stderr"` - else - output= - fi - # rm first in case test_failure exits. - rm "$stderr" - if test $test_external_has_tap -eq 0; then - test_failure_ "$descr" "$@" "$output" - else - say_color error "# test_external_without_stderr test $descr failed: $@: $output" - test_failure=$(($test_failure + 1)) - fi - fi -} - -# debugging-friendly alternatives to "test [-f|-d|-e]" -# The commands test the existence or non-existence of $1. $2 can be -# given to provide a more precise diagnosis. -test_path_is_file () { - if ! [ -f "$1" ] - then - echo "File $1 doesn't exist. $*" - false - fi -} - -test_path_is_dir () { - if ! [ -d "$1" ] - then - echo "Directory $1 doesn't exist. $*" - false - fi -} - -test_path_is_missing () { - if [ -e "$1" ] - then - echo "Path exists:" - ls -ld "$1" - if [ $# -ge 1 ]; then - echo "$*" - fi - false - fi -} - -# test_line_count checks that a file has the number of lines it -# ought to. For example: -# -# test_expect_success 'produce exactly one line of output' ' -# do something >output && -# test_line_count = 1 output -# ' -# -# is like "test $(wc -l <output) = 1" except that it passes the -# output through when the number of lines is wrong. - -test_line_count () { - if test $# != 3 - then - error "bug in the test script: not 3 parameters to test_line_count" - elif ! test $(wc -l <"$3") "$1" "$2" - then - echo "test_line_count: line count for $3 !$1 $2" - cat "$3" - return 1 - fi -} - -# This is not among top-level (test_expect_success | test_expect_failure) -# but is a prefix that can be used in the test script, like: -# -# test_expect_success 'complain and die' ' -# do something && -# do something else && -# test_must_fail git checkout ../outerspace -# ' -# -# Writing this as "! git checkout ../outerspace" is wrong, because -# the failure could be due to a segv. We want a controlled failure. - -test_must_fail () { - "$@" - exit_code=$? - if test $exit_code = 0; then - echo >&2 "test_must_fail: command succeeded: $*" - return 1 - elif test $exit_code -gt 129 -a $exit_code -le 192; then - echo >&2 "test_must_fail: died by signal: $*" - return 1 - elif test $exit_code = 127; then - echo >&2 "test_must_fail: command not found: $*" - return 1 - fi - return 0 -} - -# Similar to test_must_fail, but tolerates success, too. This is -# meant to be used in contexts like: -# -# test_expect_success 'some command works without configuration' ' -# test_might_fail git config --unset all.configuration && -# do something -# ' -# -# Writing "git config --unset all.configuration || :" would be wrong, -# because we want to notice if it fails due to segv. - -test_might_fail () { - "$@" - exit_code=$? - if test $exit_code -gt 129 -a $exit_code -le 192; then - echo >&2 "test_might_fail: died by signal: $*" - return 1 - elif test $exit_code = 127; then - echo >&2 "test_might_fail: command not found: $*" - return 1 - fi - return 0 -} - -# Similar to test_must_fail and test_might_fail, but check that a -# given command exited with a given exit code. Meant to be used as: -# -# test_expect_success 'Merge with d/f conflicts' ' -# test_expect_code 1 git merge "merge msg" B master -# ' - -test_expect_code () { - want_code=$1 - shift - "$@" - exit_code=$? - if test $exit_code = $want_code - then - return 0 - fi - - echo >&2 "test_expect_code: command exited with $exit_code, we wanted $want_code $*" - return 1 -} - -# test_cmp is a helper function to compare actual and expected output. -# You can use it like: -# -# test_expect_success 'foo works' ' -# echo expected >expected && -# foo >actual && -# test_cmp expected actual -# ' -# -# This could be written as either "cmp" or "diff -u", but: -# - cmp's output is not nearly as easy to read as diff -u -# - not all diff versions understand "-u" - -test_cmp() { - $GIT_TEST_CMP "$@" -} - -# This function can be used to schedule some commands to be run -# unconditionally at the end of the test to restore sanity: -# -# test_expect_success 'test core.capslock' ' -# git config core.capslock true && -# test_when_finished "git config --unset core.capslock" && -# hello world -# ' -# -# That would be roughly equivalent to -# -# test_expect_success 'test core.capslock' ' -# git config core.capslock true && -# hello world -# git config --unset core.capslock -# ' -# -# except that the greeting and config --unset must both succeed for -# the test to pass. -# -# Note that under --immediate mode, no clean-up is done to help diagnose -# what went wrong. - -test_when_finished () { - test_cleanup="{ $* - } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup" -} - -# Most tests can use the created repository, but some may 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" - repo="$1" - mkdir -p "$repo" - ( - cd "$repo" || error "Cannot setup test environment" - "$GIT_EXEC_PATH/git-init" "--template=$GIT_BUILD_DIR/templates/blt/" >&3 2>&4 || - error "cannot run git init -- have you built things yet?" - mv .git/hooks .git/hooks-disabled - ) || exit +# stub; perf-lib overrides it +test_at_end_hook_ () { + : } test_done () { GIT_EXIT_OK=t - if test -z "$HARNESS_ACTIVE"; then - test_results_dir="$TEST_DIRECTORY/test-results" + # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts + # output file + if test -z "$HARNESS_ACTIVE" + then + test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results" mkdir -p "$test_results_dir" - test_results_path="$test_results_dir/${0%.sh}-$$.counts" + base=${0##*/} + test_results_path="$test_results_dir/${base%.sh}-$$.counts" cat >>"$test_results_path" <<-EOF total $test_count @@ -866,22 +498,35 @@ test_done () { if test "$test_fixed" != 0 then - say_color pass "# fixed $test_fixed known breakage(s)" + say_color error "# $test_fixed known breakage(s) vanished; please update test(s)" fi if test "$test_broken" != 0 then - say_color error "# still have $test_broken known breakage(s)" - msg="remaining $(($test_count-$test_broken)) test(s)" + say_color warn "# still have $test_broken known breakage(s)" + fi + if test "$test_broken" != 0 || test "$test_fixed" != 0 + then + test_remaining=$(( $test_count - $test_broken - $test_fixed )) + msg="remaining $test_remaining test(s)" else + test_remaining=$test_count msg="$test_count test(s)" fi case "$test_failure" in 0) # Maybe print SKIP message + if test -n "$skip_all" && test $test_count -gt 0 + then + error "Can't use skip_all after running some tests" + fi [ -z "$skip_all" ] || skip_all=" # SKIP $skip_all" - if test $test_external_has_tap -eq 0; then - say_color pass "# passed all $msg" + if test $test_external_has_tap -eq 0 + then + if test $test_remaining -gt 0 + then + say_color pass "# passed all $msg" + fi say "1..$test_count$skip_all" fi @@ -889,10 +534,13 @@ test_done () { cd "$(dirname "$remove_trash")" && rm -rf "$(basename "$remove_trash")" + test_at_end_hook_ + exit 0 ;; *) - if test $test_external_has_tap -eq 0; then + if test $test_external_has_tap -eq 0 + then say_color error "# failed $test_failure among $msg" say "1..$test_count" fi @@ -902,17 +550,6 @@ test_done () { esac } -# Test the binaries we have just built. The tests are kept in -# t/ subdirectory and are run in 'trash directory' subdirectory. -if test -z "$TEST_DIRECTORY" -then - # We allow tests to override this, in case they want to run tests - # outside of t/, e.g. for running tests on the test library - # itself. - TEST_DIRECTORY=$(pwd) -fi -GIT_BUILD_DIR="$TEST_DIRECTORY"/.. - if test -n "$valgrind" then make_symlink () { @@ -983,22 +620,31 @@ then PATH=$GIT_VALGRIND/bin:$PATH GIT_EXEC_PATH=$GIT_VALGRIND/bin export GIT_VALGRIND -elif test -n "$GIT_TEST_INSTALLED" ; then + GIT_VALGRIND_MODE="$valgrind" + export GIT_VALGRIND_MODE + GIT_VALGRIND_ENABLED=t + test -n "$valgrind_only" && GIT_VALGRIND_ENABLED= + export GIT_VALGRIND_ENABLED +elif test -n "$GIT_TEST_INSTALLED" +then GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path) || error "Cannot run git from $GIT_TEST_INSTALLED." PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR:$PATH GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH} else # normal case, use ../bin-wrappers only unless $with_dashes: git_bin_dir="$GIT_BUILD_DIR/bin-wrappers" - if ! test -x "$git_bin_dir/git" ; then - if test -z "$with_dashes" ; then + if ! test -x "$git_bin_dir/git" + then + if test -z "$with_dashes" + then say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH" fi with_dashes=t fi PATH="$git_bin_dir:$PATH" GIT_EXEC_PATH=$GIT_BUILD_DIR - if test -n "$with_dashes" ; then + if test -n "$with_dashes" + then PATH="$GIT_BUILD_DIR:$PATH" fi fi @@ -1008,8 +654,6 @@ GIT_CONFIG_NOSYSTEM=1 GIT_ATTR_NOSYSTEM=1 export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_ATTR_NOSYSTEM -. "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS - if test -z "$GIT_TEST_CMP" then if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT" @@ -1026,30 +670,22 @@ test -d "$GIT_BUILD_DIR"/templates/blt || { error "You haven't built things yet, have you?" } -if test -z "$GIT_TEST_INSTALLED" && test -z "$NO_PYTHON" +if ! test -x "$GIT_BUILD_DIR"/test-chmtime then - GITPYTHONLIB="$GIT_BUILD_DIR/git_remote_helpers/build/lib" - export GITPYTHONLIB - test -d "$GIT_BUILD_DIR"/git_remote_helpers/build || { - error "You haven't built git_remote_helpers yet, have you?" - } -fi - -if ! test -x "$GIT_BUILD_DIR"/test-chmtime; then echo >&2 'You need to build test-chmtime:' echo >&2 'Run "make test-chmtime" in the source (toplevel) directory' exit 1 fi # Test repository -test="trash directory.$(basename "$0" .sh)" -test -n "$root" && test="$root/$test" -case "$test" in -/*) TRASH_DIRECTORY="$test" ;; - *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;; +TRASH_DIRECTORY="trash directory.$(basename "$0" .sh)" +test -n "$root" && TRASH_DIRECTORY="$root/$TRASH_DIRECTORY" +case "$TRASH_DIRECTORY" in +/*) ;; # absolute path is good + *) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;; esac test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY -rm -fr "$test" || { +rm -fr "$TRASH_DIRECTORY" || { GIT_EXIT_OK=t echo >&5 "FATAL: Cannot prepare test area" exit 1 @@ -1058,22 +694,24 @@ rm -fr "$test" || { HOME="$TRASH_DIRECTORY" export HOME -test_create_repo "$test" +if test -z "$TEST_NO_CREATE_REPO" +then + test_create_repo "$TRASH_DIRECTORY" +else + mkdir -p "$TRASH_DIRECTORY" +fi # Use -P to resolve symlinks in our working directory so that the cwd # in subprocesses like git equals our $PWD (for pathname comparisons). -cd -P "$test" || exit 1 +cd -P "$TRASH_DIRECTORY" || exit 1 this_test=${0##*/} this_test=${this_test%%-*} -for skp in $GIT_SKIP_TESTS -do - case "$this_test" in - $skp) - say_color skip >&3 "skipping test $this_test altogether" - skip_all="skip all tests in $this_test" - test_done - esac -done +if match_pattern_list "$this_test" $GIT_SKIP_TESTS +then + say_color info >&3 "skipping test $this_test altogether" + skip_all="skip all tests in $this_test" + test_done +fi # Provide an implementation of the 'yes' utility yes () { @@ -1111,22 +749,29 @@ case $(uname -s) in # backslashes in pathspec are converted to '/' # exec does not inherit the PID test_set_prereq MINGW + test_set_prereq NOT_CYGWIN test_set_prereq SED_STRIPS_CR + test_set_prereq GREP_STRIPS_CR + GIT_TEST_CMP=mingw_test_cmp ;; *CYGWIN*) test_set_prereq POSIXPERM test_set_prereq EXECKEEPSPID test_set_prereq NOT_MINGW + test_set_prereq CYGWIN test_set_prereq SED_STRIPS_CR + test_set_prereq GREP_STRIPS_CR ;; *) test_set_prereq POSIXPERM test_set_prereq BSLASHPSPEC test_set_prereq EXECKEEPSPID test_set_prereq NOT_MINGW + test_set_prereq NOT_CYGWIN ;; esac +( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_PERL" && test_set_prereq PERL test -z "$NO_PYTHON" && test_set_prereq PYTHON test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE @@ -1168,10 +813,54 @@ test_i18ngrep () { fi } -# test whether the filesystem supports symbolic links -ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS -rm -f y +test_lazy_prereq PIPE ' + # test whether the filesystem supports FIFOs + case $(uname -s) in + CYGWIN*) + false + ;; + *) + rm -f testfifo && mkfifo testfifo + ;; + esac +' + +test_lazy_prereq SYMLINKS ' + # test whether the filesystem supports symbolic links + ln -s x y && test -h y +' + +test_lazy_prereq CASE_INSENSITIVE_FS ' + echo good >CamelCase && + echo bad >camelcase && + test "$(cat CamelCase)" != good +' + +test_lazy_prereq UTF8_NFD_TO_NFC ' + # check whether FS converts nfd unicode to nfc + auml=$(printf "\303\244") + aumlcdiar=$(printf "\141\314\210") + >"$auml" && + case "$(echo *)" in + "$aumlcdiar") + true ;; + *) + false ;; + esac +' + +test_lazy_prereq AUTOIDENT ' + sane_unset GIT_AUTHOR_NAME && + sane_unset GIT_AUTHOR_EMAIL && + git var GIT_AUTHOR_IDENT +' # When the tests are run as root, permission tests will report that # things are writable when they shouldn't be. test -w / || test_set_prereq SANITY + +GIT_UNZIP=${GIT_UNZIP:-unzip} +test_lazy_prereq UNZIP ' + "$GIT_UNZIP" -v + test $? -ne 127 +' diff --git a/t/test-terminal.perl b/t/test-terminal.perl index 10172aee18..1fb373f25b 100755 --- a/t/test-terminal.perl +++ b/t/test-terminal.perl @@ -31,7 +31,7 @@ sub finish_child { } elsif ($? & 127) { my $code = $? & 127; warn "died of signal $code"; - return $code - 128; + return $code + 128; } else { return $? >> 8; } diff --git a/t/valgrind/analyze.sh b/t/valgrind/analyze.sh index d8105d9fab..2ffc80f721 100755 --- a/t/valgrind/analyze.sh +++ b/t/valgrind/analyze.sh @@ -1,6 +1,10 @@ #!/bin/sh -out_prefix=$(dirname "$0")/../test-results/valgrind.out +# Get TEST_OUTPUT_DIRECTORY from GIT-BUILD-OPTIONS if it's there... +. "$(dirname "$0")/../../GIT-BUILD-OPTIONS" +# ... otherwise set it to the default value. +: ${TEST_OUTPUT_DIRECTORY=$(dirname "$0")/..} + output= count=0 total_count=0 @@ -115,7 +119,7 @@ handle_one () { finish_output } -for test_script in "$(dirname "$0")"/../test-results/*.out +for test_script in "$TEST_OUTPUT_DIRECTORY"/test-results/*.out do handle_one $test_script done diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh index 582b4dca94..42153036dc 100755 --- a/t/valgrind/valgrind.sh +++ b/t/valgrind/valgrind.sh @@ -2,20 +2,30 @@ base=$(basename "$0") -TRACK_ORIGINS= +TOOL_OPTIONS='--leak-check=no' -VALGRIND_VERSION=$(valgrind --version) -VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)') -VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)') -test 3 -gt "$VALGRIND_MAJOR" || -test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" || -TRACK_ORIGINS=--track-origins=yes +test -z "$GIT_VALGRIND_ENABLED" && +exec "$GIT_VALGRIND"/../../"$base" "$@" + +case "$GIT_VALGRIND_MODE" in +memcheck-fast) + ;; +memcheck) + VALGRIND_VERSION=$(valgrind --version) + VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)') + VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)') + test 3 -gt "$VALGRIND_MAJOR" || + test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" || + TOOL_OPTIONS="$TOOL_OPTIONS --track-origins=yes" + ;; +*) + TOOL_OPTIONS="--tool=$GIT_VALGRIND_MODE" +esac exec valgrind -q --error-exitcode=126 \ - --leak-check=no \ - --suppressions="$GIT_VALGRIND/default.supp" \ --gen-suppressions=all \ - $TRACK_ORIGINS \ + --suppressions="$GIT_VALGRIND/default.supp" \ + $TOOL_OPTIONS \ --log-fd=4 \ --input-fd=4 \ $GIT_VALGRIND_OPTIONS \ |