diff options
Diffstat (limited to 't')
173 files changed, 9970 insertions, 1470 deletions
diff --git a/t/Makefile b/t/Makefile index d613935f14..1bb06c36f2 100644 --- a/t/Makefile +++ b/t/Makefile @@ -35,6 +35,12 @@ all: $(DEFAULT_TEST_TARGET) test: pre-clean $(TEST_LINT) $(MAKE) aggregate-results-and-cleanup +failed: + @failed=$$(cd '$(TEST_RESULTS_DIRECTORY_SQ)' && \ + grep -l '^failed [1-9]' *.counts | \ + sed -n 's/\.counts$$/.sh/p') && \ + test -z "$$failed" || $(MAKE) $$failed + prove: pre-clean $(TEST_LINT) @echo "*** prove ***"; $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS) $(MAKE) clean-except-prove-cache diff --git a/t/helper/test-config.c b/t/helper/test-config.c index 3c6d08cd09..83a4f2ab86 100644 --- a/t/helper/test-config.c +++ b/t/helper/test-config.c @@ -72,6 +72,9 @@ int cmd_main(int argc, const char **argv) const char *v; const struct string_list *strptr; struct config_set cs; + + setup_git_directory(); + git_configset_init(&cs); if (argc < 2) { diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c index 44f3290258..7af116d49e 100644 --- a/t/helper/test-dump-cache-tree.c +++ b/t/helper/test-dump-cache-tree.c @@ -58,6 +58,7 @@ int cmd_main(int ac, const char **av) { struct index_state istate; struct cache_tree *another = cache_tree(); + setup_git_directory(); if (read_cache() < 0) die("unable to read index file"); istate = the_index; diff --git a/t/helper/test-dump-split-index.c b/t/helper/test-dump-split-index.c index d1689248b4..e44430b699 100644 --- a/t/helper/test-dump-split-index.c +++ b/t/helper/test-dump-split-index.c @@ -23,7 +23,7 @@ int cmd_main(int ac, const char **av) for (i = 0; i < the_index.cache_nr; i++) { struct cache_entry *ce = the_index.cache[i]; printf("%06o %s %d\t%s\n", ce->ce_mode, - sha1_to_hex(ce->sha1), ce_stage(ce), ce->name); + oid_to_hex(&ce->oid), ce_stage(ce), ce->name); } printf("replacements:"); if (si->replace_bitmap) diff --git a/t/helper/test-dump-untracked-cache.c b/t/helper/test-dump-untracked-cache.c index 50112cc858..f752532ffb 100644 --- a/t/helper/test-dump-untracked-cache.c +++ b/t/helper/test-dump-untracked-cache.c @@ -18,10 +18,8 @@ static int compare_dir(const void *a_, const void *b_) static void dump(struct untracked_cache_dir *ucd, struct strbuf *base) { int i, len; - qsort(ucd->untracked, ucd->untracked_nr, sizeof(*ucd->untracked), - compare_untracked); - qsort(ucd->dirs, ucd->dirs_nr, sizeof(*ucd->dirs), - compare_dir); + QSORT(ucd->untracked, ucd->untracked_nr, compare_untracked); + QSORT(ucd->dirs, ucd->dirs_nr, compare_dir); len = base->len; strbuf_addf(base, "%s/", ucd->name); printf("%s %s", base->buf, diff --git a/t/helper/test-scrap-cache-tree.c b/t/helper/test-scrap-cache-tree.c index 5b2fd09908..d2a63bea43 100644 --- a/t/helper/test-scrap-cache-tree.c +++ b/t/helper/test-scrap-cache-tree.c @@ -7,7 +7,8 @@ static struct lock_file index_lock; int cmd_main(int ac, const char **av) { - hold_locked_index(&index_lock, 1); + setup_git_directory(); + hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); if (read_cache() < 0) die("unable to read index file"); active_cache_tree = NULL; diff --git a/t/helper/test-sha1-array.c b/t/helper/test-sha1-array.c index 09f7790971..f7a53c4ad6 100644 --- a/t/helper/test-sha1-array.c +++ b/t/helper/test-sha1-array.c @@ -1,9 +1,10 @@ #include "cache.h" #include "sha1-array.h" -static void print_sha1(const unsigned char sha1[20], void *data) +static int print_sha1(const unsigned char sha1[20], void *data) { puts(sha1_to_hex(sha1)); + return 0; } int cmd_main(int argc, const char **argv) diff --git a/t/helper/test-string-list.c b/t/helper/test-string-list.c index 4a68967bd1..c502fa16d3 100644 --- a/t/helper/test-string-list.c +++ b/t/helper/test-string-list.c @@ -97,6 +97,31 @@ int cmd_main(int argc, const char **argv) return 0; } + if (argc == 2 && !strcmp(argv[1], "sort")) { + struct string_list list = STRING_LIST_INIT_NODUP; + struct strbuf sb = STRBUF_INIT; + struct string_list_item *item; + + strbuf_read(&sb, 0, 0); + + /* + * Split by newline, but don't create a string_list item + * for the empty string after the last separator. + */ + if (sb.buf[sb.len - 1] == '\n') + strbuf_setlen(&sb, sb.len - 1); + string_list_split_in_place(&list, sb.buf, '\n', -1); + + string_list_sort(&list); + + for_each_string_list_item(item, &list) + puts(item->string); + + string_list_clear(&list, 0); + strbuf_release(&sb); + return 0; + } + fprintf(stderr, "%s: unknown function name: %s\n", argv[0], argv[1] ? argv[1] : "(there was none)"); return 1; diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf index c3e631394f..69174c6e31 100644 --- a/t/lib-httpd/apache.conf +++ b/t/lib-httpd/apache.conf @@ -123,6 +123,7 @@ ScriptAlias /error/ error.sh/ </Files> RewriteEngine on +RewriteRule ^/dumb-redir/(.*)$ /dumb/$1 [R=301] 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] @@ -132,6 +133,19 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302] RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302] RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302] +# The first rule issues a client-side redirect to something +# that _doesn't_ look like a git repo. The second rule is a +# server-side rewrite, so that it turns out the odd-looking +# thing _is_ a git repo. The "[PT]" tells Apache to match +# the usual ScriptAlias rules for /smart. +RewriteRule ^/insane-redir/(.*)$ /intern-redir/$1/foo [R=301] +RewriteRule ^/intern-redir/(.*)/foo$ /smart/$1 [PT] + +# Serve info/refs internally without redirecting, but +# issue a redirect for any object requests. +RewriteRule ^/redir-objects/(.*/info/refs)$ /dumb/$1 [PT] +RewriteRule ^/redir-objects/(.*/objects/.*)$ /dumb/$1 [R=301] + # Apache 2.2 does not understand <RequireAll>, so we use RewriteCond. # And as RewriteCond does not allow testing for non-matches, we match # the desired case first (one has abra, two has cadabra), and let it diff --git a/t/lib-proto-disable.sh b/t/lib-proto-disable.sh index b0917d93e6..02f49cb409 100644 --- a/t/lib-proto-disable.sh +++ b/t/lib-proto-disable.sh @@ -1,15 +1,12 @@ # Test routines for checking protocol disabling. -# test cloning a particular protocol -# $1 - description of the protocol -# $2 - machine-readable name of the protocol -# $3 - the URL to try cloning -test_proto () { +# Test clone/fetch/push with GIT_ALLOW_PROTOCOL whitelist +test_whitelist () { desc=$1 proto=$2 url=$3 - test_expect_success "clone $1 (enabled)" ' + test_expect_success "clone $desc (enabled)" ' rm -rf tmp.git && ( GIT_ALLOW_PROTOCOL=$proto && @@ -18,7 +15,7 @@ test_proto () { ) ' - test_expect_success "fetch $1 (enabled)" ' + test_expect_success "fetch $desc (enabled)" ' ( cd tmp.git && GIT_ALLOW_PROTOCOL=$proto && @@ -27,7 +24,7 @@ test_proto () { ) ' - test_expect_success "push $1 (enabled)" ' + test_expect_success "push $desc (enabled)" ' ( cd tmp.git && GIT_ALLOW_PROTOCOL=$proto && @@ -36,7 +33,7 @@ test_proto () { ) ' - test_expect_success "push $1 (disabled)" ' + test_expect_success "push $desc (disabled)" ' ( cd tmp.git && GIT_ALLOW_PROTOCOL=none && @@ -45,7 +42,7 @@ test_proto () { ) ' - test_expect_success "fetch $1 (disabled)" ' + test_expect_success "fetch $desc (disabled)" ' ( cd tmp.git && GIT_ALLOW_PROTOCOL=none && @@ -54,7 +51,7 @@ test_proto () { ) ' - test_expect_success "clone $1 (disabled)" ' + test_expect_success "clone $desc (disabled)" ' rm -rf tmp.git && ( GIT_ALLOW_PROTOCOL=none && @@ -62,6 +59,129 @@ test_proto () { test_must_fail git clone --bare "$url" tmp.git ) ' + + test_expect_success "clone $desc (env var has precedence)" ' + rm -rf tmp.git && + ( + GIT_ALLOW_PROTOCOL=none && + export GIT_ALLOW_PROTOCOL && + test_must_fail git -c protocol.allow=always clone --bare "$url" tmp.git && + test_must_fail git -c protocol.$proto.allow=always clone --bare "$url" tmp.git + ) + ' +} + +test_config () { + desc=$1 + proto=$2 + url=$3 + + # Test clone/fetch/push with protocol.<type>.allow config + test_expect_success "clone $desc (enabled with config)" ' + rm -rf tmp.git && + git -c protocol.$proto.allow=always clone --bare "$url" tmp.git + ' + + test_expect_success "fetch $desc (enabled)" ' + git -C tmp.git -c protocol.$proto.allow=always fetch + ' + + test_expect_success "push $desc (enabled)" ' + git -C tmp.git -c protocol.$proto.allow=always push origin HEAD:pushed + ' + + test_expect_success "push $desc (disabled)" ' + test_must_fail git -C tmp.git -c protocol.$proto.allow=never push origin HEAD:pushed + ' + + test_expect_success "fetch $desc (disabled)" ' + test_must_fail git -C tmp.git -c protocol.$proto.allow=never fetch + ' + + test_expect_success "clone $desc (disabled)" ' + rm -rf tmp.git && + test_must_fail git -c protocol.$proto.allow=never clone --bare "$url" tmp.git + ' + + # Test clone/fetch/push with protocol.user.allow and its env var + test_expect_success "clone $desc (enabled)" ' + rm -rf tmp.git && + git -c protocol.$proto.allow=user clone --bare "$url" tmp.git + ' + + test_expect_success "fetch $desc (enabled)" ' + git -C tmp.git -c protocol.$proto.allow=user fetch + ' + + test_expect_success "push $desc (enabled)" ' + git -C tmp.git -c protocol.$proto.allow=user push origin HEAD:pushed + ' + + test_expect_success "push $desc (disabled)" ' + ( + cd tmp.git && + GIT_PROTOCOL_FROM_USER=0 && + export GIT_PROTOCOL_FROM_USER && + test_must_fail git -c protocol.$proto.allow=user push origin HEAD:pushed + ) + ' + + test_expect_success "fetch $desc (disabled)" ' + ( + cd tmp.git && + GIT_PROTOCOL_FROM_USER=0 && + export GIT_PROTOCOL_FROM_USER && + test_must_fail git -c protocol.$proto.allow=user fetch + ) + ' + + test_expect_success "clone $desc (disabled)" ' + rm -rf tmp.git && + ( + GIT_PROTOCOL_FROM_USER=0 && + export GIT_PROTOCOL_FROM_USER && + test_must_fail git -c protocol.$proto.allow=user clone --bare "$url" tmp.git + ) + ' + + # Test clone/fetch/push with protocol.allow user defined default + test_expect_success "clone $desc (enabled)" ' + rm -rf tmp.git && + git config --global protocol.allow always && + git clone --bare "$url" tmp.git + ' + + test_expect_success "fetch $desc (enabled)" ' + git -C tmp.git fetch + ' + + test_expect_success "push $desc (enabled)" ' + git -C tmp.git push origin HEAD:pushed + ' + + test_expect_success "push $desc (disabled)" ' + git config --global protocol.allow never && + test_must_fail git -C tmp.git push origin HEAD:pushed + ' + + test_expect_success "fetch $desc (disabled)" ' + test_must_fail git -C tmp.git fetch + ' + + test_expect_success "clone $desc (disabled)" ' + rm -rf tmp.git && + test_must_fail git clone --bare "$url" tmp.git + ' +} + +# test cloning a particular protocol +# $1 - description of the protocol +# $2 - machine-readable name of the protocol +# $3 - the URL to try cloning +test_proto () { + test_whitelist "$@" + + test_config "$@" } # set up an ssh wrapper that will access $host/$repo in the diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh index 79cdd34a54..915eb4a7c6 100755 --- a/t/lib-submodule-update.sh +++ b/t/lib-submodule-update.sh @@ -69,10 +69,7 @@ create_lib_submodule_repo () { git checkout -b "replace_sub1_with_directory" "add_sub1" && git submodule update && - ( - cd sub1 && - git checkout modifications - ) && + git -C sub1 checkout modifications && git rm --cached sub1 && rm sub1/.git* && git config -f .gitmodules --remove-section "submodule.sub1" && diff --git a/t/perf/p0003-delta-base-cache.sh b/t/perf/p0003-delta-base-cache.sh new file mode 100755 index 0000000000..62369eaaf0 --- /dev/null +++ b/t/perf/p0003-delta-base-cache.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +test_description='Test operations that emphasize the delta base cache. + +We look at both "log --raw", which should put only trees into the delta cache, +and "log -Sfoo --raw", which should look at both trees and blobs. + +Any effects will be emphasized if the test repository is fully packed (loose +objects obviously do not use the delta base cache at all). It is also +emphasized if the pack has long delta chains (e.g., as produced by "gc +--aggressive"), though cache is still quite noticeable even with the default +depth of 50. + +The setting of core.deltaBaseCacheLimit in the source repository is also +relevant (depending on the size of your test repo), so be sure it is consistent +between runs. +' +. ./perf-lib.sh + +test_perf_large_repo + +# puts mostly trees into the delta base cache +test_perf 'log --raw' ' + git log --raw >/dev/null +' + +test_perf 'log -S' ' + git log --raw -Sfoo >/dev/null +' + +test_done diff --git a/t/perf/p0071-sort.sh b/t/perf/p0071-sort.sh new file mode 100755 index 0000000000..7c9a35a646 --- /dev/null +++ b/t/perf/p0071-sort.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +test_description='Basic sort performance tests' +. ./perf-lib.sh + +test_perf_default_repo + +test_expect_success 'setup' ' + git ls-files --stage "*.[ch]" "*.sh" | + cut -f2 -d" " | + git cat-file --batch >unsorted +' + +test_perf 'sort(1)' ' + sort <unsorted >expect +' + +test_perf 'string_list_sort()' ' + test-string-list sort <unsorted >actual +' + +test_expect_success 'string_list_sort() sorts like sort(1)' ' + test_cmp_bin expect actual +' + +test_done diff --git a/t/perf/p5302-pack-index.sh b/t/perf/p5302-pack-index.sh index 5ee9211f98..99bdb16c85 100755 --- a/t/perf/p5302-pack-index.sh +++ b/t/perf/p5302-pack-index.sh @@ -13,6 +13,13 @@ test_expect_success 'repack' ' export PACK ' +test_expect_success 'create target repositories' ' + for repo in t1 t2 t3 t4 t5 t6 + do + git init --bare $repo + done +' + test_perf 'index-pack 0 threads' ' GIT_DIR=t1 git index-pack --threads=1 --stdin < $PACK ' diff --git a/t/perf/p5310-pack-bitmaps.sh b/t/perf/p5310-pack-bitmaps.sh index de2a224a36..bb91dbb173 100755 --- a/t/perf/p5310-pack-bitmaps.sh +++ b/t/perf/p5310-pack-bitmaps.sh @@ -32,6 +32,14 @@ test_perf 'simulated fetch' ' } | git pack-objects --revs --stdout >/dev/null ' +test_perf 'pack to file' ' + git pack-objects --all pack1 </dev/null >/dev/null +' + +test_perf 'pack to file (bitmap)' ' + git pack-objects --use-bitmap-index --all pack1b </dev/null >/dev/null +' + test_expect_success 'create partial bitmap state' ' # pick a commit to represent the repo tip in the past cutoff=$(git rev-list HEAD~100 -1) && @@ -53,8 +61,12 @@ test_expect_success 'create partial bitmap state' ' git update-ref HEAD $orig_tip ' -test_perf 'partial bitmap' ' +test_perf 'clone (partial bitmap)' ' git pack-objects --stdout --all </dev/null >/dev/null ' +test_perf 'pack to file (partial bitmap)' ' + git pack-objects --use-bitmap-index --all pack2b </dev/null >/dev/null +' + test_done diff --git a/t/t0001-init.sh b/t/t0001-init.sh index a6fdd5ef3a..e424de5363 100755 --- a/t/t0001-init.sh +++ b/t/t0001-init.sh @@ -258,6 +258,9 @@ test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shar ( # Leading directories should honor umask while # the repository itself should follow "shared" + mkdir newdir && + # Remove a default ACL if possible. + (setfacl -k newdir 2>/dev/null || true) && umask 002 && git init --bare --shared=0660 newdir/a/b/c && test_path_is_dir newdir/a/b/c/refs && @@ -384,4 +387,30 @@ test_expect_success MINGW 'bare git dir not hidden' ' ! is_hidden newdir ' +test_expect_success 'remote init from does not use config from cwd' ' + rm -rf newdir && + test_config core.logallrefupdates true && + git init newdir && + echo true >expect && + git -C newdir config --bool core.logallrefupdates >actual && + test_cmp expect actual +' + +test_expect_success 're-init from a linked worktree' ' + git init main-worktree && + ( + cd main-worktree && + test_commit first && + git worktree add ../linked-worktree && + mv .git/info/exclude expected-exclude && + cp .git/config expected-config && + find .git/worktrees -print | sort >expected && + git -C ../linked-worktree init && + test_cmp expected-exclude .git/info/exclude && + test_cmp expected-config .git/config && + find .git/worktrees -print | sort >actual && + test_cmp expected actual + ) +' + test_done diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh index f0fbb42554..f19ae4f8cc 100755 --- a/t/t0003-attributes.sh +++ b/t/t0003-attributes.sh @@ -13,10 +13,31 @@ attr_check () { test_line_count = 0 err } +attr_check_quote () { + + path="$1" + quoted_path="$2" + expect="$3" + + git check-attr test -- "$path" >actual && + echo "\"$quoted_path\": test: $expect" >expect && + test_cmp expect actual + +} + +test_expect_success 'open-quoted pathname' ' + echo "\"a test=a" >.gitattributes && + test_must_fail attr_check a a +' + + test_expect_success 'setup' ' mkdir -p a/b/d a/c b && ( echo "[attr]notest !test" + echo "\" d \" test=d" + echo " e test=e" + echo " e\" test=e" echo "f test=f" echo "a/i test=a/i" echo "onoff test -test" @@ -69,6 +90,11 @@ test_expect_success 'command line checks' ' ' test_expect_success 'attribute test' ' + + attr_check " d " d && + attr_check e e && + attr_check_quote e\" e\\\" e && + attr_check f f && attr_check a/f f && attr_check a/c/f f && diff --git a/t/t0012-help.sh b/t/t0012-help.sh new file mode 100755 index 0000000000..8faba2e8bc --- /dev/null +++ b/t/t0012-help.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +test_description='help' + +. ./test-lib.sh + +configure_help () { + test_config help.format html && + + # Unless the path has "://" in it, Git tries to make sure + # the documentation directory locally exists. Avoid it as + # we are only interested in seeing an attempt to correctly + # invoke a help browser in this test. + test_config help.htmlpath test://html && + + # Name a custom browser + test_config browser.test.cmd ./test-browser && + test_config help.browser test +} + +test_expect_success "setup" ' + # Just write out which page gets requested + write_script test-browser <<-\EOF + echo "$*" >test-browser.log + EOF +' + +test_expect_success "works for commands and guides by default" ' + configure_help && + git help status && + echo "test://html/git-status.html" >expect && + test_cmp expect test-browser.log && + git help revisions && + echo "test://html/gitrevisions.html" >expect && + test_cmp expect test-browser.log +' + +test_expect_success "--exclude-guides does not work for guides" ' + >test-browser.log && + test_must_fail git help --exclude-guides revisions && + test_must_be_empty test-browser.log +' + +test_expect_success "--help does not work for guides" " + cat <<-EOF >expect && + git: 'revisions' is not a git command. See 'git --help'. + EOF + test_must_fail git revisions --help 2>actual && + test_i18ncmp expect actual +" + +test_done diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh index f94120a894..71350e0657 100755 --- a/t/t0020-crlf.sh +++ b/t/t0020-crlf.sh @@ -83,7 +83,11 @@ test_expect_success 'safecrlf: print warning only once' ' git add doublewarn && git commit -m "nowarn" && for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn && - test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1 + git add doublewarn 2>err && + if test_have_prereq C_LOCALE_OUTPUT + then + test $(grep "CRLF will be replaced by LF" err | wc -l) = 1 + fi ' diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index e799e59544..161f560446 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -4,13 +4,72 @@ test_description='blob conversion via gitattributes' . ./test-lib.sh -cat <<EOF >rot13.sh -#!$SHELL_PATH +TEST_ROOT="$PWD" +PATH=$TEST_ROOT:$PATH + +write_script <<\EOF "$TEST_ROOT/rot13.sh" tr \ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \ 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM' EOF -chmod +x rot13.sh + +write_script rot13-filter.pl "$PERL_PATH" \ + <"$TEST_DIRECTORY"/t0021/rot13-filter.pl + +generate_random_characters () { + LEN=$1 + NAME=$2 + test-genrandom some-seed $LEN | + perl -pe "s/./chr((ord($&) % 26) + ord('a'))/sge" >"$TEST_ROOT/$NAME" +} + +file_size () { + perl -e 'print -s $ARGV[0]' "$1" +} + +filter_git () { + rm -f rot13-filter.log && + git "$@" +} + +# Compare two files and ensure that `clean` and `smudge` respectively are +# called at least once if specified in the `expect` file. The actual +# invocation count is not relevant because their number can vary. +# c.f. http://public-inbox.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/ +test_cmp_count () { + expect=$1 + actual=$2 + for FILE in "$expect" "$actual" + do + sort "$FILE" | uniq -c | + sed -e "s/^ *[0-9][0-9]*[ ]*IN: /x IN: /" >"$FILE.tmp" && + mv "$FILE.tmp" "$FILE" || return + done && + test_cmp "$expect" "$actual" +} + +# Compare two files but exclude all `clean` invocations because Git can +# call `clean` zero or more times. +# c.f. http://public-inbox.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/ +test_cmp_exclude_clean () { + expect=$1 + actual=$2 + for FILE in "$expect" "$actual" + do + grep -v "IN: clean" "$FILE" >"$FILE.tmp" && + mv "$FILE.tmp" "$FILE" + done && + test_cmp "$expect" "$actual" +} + +# Check that the contents of two files are equal and that their rot13 version +# is equal to the committed content. +test_cmp_committed_rot13 () { + test_cmp "$1" "$2" && + rot13.sh <"$1" >expected && + git cat-file blob :"$2" >actual && + test_cmp expected actual +} test_expect_success setup ' git config filter.rot13.smudge ./rot13.sh && @@ -31,15 +90,18 @@ test_expect_success setup ' cat test >test.i && git add test test.t test.i && rm -f test test.t test.i && - git checkout -- test test.t test.i + git checkout -- test test.t test.i && + + echo "content-test2" >test2.o && + echo "content-test3 - filename with special characters" >"test3 '\''sq'\'',\$x=.o" ' script='s/^\$Id: \([0-9a-f]*\) \$/\1/p' test_expect_success check ' - cmp test.o test && - cmp test.o test.t && + test_cmp test.o test && + test_cmp test.o test.t && # ident should be stripped in the repository git diff --raw --exit-code :test :test.i && @@ -47,10 +109,10 @@ test_expect_success check ' embedded=$(sed -ne "$script" test.i) && test "z$id" = "z$embedded" && - git cat-file blob :test.t > test.r && + git cat-file blob :test.t >test.r && - ./rot13.sh < test.o > test.t && - cmp test.r test.t + ./rot13.sh <test.o >test.t && + test_cmp test.r test.t ' # If an expanded ident ever gets into the repository, we want to make sure that @@ -130,7 +192,7 @@ test_expect_success 'filter shell-escaped filenames' ' # delete the files and check them out again, using a smudge filter # that will count the args and echo the command-line back to us - git config filter.argc.smudge "sh ./argc.sh %f" && + test_config filter.argc.smudge "sh ./argc.sh %f" && rm "$normal" "$special" && git checkout -- "$normal" "$special" && @@ -141,7 +203,7 @@ test_expect_success 'filter shell-escaped filenames' ' test_cmp expect "$special" && # do the same thing, but with more args in the filter expression - git config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" && + test_config filter.argc.smudge "sh ./argc.sh %f --my-extra-arg" && rm "$normal" "$special" && git checkout -- "$normal" "$special" && @@ -154,9 +216,9 @@ test_expect_success 'filter shell-escaped filenames' ' ' test_expect_success 'required filter should filter data' ' - git config filter.required.smudge ./rot13.sh && - git config filter.required.clean ./rot13.sh && - git config filter.required.required true && + test_config filter.required.smudge ./rot13.sh && + test_config filter.required.clean ./rot13.sh && + test_config filter.required.required true && echo "*.r filter=required" >.gitattributes && @@ -165,17 +227,17 @@ test_expect_success 'required filter should filter data' ' rm -f test.r && git checkout -- test.r && - cmp test.o test.r && + test_cmp test.o test.r && ./rot13.sh <test.o >expected && git cat-file blob :test.r >actual && - cmp expected actual + test_cmp expected actual ' 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 && + test_config filter.failsmudge.smudge false && + test_config filter.failsmudge.clean cat && + test_config filter.failsmudge.required true && echo "*.fs filter=failsmudge" >.gitattributes && @@ -186,9 +248,9 @@ test_expect_success 'required filter smudge failure' ' ' 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 && + test_config filter.failclean.smudge cat && + test_config filter.failclean.clean false && + test_config filter.failclean.required true && echo "*.fc filter=failclean" >.gitattributes && @@ -197,8 +259,8 @@ test_expect_success 'required filter clean failure' ' ' test_expect_success 'filtering large input to small output should use little memory' ' - git config filter.devnull.clean "cat >/dev/null" && - git config filter.devnull.required true && + test_config filter.devnull.clean "cat >/dev/null" && + test_config filter.devnull.required true && for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB && echo "30MB filter=devnull" >.gitattributes && GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB @@ -207,7 +269,7 @@ test_expect_success 'filtering large input to small output should use little mem test_expect_success 'filter that does not read is fine' ' test-genrandom foo $((128 * 1024 + 1)) >big && echo "big filter=epipe" >.gitattributes && - git config filter.epipe.clean "echo xyzzy" && + test_config filter.epipe.clean "echo xyzzy" && git add big && git cat-file blob :big >actual && echo xyzzy >expect && @@ -215,20 +277,20 @@ test_expect_success 'filter that does not read is fine' ' ' test_expect_success EXPENSIVE 'filter large file' ' - git config filter.largefile.smudge cat && - git config filter.largefile.clean cat && + test_config filter.largefile.smudge cat && + test_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 && + test_must_be_empty err && rm -f 2GB && git checkout -- 2GB 2>err && - ! test -s err + test_must_be_empty err ' test_expect_success "filter: clean empty file" ' - git config filter.in-repo-header.clean "echo cleaned && cat" && - git config filter.in-repo-header.smudge "sed 1d" && + test_config filter.in-repo-header.clean "echo cleaned && cat" && + test_config filter.in-repo-header.smudge "sed 1d" && echo "empty-in-worktree filter=in-repo-header" >>.gitattributes && >empty-in-worktree && @@ -240,8 +302,8 @@ test_expect_success "filter: clean empty file" ' ' test_expect_success "filter: smudge empty file" ' - git config filter.empty-in-repo.clean "cat >/dev/null" && - git config filter.empty-in-repo.smudge "echo smudged && cat" && + test_config filter.empty-in-repo.clean "cat >/dev/null" && + test_config filter.empty-in-repo.smudge "echo smudged && cat" && echo "empty-in-repo filter=empty-in-repo" >>.gitattributes && echo dead data walking >empty-in-repo && @@ -279,4 +341,364 @@ test_expect_success 'diff does not reuse worktree files that need cleaning' ' test_line_count = 0 count ' +test_expect_success PERL 'required process filter should filter data' ' + test_config_global filter.protocol.process "rot13-filter.pl clean smudge" && + test_config_global filter.protocol.required true && + rm -rf repo && + mkdir repo && + ( + cd repo && + git init && + + echo "*.r filter=protocol" >.gitattributes && + git add . && + git commit -m "test commit 1" && + git branch empty-branch && + + cp "$TEST_ROOT/test.o" test.r && + cp "$TEST_ROOT/test2.o" test2.r && + mkdir testsubdir && + cp "$TEST_ROOT/test3 '\''sq'\'',\$x=.o" "testsubdir/test3 '\''sq'\'',\$x=.r" && + >test4-empty.r && + + S=$(file_size test.r) && + S2=$(file_size test2.r) && + S3=$(file_size "testsubdir/test3 '\''sq'\'',\$x=.r") && + + filter_git add . && + cat >expected.log <<-EOF && + START + init handshake complete + IN: clean test.r $S [OK] -- OUT: $S . [OK] + IN: clean test2.r $S2 [OK] -- OUT: $S2 . [OK] + IN: clean test4-empty.r 0 [OK] -- OUT: 0 [OK] + IN: clean testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK] + STOP + EOF + test_cmp_count expected.log rot13-filter.log && + + git commit -m "test commit 2" && + rm -f test2.r "testsubdir/test3 '\''sq'\'',\$x=.r" && + + filter_git checkout --quiet --no-progress . && + cat >expected.log <<-EOF && + START + init handshake complete + IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK] + IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK] + STOP + EOF + test_cmp_exclude_clean expected.log rot13-filter.log && + + filter_git checkout --quiet --no-progress empty-branch && + cat >expected.log <<-EOF && + START + init handshake complete + IN: clean test.r $S [OK] -- OUT: $S . [OK] + STOP + EOF + test_cmp_exclude_clean expected.log rot13-filter.log && + + filter_git checkout --quiet --no-progress master && + cat >expected.log <<-EOF && + START + init handshake complete + IN: smudge test.r $S [OK] -- OUT: $S . [OK] + IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK] + IN: smudge test4-empty.r 0 [OK] -- OUT: 0 [OK] + IN: smudge testsubdir/test3 '\''sq'\'',\$x=.r $S3 [OK] -- OUT: $S3 . [OK] + STOP + EOF + test_cmp_exclude_clean expected.log rot13-filter.log && + + test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r && + test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r && + test_cmp_committed_rot13 "$TEST_ROOT/test3 '\''sq'\'',\$x=.o" "testsubdir/test3 '\''sq'\'',\$x=.r" + ) +' + +test_expect_success PERL 'required process filter takes precedence' ' + test_config_global filter.protocol.clean false && + test_config_global filter.protocol.process "rot13-filter.pl clean" && + test_config_global filter.protocol.required true && + rm -rf repo && + mkdir repo && + ( + cd repo && + git init && + + echo "*.r filter=protocol" >.gitattributes && + cp "$TEST_ROOT/test.o" test.r && + S=$(file_size test.r) && + + # Check that the process filter is invoked here + filter_git add . && + cat >expected.log <<-EOF && + START + init handshake complete + IN: clean test.r $S [OK] -- OUT: $S . [OK] + STOP + EOF + test_cmp_count expected.log rot13-filter.log + ) +' + +test_expect_success PERL 'required process filter should be used only for "clean" operation only' ' + test_config_global filter.protocol.process "rot13-filter.pl clean" && + rm -rf repo && + mkdir repo && + ( + cd repo && + git init && + + echo "*.r filter=protocol" >.gitattributes && + cp "$TEST_ROOT/test.o" test.r && + S=$(file_size test.r) && + + filter_git add . && + cat >expected.log <<-EOF && + START + init handshake complete + IN: clean test.r $S [OK] -- OUT: $S . [OK] + STOP + EOF + test_cmp_count expected.log rot13-filter.log && + + rm test.r && + + filter_git checkout --quiet --no-progress . && + # If the filter would be used for "smudge", too, we would see + # "IN: smudge test.r 57 [OK] -- OUT: 57 . [OK]" here + cat >expected.log <<-EOF && + START + init handshake complete + STOP + EOF + test_cmp_exclude_clean expected.log rot13-filter.log + ) +' + +test_expect_success PERL 'required process filter should process multiple packets' ' + test_config_global filter.protocol.process "rot13-filter.pl clean smudge" && + test_config_global filter.protocol.required true && + + rm -rf repo && + mkdir repo && + ( + cd repo && + git init && + + # Generate data requiring 1, 2, 3 packets + S=65516 && # PKTLINE_DATA_MAXLEN -> Maximal size of a packet + generate_random_characters $(($S )) 1pkt_1__.file && + generate_random_characters $(($S +1)) 2pkt_1+1.file && + generate_random_characters $(($S*2-1)) 2pkt_2-1.file && + generate_random_characters $(($S*2 )) 2pkt_2__.file && + generate_random_characters $(($S*2+1)) 3pkt_2+1.file && + + for FILE in "$TEST_ROOT"/*.file + do + cp "$FILE" . && + rot13.sh <"$FILE" >"$FILE.rot13" + done && + + echo "*.file filter=protocol" >.gitattributes && + filter_git add *.file .gitattributes && + cat >expected.log <<-EOF && + START + init handshake complete + IN: clean 1pkt_1__.file $(($S )) [OK] -- OUT: $(($S )) . [OK] + IN: clean 2pkt_1+1.file $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK] + IN: clean 2pkt_2-1.file $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK] + IN: clean 2pkt_2__.file $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK] + IN: clean 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK] + STOP + EOF + test_cmp_count expected.log rot13-filter.log && + + rm -f *.file && + + filter_git checkout --quiet --no-progress -- *.file && + cat >expected.log <<-EOF && + START + init handshake complete + IN: smudge 1pkt_1__.file $(($S )) [OK] -- OUT: $(($S )) . [OK] + IN: smudge 2pkt_1+1.file $(($S +1)) [OK] -- OUT: $(($S +1)) .. [OK] + IN: smudge 2pkt_2-1.file $(($S*2-1)) [OK] -- OUT: $(($S*2-1)) .. [OK] + IN: smudge 2pkt_2__.file $(($S*2 )) [OK] -- OUT: $(($S*2 )) .. [OK] + IN: smudge 3pkt_2+1.file $(($S*2+1)) [OK] -- OUT: $(($S*2+1)) ... [OK] + STOP + EOF + test_cmp_exclude_clean expected.log rot13-filter.log && + + for FILE in *.file + do + test_cmp_committed_rot13 "$TEST_ROOT/$FILE" $FILE + done + ) +' + +test_expect_success PERL 'required process filter with clean error should fail' ' + test_config_global filter.protocol.process "rot13-filter.pl clean smudge" && + test_config_global filter.protocol.required true && + rm -rf repo && + mkdir repo && + ( + cd repo && + git init && + + echo "*.r filter=protocol" >.gitattributes && + + cp "$TEST_ROOT/test.o" test.r && + echo "this is going to fail" >clean-write-fail.r && + echo "content-test3-subdir" >test3.r && + + test_must_fail git add . + ) +' + +test_expect_success PERL 'process filter should restart after unexpected write failure' ' + test_config_global filter.protocol.process "rot13-filter.pl clean smudge" && + rm -rf repo && + mkdir repo && + ( + cd repo && + git init && + + echo "*.r filter=protocol" >.gitattributes && + + cp "$TEST_ROOT/test.o" test.r && + cp "$TEST_ROOT/test2.o" test2.r && + echo "this is going to fail" >smudge-write-fail.o && + cp smudge-write-fail.o smudge-write-fail.r && + + S=$(file_size test.r) && + S2=$(file_size test2.r) && + SF=$(file_size smudge-write-fail.r) && + + git add . && + rm -f *.r && + + rm -f rot13-filter.log && + git checkout --quiet --no-progress . 2>git-stderr.log && + + grep "smudge write error at" git-stderr.log && + grep "error: external filter" git-stderr.log && + + cat >expected.log <<-EOF && + START + init handshake complete + IN: smudge smudge-write-fail.r $SF [OK] -- OUT: $SF [WRITE FAIL] + START + init handshake complete + IN: smudge test.r $S [OK] -- OUT: $S . [OK] + IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK] + STOP + EOF + test_cmp_exclude_clean expected.log rot13-filter.log && + + test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r && + test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r && + + # Smudge failed + ! test_cmp smudge-write-fail.o smudge-write-fail.r && + rot13.sh <smudge-write-fail.o >expected && + git cat-file blob :smudge-write-fail.r >actual && + test_cmp expected actual + ) +' + +test_expect_success PERL 'process filter should not be restarted if it signals an error' ' + test_config_global filter.protocol.process "rot13-filter.pl clean smudge" && + rm -rf repo && + mkdir repo && + ( + cd repo && + git init && + + echo "*.r filter=protocol" >.gitattributes && + + cp "$TEST_ROOT/test.o" test.r && + cp "$TEST_ROOT/test2.o" test2.r && + echo "this will cause an error" >error.o && + cp error.o error.r && + + S=$(file_size test.r) && + S2=$(file_size test2.r) && + SE=$(file_size error.r) && + + git add . && + rm -f *.r && + + filter_git checkout --quiet --no-progress . && + cat >expected.log <<-EOF && + START + init handshake complete + IN: smudge error.r $SE [OK] -- OUT: 0 [ERROR] + IN: smudge test.r $S [OK] -- OUT: $S . [OK] + IN: smudge test2.r $S2 [OK] -- OUT: $S2 . [OK] + STOP + EOF + test_cmp_exclude_clean expected.log rot13-filter.log && + + test_cmp_committed_rot13 "$TEST_ROOT/test.o" test.r && + test_cmp_committed_rot13 "$TEST_ROOT/test2.o" test2.r && + test_cmp error.o error.r + ) +' + +test_expect_success PERL 'process filter abort stops processing of all further files' ' + test_config_global filter.protocol.process "rot13-filter.pl clean smudge" && + rm -rf repo && + mkdir repo && + ( + cd repo && + git init && + + echo "*.r filter=protocol" >.gitattributes && + + cp "$TEST_ROOT/test.o" test.r && + cp "$TEST_ROOT/test2.o" test2.r && + echo "error this blob and all future blobs" >abort.o && + cp abort.o abort.r && + + SA=$(file_size abort.r) && + + git add . && + rm -f *.r && + + # Note: This test assumes that Git filters files in alphabetical + # order ("abort.r" before "test.r"). + filter_git checkout --quiet --no-progress . && + cat >expected.log <<-EOF && + START + init handshake complete + IN: smudge abort.r $SA [OK] -- OUT: 0 [ABORT] + STOP + EOF + test_cmp_exclude_clean expected.log rot13-filter.log && + + test_cmp "$TEST_ROOT/test.o" test.r && + test_cmp "$TEST_ROOT/test2.o" test2.r && + test_cmp abort.o abort.r + ) +' + +test_expect_success PERL 'invalid process filter must fail (and not hang!)' ' + test_config_global filter.protocol.process cat && + test_config_global filter.protocol.required true && + rm -rf repo && + mkdir repo && + ( + cd repo && + git init && + + echo "*.r filter=protocol" >.gitattributes && + + cp "$TEST_ROOT/test.o" test.r && + test_must_fail git add . 2>git-stderr.log && + grep "does not support filter protocol version" git-stderr.log + ) +' + test_done diff --git a/t/t0021/rot13-filter.pl b/t/t0021/rot13-filter.pl new file mode 100644 index 0000000000..617f581e56 --- /dev/null +++ b/t/t0021/rot13-filter.pl @@ -0,0 +1,196 @@ +# +# Example implementation for the Git filter protocol version 2 +# See Documentation/gitattributes.txt, section "Filter Protocol" +# +# The script takes the list of supported protocol capabilities as +# arguments ("clean", "smudge", etc). +# +# This implementation supports special test cases: +# (1) If data with the pathname "clean-write-fail.r" is processed with +# a "clean" operation then the write operation will die. +# (2) If data with the pathname "smudge-write-fail.r" is processed with +# a "smudge" operation then the write operation will die. +# (3) If data with the pathname "error.r" is processed with any +# operation then the filter signals that it cannot or does not want +# to process the file. +# (4) If data with the pathname "abort.r" is processed with any +# operation then the filter signals that it cannot or does not want +# to process the file and any file after that is processed with the +# same command. +# + +use strict; +use warnings; +use IO::File; + +my $MAX_PACKET_CONTENT_SIZE = 65516; +my @capabilities = @ARGV; + +open my $debug, ">>", "rot13-filter.log" or die "cannot open log file: $!"; + +sub rot13 { + my $str = shift; + $str =~ y/A-Za-z/N-ZA-Mn-za-m/; + return $str; +} + +sub packet_bin_read { + my $buffer; + my $bytes_read = read STDIN, $buffer, 4; + if ( $bytes_read == 0 ) { + # EOF - Git stopped talking to us! + print $debug "STOP\n"; + exit(); + } + elsif ( $bytes_read != 4 ) { + die "invalid packet: '$buffer'"; + } + my $pkt_size = hex($buffer); + if ( $pkt_size == 0 ) { + return ( 1, "" ); + } + elsif ( $pkt_size > 4 ) { + my $content_size = $pkt_size - 4; + $bytes_read = read STDIN, $buffer, $content_size; + if ( $bytes_read != $content_size ) { + die "invalid packet ($content_size bytes expected; $bytes_read bytes read)"; + } + return ( 0, $buffer ); + } + else { + die "invalid packet size: $pkt_size"; + } +} + +sub packet_txt_read { + my ( $res, $buf ) = packet_bin_read(); + unless ( $buf =~ s/\n$// ) { + die "A non-binary line MUST be terminated by an LF."; + } + return ( $res, $buf ); +} + +sub packet_bin_write { + my $buf = shift; + print STDOUT sprintf( "%04x", length($buf) + 4 ); + print STDOUT $buf; + STDOUT->flush(); +} + +sub packet_txt_write { + packet_bin_write( $_[0] . "\n" ); +} + +sub packet_flush { + print STDOUT sprintf( "%04x", 0 ); + STDOUT->flush(); +} + +print $debug "START\n"; +$debug->flush(); + +( packet_txt_read() eq ( 0, "git-filter-client" ) ) || die "bad initialize"; +( packet_txt_read() eq ( 0, "version=2" ) ) || die "bad version"; +( packet_bin_read() eq ( 1, "" ) ) || die "bad version end"; + +packet_txt_write("git-filter-server"); +packet_txt_write("version=2"); +packet_flush(); + +( packet_txt_read() eq ( 0, "capability=clean" ) ) || die "bad capability"; +( packet_txt_read() eq ( 0, "capability=smudge" ) ) || die "bad capability"; +( packet_bin_read() eq ( 1, "" ) ) || die "bad capability end"; + +foreach (@capabilities) { + packet_txt_write( "capability=" . $_ ); +} +packet_flush(); +print $debug "init handshake complete\n"; +$debug->flush(); + +while (1) { + my ($command) = packet_txt_read() =~ /^command=(.+)$/; + print $debug "IN: $command"; + $debug->flush(); + + my ($pathname) = packet_txt_read() =~ /^pathname=(.+)$/; + print $debug " $pathname"; + $debug->flush(); + + if ( $pathname eq "" ) { + die "bad pathname '$pathname'"; + } + + # Flush + packet_bin_read(); + + my $input = ""; + { + binmode(STDIN); + my $buffer; + my $done = 0; + while ( !$done ) { + ( $done, $buffer ) = packet_bin_read(); + $input .= $buffer; + } + print $debug " " . length($input) . " [OK] -- "; + $debug->flush(); + } + + my $output; + if ( $pathname eq "error.r" or $pathname eq "abort.r" ) { + $output = ""; + } + elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) { + $output = rot13($input); + } + elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) { + $output = rot13($input); + } + else { + die "bad command '$command'"; + } + + print $debug "OUT: " . length($output) . " "; + $debug->flush(); + + if ( $pathname eq "error.r" ) { + print $debug "[ERROR]\n"; + $debug->flush(); + packet_txt_write("status=error"); + packet_flush(); + } + elsif ( $pathname eq "abort.r" ) { + print $debug "[ABORT]\n"; + $debug->flush(); + packet_txt_write("status=abort"); + packet_flush(); + } + else { + packet_txt_write("status=success"); + packet_flush(); + + if ( $pathname eq "${command}-write-fail.r" ) { + print $debug "[WRITE FAIL]\n"; + $debug->flush(); + die "${command} write error"; + } + + while ( length($output) > 0 ) { + my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE ); + packet_bin_write($packet); + # dots represent the number of packets + print $debug "."; + if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) { + $output = substr( $output, $MAX_PACKET_CONTENT_SIZE ); + } + else { + $output = ""; + } + } + packet_flush(); + print $debug " [OK]\n"; + $debug->flush(); + packet_flush(); + } +} diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh index 29e91d861c..bbf3e39e3d 100755 --- a/t/t0030-stripspace.sh +++ b/t/t0030-stripspace.sh @@ -432,6 +432,15 @@ test_expect_success '-c with changed comment char' ' test_cmp expect actual ' +test_expect_success '-c with comment char defined in .git/config' ' + test_config core.commentchar = && + printf "= foo\n" >expect && + printf "foo" | ( + mkdir sub && cd sub && git stripspace -c + ) >actual && + test_cmp expect actual +' + test_expect_success 'avoid SP-HT sequence in commented line' ' printf "#\tone\n#\n# two\n" >expect && printf "\tone\n\ntwo\n" | git stripspace -c >actual && diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index bf2deee109..444b5a4df8 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -305,8 +305,9 @@ test_git_path GIT_COMMON_DIR=bar config bar/config test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs test_git_path GIT_COMMON_DIR=bar shallow bar/shallow -# In the tests below, the distinction between $PWD and $(pwd) is important: -# on Windows, $PWD is POSIX style (/c/foo), $(pwd) has drive letter (c:/foo). +# In the tests below, $(pwd) must be used because it is a native path on +# Windows and avoids MSYS's path mangling (which simplifies "foo/../bar" and +# strips the dot from trailing "/."). test_submodule_relative_url "../" "../foo" "../submodule" "../../submodule" test_submodule_relative_url "../" "../foo/bar" "../submodule" "../../foo/submodule" @@ -314,27 +315,29 @@ test_submodule_relative_url "../" "../foo/submodule" "../submodule" "../../foo/s test_submodule_relative_url "../" "./foo" "../submodule" "../submodule" test_submodule_relative_url "../" "./foo/bar" "../submodule" "../foo/submodule" test_submodule_relative_url "../../../" "../foo/bar" "../sub/a/b/c" "../../../../foo/sub/a/b/c" -test_submodule_relative_url "../" "$PWD/addtest" "../repo" "$(pwd)/repo" +test_submodule_relative_url "../" "$(pwd)/addtest" "../repo" "$(pwd)/repo" test_submodule_relative_url "../" "foo/bar" "../submodule" "../foo/submodule" test_submodule_relative_url "../" "foo" "../submodule" "../submodule" test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c" "../foo/sub/a/b/c" +test_submodule_relative_url "(null)" "../foo/bar" "../sub/a/b/c/" "../foo/sub/a/b/c" +test_submodule_relative_url "(null)" "../foo/bar/" "../sub/a/b/c" "../foo/sub/a/b/c" test_submodule_relative_url "(null)" "../foo/bar" "../submodule" "../foo/submodule" test_submodule_relative_url "(null)" "../foo/submodule" "../submodule" "../foo/submodule" test_submodule_relative_url "(null)" "../foo" "../submodule" "../submodule" test_submodule_relative_url "(null)" "./foo/bar" "../submodule" "foo/submodule" test_submodule_relative_url "(null)" "./foo" "../submodule" "submodule" test_submodule_relative_url "(null)" "//somewhere else/repo" "../subrepo" "//somewhere else/subrepo" -test_submodule_relative_url "(null)" "$PWD/subsuper_update_r" "../subsubsuper_update_r" "$(pwd)/subsubsuper_update_r" -test_submodule_relative_url "(null)" "$PWD/super_update_r2" "../subsuper_update_r" "$(pwd)/subsuper_update_r" -test_submodule_relative_url "(null)" "$PWD/." "../." "$(pwd)/." -test_submodule_relative_url "(null)" "$PWD" "./." "$(pwd)/." -test_submodule_relative_url "(null)" "$PWD/addtest" "../repo" "$(pwd)/repo" -test_submodule_relative_url "(null)" "$PWD" "./å äö" "$(pwd)/å äö" -test_submodule_relative_url "(null)" "$PWD/." "../submodule" "$(pwd)/submodule" -test_submodule_relative_url "(null)" "$PWD/submodule" "../submodule" "$(pwd)/submodule" -test_submodule_relative_url "(null)" "$PWD/home2/../remote" "../bundle1" "$(pwd)/home2/../bundle1" -test_submodule_relative_url "(null)" "$PWD/submodule_update_repo" "./." "$(pwd)/submodule_update_repo/." +test_submodule_relative_url "(null)" "$(pwd)/subsuper_update_r" "../subsubsuper_update_r" "$(pwd)/subsubsuper_update_r" +test_submodule_relative_url "(null)" "$(pwd)/super_update_r2" "../subsuper_update_r" "$(pwd)/subsuper_update_r" +test_submodule_relative_url "(null)" "$(pwd)/." "../." "$(pwd)/." +test_submodule_relative_url "(null)" "$(pwd)" "./." "$(pwd)/." +test_submodule_relative_url "(null)" "$(pwd)/addtest" "../repo" "$(pwd)/repo" +test_submodule_relative_url "(null)" "$(pwd)" "./å äö" "$(pwd)/å äö" +test_submodule_relative_url "(null)" "$(pwd)/." "../submodule" "$(pwd)/submodule" +test_submodule_relative_url "(null)" "$(pwd)/submodule" "../submodule" "$(pwd)/submodule" +test_submodule_relative_url "(null)" "$(pwd)/home2/../remote" "../bundle1" "$(pwd)/home2/../bundle1" +test_submodule_relative_url "(null)" "$(pwd)/submodule_update_repo" "./." "$(pwd)/submodule_update_repo/." test_submodule_relative_url "(null)" "file:///tmp/repo" "../subrepo" "file:///tmp/subrepo" test_submodule_relative_url "(null)" "foo/bar" "../submodule" "foo/submodule" test_submodule_relative_url "(null)" "foo" "../submodule" "submodule" diff --git a/t/t0202/test.pl b/t/t0202/test.pl index 2c10cb4693..2cbf7b9590 100755 --- a/t/t0202/test.pl +++ b/t/t0202/test.pl @@ -4,7 +4,7 @@ use lib (split(/:/, $ENV{GITPERLLIB})); use strict; use warnings; use POSIX qw(:locale_h); -use Test::More tests => 8; +use Test::More tests => 13; use Git::I18N; my $has_gettext_library = $Git::I18N::__HAS_LIBRARY; @@ -31,6 +31,8 @@ is_deeply(\@Git::I18N::EXPORT, \@Git::I18N::EXPORT_OK, "sanity: Git::I18N export # more gettext wrapper functions. my %prototypes = (qw( __ $ + __n $$$ + N__ $ )); while (my ($sub, $proto) = each %prototypes) { is(prototype(\&{"Git::I18N::$sub"}), $proto, "sanity: $sub has a $proto prototype"); @@ -46,6 +48,16 @@ is_deeply(\@Git::I18N::EXPORT, \@Git::I18N::EXPORT_OK, "sanity: Git::I18N export my ($got, $expect) = (('TEST: A Perl test string') x 2); is(__($got), $expect, "Passing a string through __() in the C locale works"); + + my ($got_singular, $got_plural, $expect_singular, $expect_plural) = + (('TEST: 1 file', 'TEST: n files') x 2); + + is(__n($got_singular, $got_plural, 1), $expect_singular, + "Get singular string through __n() in C locale"); + is(__n($got_singular, $got_plural, 2), $expect_plural, + "Get plural string through __n() in C locale"); + + is(N__($got), $expect, "Passing a string through N__() in the C locale works"); } # Test a basic message on different locales diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh index a0b79b4839..3c4d2d6045 100755 --- a/t/t1000-read-tree-m-3way.sh +++ b/t/t1000-read-tree-m-3way.sh @@ -128,29 +128,29 @@ cat >expected <<\EOF EOF check_result () { - git ls-files --stage | sed -e 's/ '"$_x40"' / X /' >current && - test_cmp expected current + git ls-files --stage | sed -e 's/ '"$_x40"' / X /' >current && + test_cmp expected current } # This is done on an empty work directory, which is the normal # merge person behaviour. -test_expect_success \ - '3-way merge with git read-tree -m, empty cache' \ - "rm -fr [NDMALTS][NDMALTSF] Z && - rm .git/index && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" +test_expect_success '3-way merge with git read-tree -m, empty cache' ' + rm -fr [NDMALTS][NDMALTSF] Z && + rm .git/index && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' # This starts out with the first head, which is the normal # patch submitter behaviour. -test_expect_success \ - '3-way merge with git read-tree -m, match H' \ - "rm -fr [NDMALTS][NDMALTSF] Z && - rm .git/index && - read_tree_must_succeed $tree_A && - git checkout-index -f -u -a && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" +test_expect_success '3-way merge with git read-tree -m, match H' ' + rm -fr [NDMALTS][NDMALTSF] Z && + rm .git/index && + read_tree_must_succeed $tree_A && + git checkout-index -f -u -a && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' : <<\END_OF_CASE_TABLE @@ -208,322 +208,304 @@ DF (file) when tree B require DF to be a directory by having DF/DF END_OF_CASE_TABLE -test_expect_success '1 - must not have an entry not in A.' " - rm -f .git/index XX && - echo XX >XX && - git update-index --add XX && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '2 - must match B in !O && !A && B case.' \ - "rm -f .git/index NA && - cp .orig-B/NA NA && - git update-index --add NA && - read_tree_must_succeed -m $tree_O $tree_A $tree_B" - -test_expect_success \ - '2 - matching B alone is OK in !O && !A && B case.' \ - "rm -f .git/index NA && - cp .orig-B/NA NA && - git update-index --add NA && - echo extra >>NA && - read_tree_must_succeed -m $tree_O $tree_A $tree_B" - -test_expect_success \ - '3 - must match A in !O && A && !B case.' \ - "rm -f .git/index AN && - cp .orig-A/AN AN && - git update-index --add AN && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '3 - matching A alone is OK in !O && A && !B case.' \ - "rm -f .git/index AN && - cp .orig-A/AN AN && - git update-index --add AN && - echo extra >>AN && - read_tree_must_succeed -m $tree_O $tree_A $tree_B" - -test_expect_success \ - '3 (fail) - must match A in !O && A && !B case.' " - rm -f .git/index AN && - cp .orig-A/AN AN && - echo extra >>AN && - git update-index --add AN && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '4 - must match and be up-to-date in !O && A && B && A!=B case.' \ - "rm -f .git/index AA && - cp .orig-A/AA AA && - git update-index --add AA && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' " - rm -f .git/index AA && - cp .orig-A/AA AA && - git update-index --add AA && - echo extra >>AA && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' " - rm -f .git/index AA && - cp .orig-A/AA AA && - echo extra >>AA && - git update-index --add AA && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '5 - must match in !O && A && B && A==B case.' \ - "rm -f .git/index LL && - cp .orig-A/LL LL && - git update-index --add LL && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '5 - must match in !O && A && B && A==B case.' \ - "rm -f .git/index LL && - cp .orig-A/LL LL && - git update-index --add LL && - echo extra >>LL && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '5 (fail) - must match A in !O && A && B && A==B case.' " - rm -f .git/index LL && - cp .orig-A/LL LL && - echo extra >>LL && - git update-index --add LL && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '6 - must not exist in O && !A && !B case' " - rm -f .git/index DD && - echo DD >DD && - git update-index --add DD && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '7 - must not exist in O && !A && B && O!=B case' " - rm -f .git/index DM && - cp .orig-B/DM DM && - git update-index --add DM && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '8 - must not exist in O && !A && B && O==B case' " - rm -f .git/index DN && - cp .orig-B/DN DN && - git update-index --add DN && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '9 - must match and be up-to-date in O && A && !B && O!=A case' \ - "rm -f .git/index MD && - cp .orig-A/MD MD && - git update-index --add MD && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' " - rm -f .git/index MD && - cp .orig-A/MD MD && - git update-index --add MD && - echo extra >>MD && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' " - rm -f .git/index MD && - cp .orig-A/MD MD && - echo extra >>MD && - git update-index --add MD && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '10 - must match and be up-to-date in O && A && !B && O==A case' \ - "rm -f .git/index ND && - cp .orig-A/ND ND && - git update-index --add ND && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' " - rm -f .git/index ND && - cp .orig-A/ND ND && - git update-index --add ND && - echo extra >>ND && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' " - rm -f .git/index ND && - cp .orig-A/ND ND && - echo extra >>ND && - git update-index --add ND && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '11 - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \ - "rm -f .git/index MM && - cp .orig-A/MM MM && - git update-index --add MM && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' " - rm -f .git/index MM && - cp .orig-A/MM MM && - git update-index --add MM && - echo extra >>MM && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' " - rm -f .git/index MM && - cp .orig-A/MM MM && - echo extra >>MM && - git update-index --add MM && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '12 - must match A in O && A && B && O!=A && A==B case' \ - "rm -f .git/index SS && - cp .orig-A/SS SS && - git update-index --add SS && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '12 - must match A in O && A && B && O!=A && A==B case' \ - "rm -f .git/index SS && - cp .orig-A/SS SS && - git update-index --add SS && - echo extra >>SS && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '12 (fail) - must match A in O && A && B && O!=A && A==B case' " - rm -f .git/index SS && - cp .orig-A/SS SS && - echo extra >>SS && - git update-index --add SS && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '13 - must match A in O && A && B && O!=A && O==B case' \ - "rm -f .git/index MN && - cp .orig-A/MN MN && - git update-index --add MN && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '13 - must match A in O && A && B && O!=A && O==B case' \ - "rm -f .git/index MN && - cp .orig-A/MN MN && - git update-index --add MN && - echo extra >>MN && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '14 - must match and be up-to-date in O && A && B && O==A && O!=B case' \ - "rm -f .git/index NM && - cp .orig-A/NM NM && - git update-index --add NM && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '14 - may match B in O && A && B && O==A && O!=B case' \ - "rm -f .git/index NM && - cp .orig-B/NM NM && - git update-index --add NM && - echo extra >>NM && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' " - rm -f .git/index NM && - cp .orig-A/NM NM && - git update-index --add NM && - echo extra >>NM && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' " - rm -f .git/index NM && - cp .orig-A/NM NM && - echo extra >>NM && - git update-index --add NM && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -test_expect_success \ - '15 - must match A in O && A && B && O==A && O==B case' \ - "rm -f .git/index NN && - cp .orig-A/NN NN && - git update-index --add NN && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '15 - must match A in O && A && B && O==A && O==B case' \ - "rm -f .git/index NN && - cp .orig-A/NN NN && - git update-index --add NN && - echo extra >>NN && - read_tree_must_succeed -m $tree_O $tree_A $tree_B && - check_result" - -test_expect_success \ - '15 (fail) - must match A in O && A && B && O==A && O==B case' " - rm -f .git/index NN && - cp .orig-A/NN NN && - echo extra >>NN && - git update-index --add NN && - read_tree_must_fail -m $tree_O $tree_A $tree_B -" - -# #16 -test_expect_success \ - '16 - A matches in one and B matches in another.' \ - 'rm -f .git/index F16 && - echo F16 >F16 && - git update-index --add F16 && - tree0=$(git write-tree) && - echo E16 >F16 && - git update-index F16 && - tree1=$(git write-tree) && - read_tree_must_succeed -m $tree0 $tree1 $tree1 $tree0 && - git ls-files --stage' +test_expect_success '1 - must not have an entry not in A.' ' + rm -f .git/index XX && + echo XX >XX && + git update-index --add XX && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '2 - must match B in !O && !A && B case.' ' + rm -f .git/index NA && + cp .orig-B/NA NA && + git update-index --add NA && + read_tree_must_succeed -m $tree_O $tree_A $tree_B +' + +test_expect_success '2 - matching B alone is OK in !O && !A && B case.' ' + rm -f .git/index NA && + cp .orig-B/NA NA && + git update-index --add NA && + echo extra >>NA && + read_tree_must_succeed -m $tree_O $tree_A $tree_B +' + +test_expect_success '3 - must match A in !O && A && !B case.' ' + rm -f .git/index AN && + cp .orig-A/AN AN && + git update-index --add AN && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '3 - matching A alone is OK in !O && A && !B case.' ' + rm -f .git/index AN && + cp .orig-A/AN AN && + git update-index --add AN && + echo extra >>AN && + read_tree_must_succeed -m $tree_O $tree_A $tree_B +' + +test_expect_success '3 (fail) - must match A in !O && A && !B case.' ' + rm -f .git/index AN && + cp .orig-A/AN AN && + echo extra >>AN && + git update-index --add AN && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '4 - must match and be up-to-date in !O && A && B && A!=B case.' ' + rm -f .git/index AA && + cp .orig-A/AA AA && + git update-index --add AA && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' ' + rm -f .git/index AA && + cp .orig-A/AA AA && + git update-index --add AA && + echo extra >>AA && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '4 (fail) - must match and be up-to-date in !O && A && B && A!=B case.' ' + rm -f .git/index AA && + cp .orig-A/AA AA && + echo extra >>AA && + git update-index --add AA && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '5 - must match in !O && A && B && A==B case.' ' + rm -f .git/index LL && + cp .orig-A/LL LL && + git update-index --add LL && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '5 - must match in !O && A && B && A==B case.' ' + rm -f .git/index LL && + cp .orig-A/LL LL && + git update-index --add LL && + echo extra >>LL && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '5 (fail) - must match A in !O && A && B && A==B case.' ' + rm -f .git/index LL && + cp .orig-A/LL LL && + echo extra >>LL && + git update-index --add LL && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '6 - must not exist in O && !A && !B case' ' + rm -f .git/index DD && + echo DD >DD && + git update-index --add DD && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '7 - must not exist in O && !A && B && O!=B case' ' + rm -f .git/index DM && + cp .orig-B/DM DM && + git update-index --add DM && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '8 - must not exist in O && !A && B && O==B case' ' + rm -f .git/index DN && + cp .orig-B/DN DN && + git update-index --add DN && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '9 - must match and be up-to-date in O && A && !B && O!=A case' ' + rm -f .git/index MD && + cp .orig-A/MD MD && + git update-index --add MD && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' ' + rm -f .git/index MD && + cp .orig-A/MD MD && + git update-index --add MD && + echo extra >>MD && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '9 (fail) - must match and be up-to-date in O && A && !B && O!=A case' ' + rm -f .git/index MD && + cp .orig-A/MD MD && + echo extra >>MD && + git update-index --add MD && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '10 - must match and be up-to-date in O && A && !B && O==A case' ' + rm -f .git/index ND && + cp .orig-A/ND ND && + git update-index --add ND && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' ' + rm -f .git/index ND && + cp .orig-A/ND ND && + git update-index --add ND && + echo extra >>ND && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '10 (fail) - must match and be up-to-date in O && A && !B && O==A case' ' + rm -f .git/index ND && + cp .orig-A/ND ND && + echo extra >>ND && + git update-index --add ND && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '11 - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' ' + rm -f .git/index MM && + cp .orig-A/MM MM && + git update-index --add MM && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' ' + rm -f .git/index MM && + cp .orig-A/MM MM && + git update-index --add MM && + echo extra >>MM && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '11 (fail) - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' ' + rm -f .git/index MM && + cp .orig-A/MM MM && + echo extra >>MM && + git update-index --add MM && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '12 - must match A in O && A && B && O!=A && A==B case' ' + rm -f .git/index SS && + cp .orig-A/SS SS && + git update-index --add SS && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '12 - must match A in O && A && B && O!=A && A==B case' ' + rm -f .git/index SS && + cp .orig-A/SS SS && + git update-index --add SS && + echo extra >>SS && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '12 (fail) - must match A in O && A && B && O!=A && A==B case' ' + rm -f .git/index SS && + cp .orig-A/SS SS && + echo extra >>SS && + git update-index --add SS && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '13 - must match A in O && A && B && O!=A && O==B case' ' + rm -f .git/index MN && + cp .orig-A/MN MN && + git update-index --add MN && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '13 - must match A in O && A && B && O!=A && O==B case' ' + rm -f .git/index MN && + cp .orig-A/MN MN && + git update-index --add MN && + echo extra >>MN && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '14 - must match and be up-to-date in O && A && B && O==A && O!=B case' ' + rm -f .git/index NM && + cp .orig-A/NM NM && + git update-index --add NM && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '14 - may match B in O && A && B && O==A && O!=B case' ' + rm -f .git/index NM && + cp .orig-B/NM NM && + git update-index --add NM && + echo extra >>NM && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' ' + rm -f .git/index NM && + cp .orig-A/NM NM && + git update-index --add NM && + echo extra >>NM && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '14 (fail) - must match and be up-to-date in O && A && B && O==A && O!=B case' ' + rm -f .git/index NM && + cp .orig-A/NM NM && + echo extra >>NM && + git update-index --add NM && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '15 - must match A in O && A && B && O==A && O==B case' ' + rm -f .git/index NN && + cp .orig-A/NN NN && + git update-index --add NN && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '15 - must match A in O && A && B && O==A && O==B case' ' + rm -f .git/index NN && + cp .orig-A/NN NN && + git update-index --add NN && + echo extra >>NN && + read_tree_must_succeed -m $tree_O $tree_A $tree_B && + check_result +' + +test_expect_success '15 (fail) - must match A in O && A && B && O==A && O==B case' ' + rm -f .git/index NN && + cp .orig-A/NN NN && + echo extra >>NN && + git update-index --add NN && + read_tree_must_fail -m $tree_O $tree_A $tree_B +' + +test_expect_success '16 - A matches in one and B matches in another.' ' + rm -f .git/index F16 && + echo F16 >F16 && + git update-index --add F16 && + tree0=$(git write-tree) && + echo E16 >F16 && + git update-index F16 && + tree1=$(git write-tree) && + read_tree_must_succeed -m $tree0 $tree1 $tree1 $tree0 && + git ls-files --stage +' test_done diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh index db1b6f5cf4..5ededd8e40 100755 --- a/t/t1001-read-tree-m-2way.sh +++ b/t/t1001-read-tree-m-2way.sh @@ -14,10 +14,10 @@ all the combinations described in the two-tree merge "carry forward" rules, found in <Documentation/git read-tree.txt>. In the test, these paths are used: - bozbar - in H, stays in M, modified from bozbar to gnusto - frotz - not in H added in M - nitfol - in H, stays in M unmodified - rezrov - in H, deleted in M + bozbar - in H, stays in M, modified from bozbar to gnusto + frotz - not in H added in M + nitfol - in H, stays in M unmodified + rezrov - in H, deleted in M yomin - not in H or M ' . ./test-lib.sh @@ -60,336 +60,343 @@ EOF sed -e 's/bozbar/gnusto (earlier bozbar)/' bozbar-old >bozbar-new -test_expect_success \ - setup \ - 'echo frotz >frotz && - echo nitfol >nitfol && - cat bozbar-old >bozbar && - echo rezrov >rezrov && - echo yomin >yomin && - git update-index --add nitfol bozbar rezrov && - treeH=$(git write-tree) && - echo treeH $treeH && - git ls-tree $treeH && - - cat bozbar-new >bozbar && - git update-index --add frotz bozbar --force-remove rezrov && - git ls-files --stage >M.out && - treeM=$(git write-tree) && - echo treeM $treeM && - git ls-tree $treeM && - git diff-tree $treeH $treeM' - -test_expect_success \ - '1, 2, 3 - no carry forward' \ - 'rm -f .git/index && - read_tree_twoway $treeH $treeM && - git ls-files --stage >1-3.out && - test_cmp M.out 1-3.out && - check_cache_at bozbar dirty && - check_cache_at frotz dirty && - check_cache_at nitfol dirty' +test_expect_success 'setup' ' + echo frotz >frotz && + echo nitfol >nitfol && + cat bozbar-old >bozbar && + echo rezrov >rezrov && + echo yomin >yomin && + git update-index --add nitfol bozbar rezrov && + treeH=$(git write-tree) && + echo treeH $treeH && + git ls-tree $treeH && + + cat bozbar-new >bozbar && + git update-index --add frotz bozbar --force-remove rezrov && + git ls-files --stage >M.out && + treeM=$(git write-tree) && + echo treeM $treeM && + git ls-tree $treeM && + git diff-tree $treeH $treeM +' +test_expect_success '1, 2, 3 - no carry forward' ' + rm -f .git/index && + read_tree_twoway $treeH $treeM && + git ls-files --stage >1-3.out && + test_cmp M.out 1-3.out && + check_cache_at bozbar dirty && + check_cache_at frotz dirty && + check_cache_at nitfol dirty +' echo '+100644 X 0 yomin' >expected -test_expect_success \ - '4 - carry forward local addition.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - git update-index --add yomin && - read_tree_twoway $treeH $treeM && - git ls-files --stage >4.out && - test_must_fail git diff --no-index M.out 4.out >4diff.out && - compare_change 4diff.out expected && - check_cache_at yomin clean' - -test_expect_success \ - '5 - carry forward local addition.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo yomin >yomin && - git update-index --add yomin && - echo yomin yomin >yomin && - read_tree_twoway $treeH $treeM && - git ls-files --stage >5.out && - test_must_fail git diff --no-index M.out 5.out >5diff.out && - compare_change 5diff.out expected && - check_cache_at yomin dirty' - -test_expect_success \ - '6 - local addition already has the same.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - git update-index --add frotz && - read_tree_twoway $treeH $treeM && - git ls-files --stage >6.out && - test_cmp M.out 6.out && - check_cache_at frotz clean' - -test_expect_success \ - '7 - local addition already has the same.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo frotz >frotz && - git update-index --add frotz && - echo frotz frotz >frotz && - read_tree_twoway $treeH $treeM && - git ls-files --stage >7.out && - test_cmp M.out 7.out && - check_cache_at frotz dirty' - -test_expect_success \ - '8 - conflicting addition.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo frotz frotz >frotz && - git update-index --add frotz && - if read_tree_twoway $treeH $treeM; then false; else :; fi' - -test_expect_success \ - '9 - conflicting addition.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo frotz frotz >frotz && - git update-index --add frotz && - echo frotz >frotz && - if read_tree_twoway $treeH $treeM; then false; else :; fi' - -test_expect_success \ - '10 - path removed.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo rezrov >rezrov && - git update-index --add rezrov && - read_tree_twoway $treeH $treeM && - git ls-files --stage >10.out && - test_cmp M.out 10.out' - -test_expect_success \ - '11 - dirty path removed.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo rezrov >rezrov && - git update-index --add rezrov && - echo rezrov rezrov >rezrov && - if read_tree_twoway $treeH $treeM; then false; else :; fi' - -test_expect_success \ - '12 - unmatching local changes being removed.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo rezrov rezrov >rezrov && - git update-index --add rezrov && - if read_tree_twoway $treeH $treeM; then false; else :; fi' - -test_expect_success \ - '13 - unmatching local changes being removed.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo rezrov rezrov >rezrov && - git update-index --add rezrov && - echo rezrov >rezrov && - if read_tree_twoway $treeH $treeM; then false; else :; fi' +test_expect_success '4 - carry forward local addition.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + git update-index --add yomin && + read_tree_twoway $treeH $treeM && + git ls-files --stage >4.out && + test_must_fail git diff --no-index M.out 4.out >4diff.out && + compare_change 4diff.out expected && + check_cache_at yomin clean +' + +test_expect_success '5 - carry forward local addition.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo yomin >yomin && + git update-index --add yomin && + echo yomin yomin >yomin && + read_tree_twoway $treeH $treeM && + git ls-files --stage >5.out && + test_must_fail git diff --no-index M.out 5.out >5diff.out && + compare_change 5diff.out expected && + check_cache_at yomin dirty +' + +test_expect_success '6 - local addition already has the same.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + git update-index --add frotz && + read_tree_twoway $treeH $treeM && + git ls-files --stage >6.out && + test_cmp M.out 6.out && + check_cache_at frotz clean +' + +test_expect_success '7 - local addition already has the same.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo frotz >frotz && + git update-index --add frotz && + echo frotz frotz >frotz && + read_tree_twoway $treeH $treeM && + git ls-files --stage >7.out && + test_cmp M.out 7.out && + check_cache_at frotz dirty +' + +test_expect_success '8 - conflicting addition.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo frotz frotz >frotz && + git update-index --add frotz && + if read_tree_twoway $treeH $treeM; then false; else :; fi +' + +test_expect_success '9 - conflicting addition.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo frotz frotz >frotz && + git update-index --add frotz && + echo frotz >frotz && + if read_tree_twoway $treeH $treeM; then false; else :; fi +' + +test_expect_success '10 - path removed.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo rezrov >rezrov && + git update-index --add rezrov && + read_tree_twoway $treeH $treeM && + git ls-files --stage >10.out && + test_cmp M.out 10.out +' + +test_expect_success '11 - dirty path removed.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo rezrov >rezrov && + git update-index --add rezrov && + echo rezrov rezrov >rezrov && + if read_tree_twoway $treeH $treeM; then false; else :; fi +' + +test_expect_success '12 - unmatching local changes being removed.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo rezrov rezrov >rezrov && + git update-index --add rezrov && + if read_tree_twoway $treeH $treeM; then false; else :; fi +' + +test_expect_success '13 - unmatching local changes being removed.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo rezrov rezrov >rezrov && + git update-index --add rezrov && + echo rezrov >rezrov && + if read_tree_twoway $treeH $treeM; then false; else :; fi +' cat >expected <<EOF -100644 X 0 nitfol +100644 X 0 nitfol EOF -test_expect_success \ - '14 - unchanged in two heads.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo nitfol nitfol >nitfol && - git update-index --add nitfol && - read_tree_twoway $treeH $treeM && - git ls-files --stage >14.out && - test_must_fail git diff --no-index M.out 14.out >14diff.out && - compare_change 14diff.out expected && - check_cache_at nitfol clean' - -test_expect_success \ - '15 - unchanged in two heads.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo nitfol nitfol >nitfol && - git update-index --add nitfol && - echo nitfol nitfol nitfol >nitfol && - read_tree_twoway $treeH $treeM && - git ls-files --stage >15.out && - test_must_fail git diff --no-index M.out 15.out >15diff.out && - compare_change 15diff.out expected && - check_cache_at nitfol dirty' - -test_expect_success \ - '16 - conflicting local change.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo bozbar bozbar >bozbar && - git update-index --add bozbar && - if read_tree_twoway $treeH $treeM; then false; else :; fi' - -test_expect_success \ - '17 - conflicting local change.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - echo bozbar bozbar >bozbar && - git update-index --add bozbar && - echo bozbar bozbar bozbar >bozbar && - if read_tree_twoway $treeH $treeM; then false; else :; fi' - -test_expect_success \ - '18 - local change already having a good result.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - cat bozbar-new >bozbar && - git update-index --add bozbar && - read_tree_twoway $treeH $treeM && - git ls-files --stage >18.out && - test_cmp M.out 18.out && - check_cache_at bozbar clean' - -test_expect_success \ - '19 - local change already having a good result, further modified.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - cat bozbar-new >bozbar && - git update-index --add bozbar && - echo gnusto gnusto >bozbar && - read_tree_twoway $treeH $treeM && - git ls-files --stage >19.out && - test_cmp M.out 19.out && - check_cache_at bozbar dirty' - -test_expect_success \ - '20 - no local change, use new tree.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - cat bozbar-old >bozbar && - git update-index --add bozbar && - read_tree_twoway $treeH $treeM && - git ls-files --stage >20.out && - test_cmp M.out 20.out && - check_cache_at bozbar dirty' - -test_expect_success \ - '21 - no local change, dirty cache.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - cat bozbar-old >bozbar && - git update-index --add bozbar && - echo gnusto gnusto >bozbar && - if read_tree_twoway $treeH $treeM; then false; else :; fi' +test_expect_success '14 - unchanged in two heads.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo nitfol nitfol >nitfol && + git update-index --add nitfol && + read_tree_twoway $treeH $treeM && + git ls-files --stage >14.out && + test_must_fail git diff --no-index M.out 14.out >14diff.out && + compare_change 14diff.out expected && + check_cache_at nitfol clean +' + +test_expect_success '15 - unchanged in two heads.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo nitfol nitfol >nitfol && + git update-index --add nitfol && + echo nitfol nitfol nitfol >nitfol && + read_tree_twoway $treeH $treeM && + git ls-files --stage >15.out && + test_must_fail git diff --no-index M.out 15.out >15diff.out && + compare_change 15diff.out expected && + check_cache_at nitfol dirty +' + +test_expect_success '16 - conflicting local change.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo bozbar bozbar >bozbar && + git update-index --add bozbar && + if read_tree_twoway $treeH $treeM; then false; else :; fi +' + +test_expect_success '17 - conflicting local change.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + echo bozbar bozbar >bozbar && + git update-index --add bozbar && + echo bozbar bozbar bozbar >bozbar && + if read_tree_twoway $treeH $treeM; then false; else :; fi +' + +test_expect_success '18 - local change already having a good result.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + cat bozbar-new >bozbar && + git update-index --add bozbar && + read_tree_twoway $treeH $treeM && + git ls-files --stage >18.out && + test_cmp M.out 18.out && + check_cache_at bozbar clean +' + +test_expect_success '19 - local change already having a good result, further modified.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + cat bozbar-new >bozbar && + git update-index --add bozbar && + echo gnusto gnusto >bozbar && + read_tree_twoway $treeH $treeM && + git ls-files --stage >19.out && + test_cmp M.out 19.out && + check_cache_at bozbar dirty +' + +test_expect_success '20 - no local change, use new tree.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + cat bozbar-old >bozbar && + git update-index --add bozbar && + read_tree_twoway $treeH $treeM && + git ls-files --stage >20.out && + test_cmp M.out 20.out && + check_cache_at bozbar dirty +' + +test_expect_success '21 - no local change, dirty cache.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + cat bozbar-old >bozbar && + git update-index --add bozbar && + echo gnusto gnusto >bozbar && + if read_tree_twoway $treeH $treeM; then false; else :; fi +' # This fails with straight two-way fast-forward. -test_expect_success \ - '22 - local change cache updated.' \ - 'rm -f .git/index && - read_tree_must_succeed $treeH && - git checkout-index -u -f -q -a && - sed -e "s/such as/SUCH AS/" bozbar-old >bozbar && - git update-index --add bozbar && - if read_tree_twoway $treeH $treeM; then false; else :; fi' +test_expect_success '22 - local change cache updated.' ' + rm -f .git/index && + read_tree_must_succeed $treeH && + git checkout-index -u -f -q -a && + sed -e "s/such as/SUCH AS/" bozbar-old >bozbar && + git update-index --add bozbar && + if read_tree_twoway $treeH $treeM; then false; else :; fi +' # Also make sure we did not break DF vs DF/DF case. -test_expect_success \ - 'DF vs DF/DF case setup.' \ - 'rm -f .git/index && - echo DF >DF && - git update-index --add DF && - treeDF=$(git write-tree) && - echo treeDF $treeDF && - git ls-tree $treeDF && - - rm -f DF && - mkdir DF && - echo DF/DF >DF/DF && - git update-index --add --remove DF DF/DF && - treeDFDF=$(git write-tree) && - echo treeDFDF $treeDFDF && - git ls-tree $treeDFDF && - git ls-files --stage >DFDF.out' - -test_expect_success \ - 'DF vs DF/DF case test.' \ - 'rm -f .git/index && - rm -fr DF && - echo DF >DF && - git update-index --add DF && - read_tree_twoway $treeDF $treeDFDF && - git ls-files --stage >DFDFcheck.out && - test_cmp DFDF.out DFDFcheck.out && - check_cache_at DF/DF dirty && - :' - -test_expect_success \ - 'a/b (untracked) vs a case setup.' \ - 'rm -f .git/index && - : >a && - git update-index --add a && - treeM=$(git write-tree) && - echo treeM $treeM && - git ls-tree $treeM && - git ls-files --stage >treeM.out && - - rm -f a && - git update-index --remove a && - mkdir a && - : >a/b && - treeH=$(git write-tree) && - echo treeH $treeH && - git ls-tree $treeH' - -test_expect_success \ - 'a/b (untracked) vs a, plus c/d case test.' \ - 'read_tree_u_must_fail -u -m "$treeH" "$treeM" && - git ls-files --stage && - test -f a/b' - -test_expect_success \ - 'a/b vs a, plus c/d case setup.' \ - 'rm -f .git/index && - rm -fr a && - : >a && - mkdir c && - : >c/d && - git update-index --add a c/d && - treeM=$(git write-tree) && - echo treeM $treeM && - git ls-tree $treeM && - git ls-files --stage >treeM.out && - - rm -f a && - mkdir a && - : >a/b && - git update-index --add --remove a a/b && - treeH=$(git write-tree) && - echo treeH $treeH && - git ls-tree $treeH' - -test_expect_success \ - 'a/b vs a, plus c/d case test.' \ - 'read_tree_u_must_succeed -u -m "$treeH" "$treeM" && - git ls-files --stage | tee >treeMcheck.out && - test_cmp treeM.out treeMcheck.out' +test_expect_success 'DF vs DF/DF case setup.' ' + rm -f .git/index && + echo DF >DF && + git update-index --add DF && + treeDF=$(git write-tree) && + echo treeDF $treeDF && + git ls-tree $treeDF && + + rm -f DF && + mkdir DF && + echo DF/DF >DF/DF && + git update-index --add --remove DF DF/DF && + treeDFDF=$(git write-tree) && + echo treeDFDF $treeDFDF && + git ls-tree $treeDFDF && + git ls-files --stage >DFDF.out +' + +test_expect_success 'DF vs DF/DF case test.' ' + rm -f .git/index && + rm -fr DF && + echo DF >DF && + git update-index --add DF && + read_tree_twoway $treeDF $treeDFDF && + git ls-files --stage >DFDFcheck.out && + test_cmp DFDF.out DFDFcheck.out && + check_cache_at DF/DF dirty && + : +' + +test_expect_success 'a/b (untracked) vs a case setup.' ' + rm -f .git/index && + : >a && + git update-index --add a && + treeM=$(git write-tree) && + echo treeM $treeM && + git ls-tree $treeM && + git ls-files --stage >treeM.out && + + rm -f a && + git update-index --remove a && + mkdir a && + : >a/b && + treeH=$(git write-tree) && + echo treeH $treeH && + git ls-tree $treeH +' + +test_expect_success 'a/b (untracked) vs a, plus c/d case test.' ' + read_tree_u_must_fail -u -m "$treeH" "$treeM" && + git ls-files --stage && + test -f a/b +' + +test_expect_success 'read-tree supports the super-prefix' ' + cat <<-EOF >expect && + error: Updating '\''fictional/a'\'' would lose untracked files in it + EOF + test_must_fail git --super-prefix fictional/ read-tree -u -m "$treeH" "$treeM" 2>actual && + test_cmp expect actual +' + +test_expect_success 'a/b vs a, plus c/d case setup.' ' + rm -f .git/index && + rm -fr a && + : >a && + mkdir c && + : >c/d && + git update-index --add a c/d && + treeM=$(git write-tree) && + echo treeM $treeM && + git ls-tree $treeM && + git ls-files --stage >treeM.out && + + rm -f a && + mkdir a && + : >a/b && + git update-index --add --remove a a/b && + treeH=$(git write-tree) && + echo treeH $treeH && + git ls-tree $treeH +' + +test_expect_success 'a/b vs a, plus c/d case test.' ' + read_tree_u_must_succeed -u -m "$treeH" "$treeM" && + git ls-files --stage | tee >treeMcheck.out && + test_cmp treeM.out treeMcheck.out +' test_expect_success '-m references the correct modified tree' ' echo >file-a && diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh index 7d2baa15bb..c5245c5cb4 100755 --- a/t/t1007-hash-object.sh +++ b/t/t1007-hash-object.sh @@ -101,7 +101,7 @@ test_expect_success 'git hash-object --stdin file1 <file0 first operates on file test "$obname1" = "$obname1new" ' -test_expect_success 'check that appropriate filter is invoke when --path is used' ' +test_expect_success 'set up crlf tests' ' echo fooQ | tr Q "\\015" >file0 && cp file0 file1 && echo "file0 -crlf" >.gitattributes && @@ -109,7 +109,10 @@ test_expect_success 'check that appropriate filter is invoke when --path is used git config core.autocrlf true && file0_sha=$(git hash-object file0) && file1_sha=$(git hash-object file1) && - test "$file0_sha" != "$file1_sha" && + test "$file0_sha" != "$file1_sha" +' + +test_expect_success 'check that appropriate filter is invoke when --path is used' ' path1_sha=$(git hash-object --path=file1 file0) && path0_sha=$(git hash-object --path=file0 file1) && test "$file0_sha" = "$path0_sha" && @@ -117,38 +120,30 @@ test_expect_success 'check that appropriate filter is invoke when --path is used path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) && path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) && test "$file0_sha" = "$path0_sha" && - test "$file1_sha" = "$path1_sha" && - git config --unset core.autocrlf + test "$file1_sha" = "$path1_sha" +' + +test_expect_success 'gitattributes also work in a subdirectory' ' + mkdir subdir && + ( + cd subdir && + subdir_sha0=$(git hash-object ../file0) && + subdir_sha1=$(git hash-object ../file1) && + test "$file0_sha" = "$subdir_sha0" && + test "$file1_sha" = "$subdir_sha1" + ) ' test_expect_success 'check that --no-filters option works' ' - echo fooQ | tr Q "\\015" >file0 && - cp file0 file1 && - echo "file0 -crlf" >.gitattributes && - echo "file1 crlf" >>.gitattributes && - git config core.autocrlf true && - file0_sha=$(git hash-object file0) && - file1_sha=$(git hash-object file1) && - test "$file0_sha" != "$file1_sha" && nofilters_file1=$(git hash-object --no-filters file1) && test "$file0_sha" = "$nofilters_file1" && nofilters_file1=$(cat file1 | git hash-object --stdin) && - test "$file0_sha" = "$nofilters_file1" && - git config --unset core.autocrlf + test "$file0_sha" = "$nofilters_file1" ' test_expect_success 'check that --no-filters option works with --stdin-paths' ' - echo fooQ | tr Q "\\015" >file0 && - cp file0 file1 && - echo "file0 -crlf" >.gitattributes && - echo "file1 crlf" >>.gitattributes && - git config core.autocrlf true && - file0_sha=$(git hash-object file0) && - file1_sha=$(git hash-object file1) && - test "$file0_sha" != "$file1_sha" && nofilters_file1=$(echo "file1" | git hash-object --stdin-paths --no-filters) && - test "$file0_sha" = "$nofilters_file1" && - git config --unset core.autocrlf + test "$file0_sha" = "$nofilters_file1" ' pop_repo @@ -188,9 +183,30 @@ for args in "-w --stdin-paths" "--stdin-paths -w"; do pop_repo done -test_expect_success 'corrupt tree' ' +test_expect_success 'too-short tree' ' echo abc >malformed-tree && - test_must_fail git hash-object -t tree malformed-tree + test_must_fail git hash-object -t tree malformed-tree 2>err && + test_i18ngrep "too-short tree object" err +' + +hex2oct() { + perl -ne 'printf "\\%03o", hex for /../g' +} + +test_expect_success 'malformed mode in tree' ' + hex_sha1=$(echo foo | git hash-object --stdin -w) && + bin_sha1=$(echo $hex_sha1 | hex2oct) && + printf "9100644 \0$bin_sha1" >tree-with-malformed-mode && + test_must_fail git hash-object -t tree tree-with-malformed-mode 2>err && + test_i18ngrep "malformed mode in tree entry" err +' + +test_expect_success 'empty filename in tree' ' + hex_sha1=$(echo foo | git hash-object --stdin -w) && + bin_sha1=$(echo $hex_sha1 | hex2oct) && + printf "100644 \0$bin_sha1" >tree-with-empty-filename && + test_must_fail git hash-object -t tree tree-with-empty-filename 2>err && + test_i18ngrep "empty filename in tree entry" err ' test_expect_success 'corrupt commit' ' diff --git a/t/t1050-large.sh b/t/t1050-large.sh index 096dbffecc..6fd264cff0 100755 --- a/t/t1050-large.sh +++ b/t/t1050-large.sh @@ -5,6 +5,12 @@ test_description='adding and checking out large blobs' . ./test-lib.sh +# This should be moved to test-lib.sh together with the +# copy in t0021 after both topics have graduated to 'master'. +file_size () { + perl -e 'print -s $ARGV[0]' "$1" +} + test_expect_success setup ' # clone does not allow us to pass core.bigfilethreshold to # new repos, so set core.bigfilethreshold globally @@ -17,6 +23,29 @@ test_expect_success setup ' export GIT_ALLOC_LIMIT ' +# add a large file with different settings +while read expect config +do + test_expect_success "add with $config" ' + test_when_finished "rm -f .git/objects/pack/pack-*.* .git/index" && + git $config add large1 && + sz=$(file_size .git/objects/pack/pack-*.pack) && + case "$expect" in + small) test "$sz" -le 100000 ;; + large) test "$sz" -ge 100000 ;; + esac + ' +done <<\EOF +large -c core.compression=0 +small -c core.compression=9 +large -c core.compression=0 -c pack.compression=0 +large -c core.compression=9 -c pack.compression=0 +small -c core.compression=0 -c pack.compression=9 +small -c core.compression=9 -c pack.compression=9 +large -c pack.compression=0 +small -c pack.compression=9 +EOF + test_expect_success 'add a large file or two' ' git add large1 huge large2 && # make sure we got a single packfile and no loose objects diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh index 923bfc5a26..052f120216 100755 --- a/t/t1300-repo-config.sh +++ b/t/t1300-repo-config.sh @@ -1177,6 +1177,111 @@ test_expect_success 'urlmatch' ' test_cmp expect actual ' +test_expect_success 'urlmatch favors more specific URLs' ' + cat >.git/config <<-\EOF && + [http "https://example.com/"] + cookieFile = /tmp/root.txt + [http "https://example.com/subdirectory"] + cookieFile = /tmp/subdirectory.txt + [http "https://user@example.com/"] + cookieFile = /tmp/user.txt + [http "https://averylonguser@example.com/"] + cookieFile = /tmp/averylonguser.txt + [http "https://preceding.example.com"] + cookieFile = /tmp/preceding.txt + [http "https://*.example.com"] + cookieFile = /tmp/wildcard.txt + [http "https://*.example.com/wildcardwithsubdomain"] + cookieFile = /tmp/wildcardwithsubdomain.txt + [http "https://trailing.example.com"] + cookieFile = /tmp/trailing.txt + [http "https://user@*.example.com/"] + cookieFile = /tmp/wildcardwithuser.txt + [http "https://sub.example.com/"] + cookieFile = /tmp/sub.txt + EOF + + echo http.cookiefile /tmp/root.txt >expect && + git config --get-urlmatch HTTP https://example.com >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/subdirectory.txt >expect && + git config --get-urlmatch HTTP https://example.com/subdirectory >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/subdirectory.txt >expect && + git config --get-urlmatch HTTP https://example.com/subdirectory/nested >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/user.txt >expect && + git config --get-urlmatch HTTP https://user@example.com/ >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/subdirectory.txt >expect && + git config --get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/preceding.txt >expect && + git config --get-urlmatch HTTP https://preceding.example.com >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/wildcard.txt >expect && + git config --get-urlmatch HTTP https://wildcard.example.com >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/sub.txt >expect && + git config --get-urlmatch HTTP https://sub.example.com/wildcardwithsubdomain >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/trailing.txt >expect && + git config --get-urlmatch HTTP https://trailing.example.com >actual && + test_cmp expect actual && + + echo http.cookiefile /tmp/sub.txt >expect && + git config --get-urlmatch HTTP https://user@sub.example.com >actual && + test_cmp expect actual +' + +test_expect_success 'urlmatch with wildcard' ' + cat >.git/config <<-\EOF && + [http] + sslVerify + [http "https://*.example.com"] + sslVerify = false + cookieFile = /tmp/cookie.txt + EOF + + test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual && + test_must_be_empty actual && + + echo true >expect && + git config --bool --get-urlmatch http.SSLverify https://example.com >actual && + test_cmp expect actual && + + echo true >expect && + git config --bool --get-urlmatch http.SSLverify https://good-example.com >actual && + test_cmp expect actual && + + echo true >expect && + git config --bool --get-urlmatch http.sslverify https://deep.nested.example.com >actual && + test_cmp expect actual && + + echo false >expect && + git config --bool --get-urlmatch http.sslverify https://good.example.com >actual && + test_cmp expect actual && + + { + echo http.cookiefile /tmp/cookie.txt && + echo http.sslverify false + } >expect && + git config --get-urlmatch HTTP https://good.example.com >actual && + test_cmp expect actual && + + echo http.sslverify >expect && + git config --get-urlmatch HTTP https://more.example.com.au >actual && + test_cmp expect actual +' + # good section hygiene test_expect_failure 'unsetting the last key in a section removes header' ' cat >.git/config <<-\EOF && diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh index ac10875408..1312004f8c 100755 --- a/t/t1301-shared-repo.sh +++ b/t/t1301-shared-repo.sh @@ -172,4 +172,45 @@ test_expect_success POSIXPERM 'forced modes' ' }" actual)" ' +test_expect_success POSIXPERM 'remote init does not use config from cwd' ' + git config core.sharedrepository 0666 && + umask 0022 && + git init --bare child.git && + echo "-rw-r--r--" >expect && + modebits child.git/config >actual && + test_cmp expect actual +' + +test_expect_success POSIXPERM 're-init respects core.sharedrepository (local)' ' + git config core.sharedrepository 0666 && + umask 0022 && + echo whatever >templates/foo && + git init --template=templates && + echo "-rw-rw-rw-" >expect && + modebits .git/foo >actual && + test_cmp expect actual +' + +test_expect_success POSIXPERM 're-init respects core.sharedrepository (remote)' ' + rm -rf child.git && + umask 0022 && + git init --bare --shared=0666 child.git && + test_path_is_missing child.git/foo && + git init --bare --template=../templates child.git && + echo "-rw-rw-rw-" >expect && + modebits child.git/foo >actual && + test_cmp expect actual +' + +test_expect_success POSIXPERM 'template can set core.sharedrepository' ' + rm -rf child.git && + umask 0022 && + git config core.sharedrepository 0666 && + cp .git/config templates/config && + git init --bare --template=../templates child.git && + echo "-rw-rw-rw-" >expect && + modebits child.git/HEAD >actual && + test_cmp expect actual +' + test_done diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh index 9bcd34969f..ce4cff13bb 100755 --- a/t/t1302-repo-version.sh +++ b/t/t1302-repo-version.sh @@ -25,46 +25,26 @@ test_expect_success 'setup' ' test_expect_success 'gitdir selection on normal repos' ' echo 0 >expect && git config core.repositoryformatversion >actual && - ( - cd test && - git config core.repositoryformatversion >../actual2 - ) && + git -C test config core.repositoryformatversion >actual2 && test_cmp expect actual && test_cmp expect actual2 ' test_expect_success 'gitdir selection on unsupported repo' ' # Make sure it would stop at test2, not trash - echo 99 >expect && - ( - cd test2 && - git config core.repositoryformatversion >../actual - ) && - test_cmp expect actual + test_expect_code 1 git -C test2 config core.repositoryformatversion >actual ' test_expect_success 'gitdir not required mode' ' git apply --stat test.patch && - ( - cd test && - git apply --stat ../test.patch - ) && - ( - cd test2 && - git apply --stat ../test.patch - ) + git -C test apply --stat ../test.patch && + git -C test2 apply --stat ../test.patch ' test_expect_success 'gitdir required mode' ' git apply --check --index test.patch && - ( - cd test && - git apply --check --index ../test.patch - ) && - ( - cd test2 && - test_must_fail git apply --check --index ../test.patch - ) + git -C test apply --check --index ../test.patch && + test_must_fail git -C test2 apply --check --index ../test.patch ' check_allow () { diff --git a/t/t1308-config-set.sh b/t/t1308-config-set.sh index 7655c94c28..ff50960cca 100755 --- a/t/t1308-config-set.sh +++ b/t/t1308-config-set.sh @@ -219,14 +219,8 @@ test_expect_success 'check line errors for malformed values' ' ' test_expect_success 'error on modifying repo config without repo' ' - mkdir no-repo && - ( - GIT_CEILING_DIRECTORIES=$(pwd) && - export GIT_CEILING_DIRECTORIES && - cd no-repo && - test_must_fail git config a.b c 2>err && - grep "not in a git directory" err - ) + nongit test_must_fail git config a.b c 2>err && + grep "not in a git directory" err ' cmdline_config="'foo.bar=from-cmdline'" diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index d4fb977060..e208009906 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -8,23 +8,33 @@ test_description='Test git update-ref and basic ref logging' Z=$_z40 -test_expect_success setup ' +m=refs/heads/master +n_dir=refs/heads/gu +n=$n_dir/fixes +outside=refs/foo +bare=bare-repo +create_test_commits () +{ + prfx="$1" for name in A B C D E F do test_tick && T=$(git write-tree) && sha1=$(echo $name | git commit-tree $T) && - eval $name=$sha1 + eval $prfx$name=$sha1 done +} +test_expect_success setup ' + create_test_commits "" && + mkdir $bare && + cd $bare && + git init --bare && + create_test_commits "bare" && + cd - ' -m=refs/heads/master -n_dir=refs/heads/gu -n=$n_dir/fixes -outside=refs/foo - test_expect_success \ "create $m" \ "git update-ref $m $A && @@ -93,6 +103,61 @@ test_expect_success 'update-ref creates reflogs with --create-reflog' ' git reflog exists $outside ' +test_expect_success 'creates no reflog in bare repository' ' + git -C $bare update-ref $m $bareA && + git -C $bare rev-parse $bareA >expect && + git -C $bare rev-parse $m >actual && + test_cmp expect actual && + test_must_fail git -C $bare reflog exists $m +' + +test_expect_success 'core.logAllRefUpdates=true creates reflog in bare repository' ' + test_when_finished "git -C $bare config --unset core.logAllRefUpdates && \ + rm $bare/logs/$m" && + git -C $bare config core.logAllRefUpdates true && + git -C $bare update-ref $m $bareB && + git -C $bare rev-parse $bareB >expect && + git -C $bare rev-parse $m >actual && + test_cmp expect actual && + git -C $bare reflog exists $m +' + +test_expect_success 'core.logAllRefUpdates=true does not create reflog by default' ' + test_config core.logAllRefUpdates true && + test_when_finished "git update-ref -d $outside" && + git update-ref $outside $A && + git rev-parse $A >expect && + git rev-parse $outside >actual && + test_cmp expect actual && + test_must_fail git reflog exists $outside +' + +test_expect_success 'core.logAllRefUpdates=always creates reflog by default' ' + test_config core.logAllRefUpdates always && + test_when_finished "git update-ref -d $outside" && + git update-ref $outside $A && + git rev-parse $A >expect && + git rev-parse $outside >actual && + test_cmp expect actual && + git reflog exists $outside +' + +test_expect_success 'core.logAllRefUpdates=always creates no reflog for ORIG_HEAD' ' + test_config core.logAllRefUpdates always && + git update-ref ORIG_HEAD $A && + test_must_fail git reflog exists ORIG_HEAD +' + +test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always' ' + test_config core.logAllRefUpdates true && + test_when_finished "git update-ref -d $outside" && + git update-ref --no-create-reflog $outside $A && + git rev-parse $A >expect && + git rev-parse $outside >actual && + test_cmp expect actual && + test_must_fail git reflog exists $outside +' + test_expect_success \ "create $m (by HEAD)" \ "git update-ref HEAD $A && @@ -191,6 +256,33 @@ test_expect_success \ git update-ref HEAD'" $A && test $A"' = $(cat .git/'"$m"')' +test_expect_success "empty directory removal" ' + git branch d1/d2/r1 HEAD && + git branch d1/r2 HEAD && + test -f .git/refs/heads/d1/d2/r1 && + test -f .git/logs/refs/heads/d1/d2/r1 && + git branch -d d1/d2/r1 && + ! test -e .git/refs/heads/d1/d2 && + ! test -e .git/logs/refs/heads/d1/d2 && + test -f .git/refs/heads/d1/r2 && + test -f .git/logs/refs/heads/d1/r2 +' + +test_expect_success "symref empty directory removal" ' + git branch e1/e2/r1 HEAD && + git branch e1/r2 HEAD && + git checkout e1/e2/r1 && + test_when_finished "git checkout master" && + test -f .git/refs/heads/e1/e2/r1 && + test -f .git/logs/refs/heads/e1/e2/r1 && + git update-ref -d HEAD && + ! test -e .git/refs/heads/e1/e2 && + ! test -e .git/logs/refs/heads/e1/e2 && + test -f .git/refs/heads/e1/r2 && + test -f .git/logs/refs/heads/e1/r2 && + test -f .git/logs/HEAD +' + cat >expect <<EOF $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch @@ -501,6 +593,7 @@ test_expect_success 'stdin does not create reflogs by default' ' ' test_expect_success 'stdin creates reflogs with --create-reflog' ' + test_when_finished "git update-ref -d $outside" && echo "create $outside $m" >stdin && git update-ref --create-reflog --stdin <stdin && git rev-parse $m >expect && diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh index 7e10bcfe39..30354fd26c 100755 --- a/t/t1403-show-ref.sh +++ b/t/t1403-show-ref.sh @@ -97,6 +97,9 @@ test_expect_success 'show-ref -d' ' git show-ref -d refs/tags/A refs/tags/C >actual && test_cmp expect actual && + git show-ref --verify -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 && @@ -116,6 +119,12 @@ test_expect_success 'show-ref -d' ' test_cmp expect actual && test_must_fail git show-ref -d --verify heads/master >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify -d A C >actual && + test_cmp expect actual && + + test_must_fail git show-ref --verify -d tags/A tags/C >actual && test_cmp expect actual ' @@ -164,4 +173,37 @@ test_expect_success 'show-ref --heads, --tags, --head, pattern' ' test_cmp expect actual ' +test_expect_success 'show-ref --verify HEAD' ' + echo $(git rev-parse HEAD) HEAD >expect && + git show-ref --verify HEAD >actual && + test_cmp expect actual && + + >expect && + + git show-ref --verify -q HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'show-ref --verify with dangling ref' ' + sha1_file() { + echo "$*" | sed "s#..#.git/objects/&/#" + } && + + remove_object() { + file=$(sha1_file "$*") && + test -e "$file" && + rm -f "$file" + } && + + test_when_finished "rm -rf dangling" && + ( + git init dangling && + cd dangling && + test_commit dangling && + sha=$(git rev-parse refs/tags/dangling) && + remove_object $sha && + test_must_fail git show-ref --verify refs/tags/dangling + ) +' + test_done diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 8f52da2771..33a51c9a67 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -43,13 +43,13 @@ test_expect_success 'HEAD is part of refs, valid objects appear valid' ' test_expect_success 'setup: helpers for corruption tests' ' sha1_file() { - echo "$*" | sed "s#..#.git/objects/&/#" + remainder=${1#??} && + firsttwo=${1%$remainder} && + echo ".git/objects/$firsttwo/$remainder" } && remove_object() { - file=$(sha1_file "$*") && - test -e "$file" && - rm -f "$file" + rm "$(sha1_file "$1")" } ' @@ -188,16 +188,17 @@ test_expect_success 'commit with NUL in header' ' grep "error in commit $new.*unterminated header: NUL at offset" out ' -test_expect_success 'malformatted tree object' ' - test_when_finished "git update-ref -d refs/tags/wrong" && - test_when_finished "remove_object \$T" && +test_expect_success 'tree object with duplicate entries' ' + test_when_finished "for i in \$T; do remove_object \$i; done" && T=$( GIT_INDEX_FILE=test-index && export GIT_INDEX_FILE && rm -f test-index && >x && git add x && + git rev-parse :x && T=$(git write-tree) && + echo $T && ( git cat-file tree $T && git cat-file tree $T @@ -208,6 +209,19 @@ test_expect_success 'malformatted tree object' ' grep "error in tree .*contains duplicate file entries" out ' +test_expect_success 'unparseable tree object' ' + test_when_finished "git update-ref -d refs/heads/wrong" && + test_when_finished "remove_object \$tree_sha1" && + test_when_finished "remove_object \$commit_sha1" && + tree_sha1=$(printf "100644 \0twenty-bytes-of-junk" | git hash-object -t tree --stdin -w --literally) && + commit_sha1=$(git commit-tree $tree_sha1) && + git update-ref refs/heads/wrong $commit_sha1 && + test_must_fail git fsck 2>out && + test_i18ngrep "error: empty filename in tree entry" out && + test_i18ngrep "$tree_sha1" out && + test_i18ngrep ! "fatal: empty filename in tree entry" out +' + test_expect_success 'tag pointing to nonexistent' ' cat >invalid-tag <<-\EOF && object ffffffffffffffffffffffffffffffffffffffff @@ -509,9 +523,21 @@ test_expect_success 'fsck --connectivity-only' ' touch empty && git add empty && test_commit empty && + + # Drop the index now; we want to be sure that we + # recursively notice the broken objects + # because they are reachable from refs, not because + # they are in the index. + rm -f .git/index && + + # corrupt the blob, but in a way that we can still identify + # its type. That lets us see that --connectivity-only is + # not actually looking at the contents, but leaves it + # free to examine the type if it chooses. empty=.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 && - rm -f $empty && - echo invalid >$empty && + blob=$(echo unrelated | git hash-object -w --stdin) && + mv -f $(sha1_file $blob) $empty && + test_must_fail git fsck --strict && git fsck --strict --connectivity-only && tree=$(git rev-parse HEAD:) && @@ -523,12 +549,18 @@ test_expect_success 'fsck --connectivity-only' ' ) ' -remove_loose_object () { - sha1="$(git rev-parse "$1")" && - remainder=${sha1#??} && - firsttwo=${sha1%$remainder} && - rm .git/objects/$firsttwo/$remainder -} +test_expect_success 'fsck --connectivity-only with explicit head' ' + rm -rf connectivity-only && + git init connectivity-only && + ( + cd connectivity-only && + test_commit foo && + rm -f .git/index && + tree=$(git rev-parse HEAD^{tree}) && + remove_object $(git rev-parse HEAD:foo.t) && + test_must_fail git fsck --connectivity-only $tree + ) +' test_expect_success 'fsck --name-objects' ' rm -rf name-objects && @@ -538,11 +570,123 @@ test_expect_success 'fsck --name-objects' ' test_commit julius caesar.t && test_commit augustus && test_commit caesar && - remove_loose_object $(git rev-parse julius:caesar.t) && + remove_object $(git rev-parse julius:caesar.t) && test_must_fail git fsck --name-objects >out && tree=$(git rev-parse --verify julius:) && grep "$tree (\(refs/heads/master\|HEAD\)@{[0-9]*}:" out ) ' +test_expect_success 'alternate objects are correctly blamed' ' + test_when_finished "rm -rf alt.git .git/objects/info/alternates" && + git init --bare alt.git && + echo "../../alt.git/objects" >.git/objects/info/alternates && + mkdir alt.git/objects/12 && + >alt.git/objects/12/34567890123456789012345678901234567890 && + test_must_fail git fsck >out 2>&1 && + grep alt.git out +' + +test_expect_success 'fsck errors in packed objects' ' + git cat-file commit HEAD >basis && + sed "s/</one/" basis >one && + sed "s/</foo/" basis >two && + one=$(git hash-object -t commit -w one) && + two=$(git hash-object -t commit -w two) && + pack=$( + { + echo $one && + echo $two + } | git pack-objects .git/objects/pack/pack + ) && + test_when_finished "rm -f .git/objects/pack/pack-$pack.*" && + remove_object $one && + remove_object $two && + test_must_fail git fsck 2>out && + grep "error in commit $one.* - bad name" out && + grep "error in commit $two.* - bad name" out && + ! grep corrupt out +' + +test_expect_success 'fsck finds problems in duplicate loose objects' ' + rm -rf broken-duplicate && + git init broken-duplicate && + ( + cd broken-duplicate && + test_commit duplicate && + # no "-d" here, so we end up with duplicates + git repack && + # now corrupt the loose copy + file=$(sha1_file "$(git rev-parse HEAD)") && + rm "$file" && + echo broken >"$file" && + test_must_fail git fsck + ) +' + +test_expect_success 'fsck detects trailing loose garbage (commit)' ' + git cat-file commit HEAD >basis && + echo bump-commit-sha1 >>basis && + commit=$(git hash-object -w -t commit basis) && + file=$(sha1_file $commit) && + test_when_finished "remove_object $commit" && + chmod +w "$file" && + echo garbage >>"$file" && + test_must_fail git fsck 2>out && + test_i18ngrep "garbage.*$commit" out +' + +test_expect_success 'fsck detects trailing loose garbage (blob)' ' + blob=$(echo trailing | git hash-object -w --stdin) && + file=$(sha1_file $blob) && + test_when_finished "remove_object $blob" && + chmod +w "$file" && + echo garbage >>"$file" && + test_must_fail git fsck 2>out && + test_i18ngrep "garbage.*$blob" out +' + +# for each of type, we have one version which is referenced by another object +# (and so while unreachable, not dangling), and another variant which really is +# dangling. +test_expect_success 'fsck notices dangling objects' ' + git init dangling && + ( + cd dangling && + blob=$(echo not-dangling | git hash-object -w --stdin) && + dblob=$(echo dangling | git hash-object -w --stdin) && + tree=$(printf "100644 blob %s\t%s\n" $blob one | git mktree) && + dtree=$(printf "100644 blob %s\t%s\n" $blob two | git mktree) && + commit=$(git commit-tree $tree) && + dcommit=$(git commit-tree -p $commit $tree) && + + cat >expect <<-EOF && + dangling blob $dblob + dangling commit $dcommit + dangling tree $dtree + EOF + + git fsck >actual && + # the output order is non-deterministic, as it comes from a hash + sort <actual >actual.sorted && + test_cmp expect actual.sorted + ) +' + +test_expect_success 'fsck $name notices bogus $name' ' + test_must_fail git fsck bogus && + test_must_fail git fsck $_z40 +' + +test_expect_success 'bogus head does not fallback to all heads' ' + # set up a case that will cause a reachability complaint + echo to-be-deleted >foo && + git add foo && + blob=$(git rev-parse :foo) && + test_when_finished "git rm --cached foo" && + remove_object $blob && + test_must_fail git fsck $_z40 >out 2>&1 && + ! grep $blob out +' + test_done diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh index 038e24c401..9ed8b8ccba 100755 --- a/t/t1500-rev-parse.sh +++ b/t/t1500-rev-parse.sh @@ -3,7 +3,7 @@ test_description='test git rev-parse' . ./test-lib.sh -# usage: [options] label is-bare is-inside-git is-inside-work prefix git-dir +# usage: [options] label is-bare is-inside-git is-inside-work prefix git-dir absolute-git-dir test_rev_parse () { d= bare= @@ -29,7 +29,8 @@ test_rev_parse () { --is-inside-git-dir \ --is-inside-work-tree \ --show-prefix \ - --git-dir + --git-dir \ + --absolute-git-dir do test $# -eq 0 && break expect="$1" @@ -62,29 +63,57 @@ test_expect_success 'setup' ' cp -R .git repo.git ' -test_rev_parse toplevel false false true '' .git +test_rev_parse toplevel false false true '' .git "$ROOT/.git" -test_rev_parse -C .git .git/ false true false '' . -test_rev_parse -C .git/objects .git/objects/ false true false '' "$ROOT/.git" +test_rev_parse -C .git .git/ false true false '' . "$ROOT/.git" +test_rev_parse -C .git/objects .git/objects/ false true false '' "$ROOT/.git" "$ROOT/.git" -test_rev_parse -C sub/dir subdirectory false false true sub/dir/ "$ROOT/.git" +test_rev_parse -C sub/dir subdirectory false false true sub/dir/ "$ROOT/.git" "$ROOT/.git" test_rev_parse -b t 'core.bare = true' true false false test_rev_parse -b u 'core.bare undefined' false false true -test_rev_parse -C work -g ../.git -b f 'GIT_DIR=../.git, core.bare = false' false false true '' +test_rev_parse -C work -g ../.git -b f 'GIT_DIR=../.git, core.bare = false' false false true '' "../.git" "$ROOT/.git" test_rev_parse -C work -g ../.git -b t 'GIT_DIR=../.git, core.bare = true' true false false '' test_rev_parse -C work -g ../.git -b u 'GIT_DIR=../.git, core.bare undefined' false false true '' -test_rev_parse -C work -g ../repo.git -b f 'GIT_DIR=../repo.git, core.bare = false' false false true '' +test_rev_parse -C work -g ../repo.git -b f 'GIT_DIR=../repo.git, core.bare = false' false false true '' "../repo.git" "$ROOT/repo.git" test_rev_parse -C work -g ../repo.git -b t 'GIT_DIR=../repo.git, core.bare = true' true false false '' test_rev_parse -C work -g ../repo.git -b u 'GIT_DIR=../repo.git, core.bare undefined' false false true '' +test_expect_success 'git-common-dir from worktree root' ' + echo .git >expect && + git rev-parse --git-common-dir >actual && + test_cmp expect actual +' + +test_expect_success 'git-common-dir inside sub-dir' ' + mkdir -p path/to/child && + test_when_finished "rm -rf path" && + echo "$(git -C path/to/child rev-parse --show-cdup).git" >expect && + git -C path/to/child rev-parse --git-common-dir >actual && + test_cmp expect actual +' + +test_expect_success 'git-path from worktree root' ' + echo .git/objects >expect && + git rev-parse --git-path objects >actual && + test_cmp expect actual +' + +test_expect_success 'git-path inside sub-dir' ' + mkdir -p path/to/child && + test_when_finished "rm -rf path" && + echo "$(git -C path/to/child rev-parse --show-cdup).git/objects" >expect && + git -C path/to/child rev-parse --git-path objects >actual && + test_cmp expect actual +' + test_done diff --git a/t/t1512-rev-parse-disambiguation.sh b/t/t1512-rev-parse-disambiguation.sh index e221167cfb..711704ba5a 100755 --- a/t/t1512-rev-parse-disambiguation.sh +++ b/t/t1512-rev-parse-disambiguation.sh @@ -42,7 +42,7 @@ test_expect_success 'blob and 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_i18ngrep "short SHA1 000000000 is ambiguous" actual ' test_expect_success 'disambiguate tree-ish' ' @@ -264,6 +264,13 @@ test_expect_success 'ambiguous commit-ish' ' test_must_fail git log 000000000... ' +# There are three objects with this prefix: a blob, a tree, and a tag. We know +# the blob will not pass as a treeish, but the tree and tag should (and thus +# cause an error). +test_expect_success 'ambiguous tags peel to treeish' ' + test_must_fail git rev-parse 0000000000f^{tree} +' + 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 @@ -273,6 +280,13 @@ test_expect_success 'rev-parse --disambiguate' ' test "$(sed -e "s/^\(.........\).*/\1/" actual | sort -u)" = 000000000 ' +test_expect_success 'rev-parse --disambiguate drops duplicates' ' + git rev-parse --disambiguate=000000000 >expect && + git pack-objects .git/objects/pack/pack <expect && + git rev-parse --disambiguate=000000000 >actual && + test_cmp expect actual +' + test_expect_success 'ambiguous 40-hex ref' ' TREE=$(git mktree </dev/null) && REF=$(git rev-parse HEAD) && @@ -291,4 +305,60 @@ test_expect_success 'ambiguous short sha1 ref' ' grep "refname.*${REF}.*ambiguous" err ' +test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (raw)' ' + test_must_fail git rev-parse 00000 2>stderr && + grep "is ambiguous" stderr >errors && + test_line_count = 1 errors +' + +test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (treeish)' ' + test_must_fail git rev-parse 00000:foo 2>stderr && + grep "is ambiguous" stderr >errors && + test_line_count = 1 errors +' + +test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (peel)' ' + test_must_fail git rev-parse 00000^{commit} 2>stderr && + grep "is ambiguous" stderr >errors && + test_line_count = 1 errors +' + +test_expect_success C_LOCALE_OUTPUT 'ambiguity hints' ' + test_must_fail git rev-parse 000000000 2>stderr && + grep ^hint: stderr >hints && + # 16 candidates, plus one intro line + test_line_count = 17 hints +' + +test_expect_success C_LOCALE_OUTPUT 'ambiguity hints respect type' ' + test_must_fail git rev-parse 000000000^{commit} 2>stderr && + grep ^hint: stderr >hints && + # 5 commits, 1 tag (which is a commitish), plus intro line + test_line_count = 7 hints +' + +test_expect_success C_LOCALE_OUTPUT 'failed type-selector still shows hint' ' + # these two blobs share the same prefix "ee3d", but neither + # will pass for a commit + echo 851 | git hash-object --stdin -w && + echo 872 | git hash-object --stdin -w && + test_must_fail git rev-parse ee3d^{commit} 2>stderr && + grep ^hint: stderr >hints && + test_line_count = 3 hints +' + +test_expect_success 'core.disambiguate config can prefer types' ' + # ambiguous between tree and tag + sha1=0000000000f && + test_must_fail git rev-parse $sha1 && + git rev-parse $sha1^{commit} && + git -c core.disambiguate=committish rev-parse $sha1 +' + +test_expect_success 'core.disambiguate does not override context' ' + # treeish ambiguous between tag and tree + test_must_fail \ + git -c core.disambiguate=committish rev-parse $sha1^{tree} +' + test_done diff --git a/t/t1514-rev-parse-push.sh b/t/t1514-rev-parse-push.sh index 7214f5b33f..623a32aa6e 100755 --- a/t/t1514-rev-parse-push.sh +++ b/t/t1514-rev-parse-push.sh @@ -60,4 +60,10 @@ test_expect_success '@{push} with push refspecs' ' resolve topic@{push} refs/remotes/origin/magic/topic ' +test_expect_success 'resolving @{push} fails with a detached HEAD' ' + git checkout HEAD^0 && + test_when_finished "git checkout -" && + test_must_fail git rev-parse @{push} +' + test_done diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh index 292a0720fc..6096f2c630 100755 --- a/t/t1700-split-index.sh +++ b/t/t1700-split-index.sh @@ -200,4 +200,20 @@ EOF test_cmp expect actual ' +test_expect_success 'rev-parse --shared-index-path' ' + test_create_repo split-index && + ( + cd split-index && + git update-index --split-index && + echo .git/sharedindex* >expect && + git rev-parse --shared-index-path >actual && + test_cmp expect actual && + mkdir subdirectory && + cd subdirectory && + echo ../.git/sharedindex* >expect && + git rev-parse --shared-index-path >actual && + test_cmp expect actual + ) +' + test_done diff --git a/t/t2027-worktree-list.sh b/t/t2027-worktree-list.sh index 1b1b65a6b0..848da5f368 100755 --- a/t/t2027-worktree-list.sh +++ b/t/t2027-worktree-list.sh @@ -14,10 +14,18 @@ test_expect_success 'rev-parse --git-common-dir on main worktree' ' test_cmp expected actual && mkdir sub && git -C sub rev-parse --git-common-dir >actual2 && - echo sub/.git >expected2 && + echo ../.git >expected2 && test_cmp expected2 actual2 ' +test_expect_success 'rev-parse --git-path objects linked worktree' ' + echo "$(git rev-parse --show-toplevel)/.git/objects" >expect && + test_when_finished "rm -rf linked-tree && git worktree prune" && + git worktree add --detach linked-tree master && + git -C linked-tree rev-parse --git-path objects >actual && + test_cmp expect actual +' + test_expect_success '"list" all worktrees from main' ' echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect && test_when_finished "rm -rf here && git worktree prune" && @@ -96,4 +104,44 @@ test_expect_success 'bare repo cleanup' ' rm -rf bare1 ' +test_expect_success 'broken main worktree still at the top' ' + git init broken-main && + ( + cd broken-main && + test_commit new && + git worktree add linked && + cat >expected <<-EOF && + worktree $(pwd) + HEAD $_z40 + + EOF + cd linked && + echo "worktree $(pwd)" >expected && + echo "ref: .broken" >../.git/HEAD && + git worktree list --porcelain | head -n 3 >actual && + test_cmp ../expected actual && + git worktree list | head -n 1 >actual.2 && + grep -F "(error)" actual.2 + ) +' + +test_expect_success 'linked worktrees are sorted' ' + mkdir sorted && + git init sorted/main && + ( + cd sorted/main && + test_tick && + test_commit new && + git worktree add ../first && + git worktree add ../second && + git worktree list --porcelain | grep ^worktree >actual + ) && + cat >expected <<-EOF && + worktree $(pwd)/sorted/main + worktree $(pwd)/sorted/first + worktree $(pwd)/sorted/second + EOF + test_cmp expected sorted/main/actual +' + test_done diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh index 8f22c43e24..84a9028c43 100755 --- a/t/t2203-add-intent.sh +++ b/t/t2203-add-intent.sh @@ -5,10 +5,24 @@ test_description='Intent to add' . ./test-lib.sh test_expect_success 'intent to add' ' + test_commit 1 && + git rm 1.t && + echo hello >1.t && echo hello >file && echo hello >elif && git add -N file && - git add elif + git add elif && + git add -N 1.t +' + +test_expect_success 'git status' ' + git status --porcelain | grep -v actual >actual && + cat >expect <<-\EOF && + DA 1.t + A elif + A file + EOF + test_cmp expect actual ' test_expect_success 'check result of "add -N"' ' @@ -43,7 +57,9 @@ test_expect_success 'i-t-a entry is simply ignored' ' git add -N nitfol && git commit -m second && test $(git ls-tree HEAD -- nitfol | wc -l) = 0 && - test $(git diff --name-only HEAD -- nitfol | wc -l) = 1 + test $(git diff --name-only HEAD -- nitfol | wc -l) = 1 && + test $(git diff --name-only --ita-invisible-in-index HEAD -- nitfol | wc -l) = 0 && + test $(git diff --name-only --ita-invisible-in-index -- nitfol | wc -l) = 1 ' test_expect_success 'can commit with an unrelated i-t-a entry in index' ' @@ -113,5 +129,26 @@ test_expect_success 'cache-tree does skip dir that becomes empty' ' ) ' +test_expect_success 'commit: ita entries ignored in empty intial commit check' ' + git init empty-intial-commit && + ( + cd empty-intial-commit && + : >one && + git add -N one && + test_must_fail git commit -m nothing-new-here + ) +' + +test_expect_success 'commit: ita entries ignored in empty commit check' ' + git init empty-subsequent-commit && + ( + cd empty-subsequent-commit && + test_commit one && + : >two && + git add -N two && + test_must_fail git commit -m nothing-new-here + ) +' + test_done diff --git a/t/t3007-ls-files-recurse-submodules.sh b/t/t3007-ls-files-recurse-submodules.sh new file mode 100755 index 0000000000..a5426171d3 --- /dev/null +++ b/t/t3007-ls-files-recurse-submodules.sh @@ -0,0 +1,210 @@ +#!/bin/sh + +test_description='Test ls-files recurse-submodules feature + +This test verifies the recurse-submodules feature correctly lists files from +submodules. +' + +. ./test-lib.sh + +test_expect_success 'setup directory structure and submodules' ' + echo a >a && + mkdir b && + echo b >b/b && + git add a b && + git commit -m "add a and b" && + git init submodule && + echo c >submodule/c && + git -C submodule add c && + git -C submodule commit -m "add c" && + git submodule add ./submodule && + git commit -m "added submodule" +' + +test_expect_success 'ls-files correctly outputs files in submodule' ' + cat >expect <<-\EOF && + .gitmodules + a + b/b + submodule/c + EOF + + git ls-files --recurse-submodules >actual && + test_cmp expect actual +' + +test_expect_success 'ls-files correctly outputs files in submodule with -z' ' + lf_to_nul >expect <<-\EOF && + .gitmodules + a + b/b + submodule/c + EOF + + git ls-files --recurse-submodules -z >actual && + test_cmp expect actual +' + +test_expect_success 'ls-files does not output files not added to a repo' ' + cat >expect <<-\EOF && + .gitmodules + a + b/b + submodule/c + EOF + + echo a >not_added && + echo b >b/not_added && + echo c >submodule/not_added && + git ls-files --recurse-submodules >actual && + test_cmp expect actual +' + +test_expect_success 'ls-files recurses more than 1 level' ' + cat >expect <<-\EOF && + .gitmodules + a + b/b + submodule/.gitmodules + submodule/c + submodule/subsub/d + EOF + + git init submodule/subsub && + echo d >submodule/subsub/d && + git -C submodule/subsub add d && + git -C submodule/subsub commit -m "add d" && + git -C submodule submodule add ./subsub && + git -C submodule commit -m "added subsub" && + git ls-files --recurse-submodules >actual && + test_cmp expect actual +' + +test_expect_success '--recurse-submodules and pathspecs setup' ' + echo e >submodule/subsub/e.txt && + git -C submodule/subsub add e.txt && + git -C submodule/subsub commit -m "adding e.txt" && + echo f >submodule/f.TXT && + echo g >submodule/g.txt && + git -C submodule add f.TXT g.txt && + git -C submodule commit -m "add f and g" && + echo h >h.txt && + mkdir sib && + echo sib >sib/file && + git add h.txt sib/file && + git commit -m "add h and sib/file" && + git init sub && + echo sub >sub/file && + git -C sub add file && + git -C sub commit -m "add file" && + git submodule add ./sub && + git commit -m "added sub" && + + cat >expect <<-\EOF && + .gitmodules + a + b/b + h.txt + sib/file + sub/file + submodule/.gitmodules + submodule/c + submodule/f.TXT + submodule/g.txt + submodule/subsub/d + submodule/subsub/e.txt + EOF + + git ls-files --recurse-submodules >actual && + test_cmp expect actual && + cat actual && + git ls-files --recurse-submodules "*" >actual && + test_cmp expect actual +' + +test_expect_success '--recurse-submodules and pathspecs' ' + cat >expect <<-\EOF && + h.txt + submodule/g.txt + submodule/subsub/e.txt + EOF + + git ls-files --recurse-submodules "*.txt" >actual && + test_cmp expect actual +' + +test_expect_success '--recurse-submodules and pathspecs' ' + cat >expect <<-\EOF && + h.txt + submodule/f.TXT + submodule/g.txt + submodule/subsub/e.txt + EOF + + git ls-files --recurse-submodules ":(icase)*.txt" >actual && + test_cmp expect actual +' + +test_expect_success '--recurse-submodules and pathspecs' ' + cat >expect <<-\EOF && + h.txt + submodule/f.TXT + submodule/g.txt + EOF + + git ls-files --recurse-submodules ":(icase)*.txt" ":(exclude)submodule/subsub/*" >actual && + test_cmp expect actual +' + +test_expect_success '--recurse-submodules and pathspecs' ' + cat >expect <<-\EOF && + sub/file + EOF + + git ls-files --recurse-submodules "sub" >actual && + test_cmp expect actual && + git ls-files --recurse-submodules "sub/" >actual && + test_cmp expect actual && + git ls-files --recurse-submodules "sub/file" >actual && + test_cmp expect actual && + git ls-files --recurse-submodules "su*/file" >actual && + test_cmp expect actual && + git ls-files --recurse-submodules "su?/file" >actual && + test_cmp expect actual +' + +test_expect_success '--recurse-submodules and pathspecs' ' + cat >expect <<-\EOF && + sib/file + sub/file + EOF + + git ls-files --recurse-submodules "s??/file" >actual && + test_cmp expect actual && + git ls-files --recurse-submodules "s???file" >actual && + test_cmp expect actual && + git ls-files --recurse-submodules "s*file" >actual && + test_cmp expect actual +' + +test_expect_success '--recurse-submodules does not support --error-unmatch' ' + test_must_fail git ls-files --recurse-submodules --error-unmatch 2>actual && + test_i18ngrep "does not support --error-unmatch" actual +' + +test_incompatible_with_recurse_submodules () { + test_expect_success "--recurse-submodules and $1 are incompatible" " + test_must_fail git ls-files --recurse-submodules $1 2>actual && + test_i18ngrep 'unsupported mode' actual + " +} + +test_incompatible_with_recurse_submodules --deleted +test_incompatible_with_recurse_submodules --modified +test_incompatible_with_recurse_submodules --others +test_incompatible_with_recurse_submodules --stage +test_incompatible_with_recurse_submodules --killed +test_incompatible_with_recurse_submodules --unmerged + +test_done diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh index 470f33466c..9a893b5fe7 100755 --- a/t/t3030-merge-recursive.sh +++ b/t/t3030-merge-recursive.sh @@ -575,13 +575,13 @@ test_expect_success 'merge removes empty directories' ' test_must_fail test -d d ' -test_expect_failure 'merge-recursive simple w/submodule' ' +test_expect_success 'merge-recursive simple w/submodule' ' git checkout submod && git merge remove ' -test_expect_failure 'merge-recursive simple w/submodule result' ' +test_expect_success 'merge-recursive simple w/submodule result' ' git ls-files -s >actual && ( diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index c6a3ccba1b..5778c0afe1 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -89,6 +89,11 @@ test_expect_success 'git branch --list -v pattern shows branch summaries' ' awk "{print \$NF}" <tmp >actual && test_cmp expect actual ' +test_expect_success 'git branch --ignore-case --list -v pattern shows branch summaries' ' + git branch --list --ignore-case -v BRANCH* >tmp && + awk "{print \$NF}" <tmp >actual && + test_cmp expect actual +' test_expect_success 'git branch -v pattern does not show branch summaries' ' test_must_fail git branch -v branch* @@ -189,11 +194,49 @@ test_expect_success 'local-branch symrefs shortened properly' ' git symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one && cat >expect <<-\EOF && ref-to-branch -> branch-one - ref-to-remote -> refs/remotes/origin/branch-one + ref-to-remote -> origin/branch-one EOF git branch >actual.raw && grep ref-to <actual.raw >actual && test_cmp expect actual ' +test_expect_success 'sort branches, ignore case' ' + ( + git init sort-icase && + cd sort-icase && + test_commit initial && + git branch branch-one && + git branch BRANCH-two && + git branch --list | awk "{print \$NF}" >actual && + cat >expected <<-\EOF && + BRANCH-two + branch-one + master + EOF + test_cmp expected actual && + git branch --list -i | awk "{print \$NF}" >actual && + cat >expected <<-\EOF && + branch-one + BRANCH-two + master + EOF + test_cmp expected actual + ) +' + +test_expect_success 'git branch --format option' ' + cat >expect <<-\EOF && + Refname is (HEAD detached from fromtag) + Refname is refs/heads/ambiguous + Refname is refs/heads/branch-one + Refname is refs/heads/branch-two + Refname is refs/heads/master + Refname is refs/heads/ref-to-branch + Refname is refs/heads/ref-to-remote + EOF + git branch --format="Refname is %(refname)" >actual && + test_cmp expect actual +' + test_done diff --git a/t/t3310-notes-merge-manual-resolve.sh b/t/t3310-notes-merge-manual-resolve.sh index 6967436327..baef2d6924 100755 --- a/t/t3310-notes-merge-manual-resolve.sh +++ b/t/t3310-notes-merge-manual-resolve.sh @@ -225,7 +225,7 @@ test_expect_success 'cannot do merge w/conflicts when previous merge is unfinish test -d .git/NOTES_MERGE_WORKTREE && test_must_fail git notes merge z >output 2>&1 && # Output should indicate what is wrong - grep -q "\\.git/NOTES_MERGE_\\* exists" output + test_i18ngrep -q "\\.git/NOTES_MERGE_\\* exists" output ' # Setup non-conflicting merge between x and new notes ref w diff --git a/t/t3320-notes-merge-worktrees.sh b/t/t3320-notes-merge-worktrees.sh index 6e0511596b..b9c3bc2487 100755 --- a/t/t3320-notes-merge-worktrees.sh +++ b/t/t3320-notes-merge-worktrees.sh @@ -52,7 +52,7 @@ test_expect_success 'merge z into y while mid-merge in another workdir fails' ' cd worktree && git config core.notesRef refs/notes/y && test_must_fail git notes merge z 2>err && - test_i18ngrep "A notes merge into refs/notes/y is already in-progress at" err + test_i18ngrep "a notes merge into refs/notes/y is already in-progress at" err ) && test_path_is_missing .git/worktrees/worktree/NOTES_MERGE_REF ' diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index e38e296388..33d392ba11 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -237,6 +237,22 @@ test_expect_success 'retain authorship' ' git show HEAD | grep "^Author: Twerp Snog" ' +test_expect_success 'retain authorship w/ conflicts' ' + git reset --hard twerp && + test_commit a conflict a conflict-a && + git reset --hard twerp && + GIT_AUTHOR_NAME=AttributeMe \ + test_commit b conflict b conflict-b && + set_fake_editor && + test_must_fail git rebase -i conflict-a && + echo resolved >conflict && + git add conflict && + git rebase --continue && + test $(git rev-parse conflict-a^0) = $(git rev-parse HEAD^) && + git show >out && + grep AttributeMe out +' + test_expect_success 'squash' ' git reset --hard twerp && echo B > file7 && @@ -540,7 +556,7 @@ test_expect_success 'clean error after failed "exec"' ' echo "edited again" > file7 && git add file7 && test_must_fail git rebase --continue 2>error && - test_i18ngrep "You have staged changes in your working tree." error + test_i18ngrep "you have staged changes in your working tree" error ' test_expect_success 'rebase a detached HEAD' ' @@ -976,6 +992,17 @@ test_expect_success 'rebase -i respects core.commentchar' ' test B = $(git cat-file commit HEAD^ | sed -ne \$p) ' +test_expect_success 'rebase -i respects core.commentchar=auto' ' + test_config core.commentchar auto && + write_script copy-edit-script.sh <<-\EOF && + cp "$1" edit-script + EOF + test_set_editor "$(pwd)/copy-edit-script.sh" && + test_when_finished "git rebase --abort || :" && + git rebase -i HEAD^ && + test -z "$(grep -ve "^#" -e "^\$" -e "^pick" edit-script)" +' + test_expect_success 'rebase -i, with <onto> and <upstream> specified as :/quuxery' ' test_when_finished "git branch -D torebase" && git checkout -b torebase branch1 && diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh index a6a6c40a98..910f218284 100755 --- a/t/t3407-rebase-abort.sh +++ b/t/t3407-rebase-abort.sh @@ -99,4 +99,28 @@ testrebase() { testrebase "" .git/rebase-apply testrebase " --merge" .git/rebase-merge +test_expect_success 'rebase --quit' ' + cd "$work_dir" && + # Clean up the state from the previous one + git reset --hard pre-rebase && + test_must_fail git rebase master && + test_path_is_dir .git/rebase-apply && + head_before=$(git rev-parse HEAD) && + git rebase --quit && + test $(git rev-parse HEAD) = $head_before && + test ! -d .git/rebase-apply +' + +test_expect_success 'rebase --merge --quit' ' + cd "$work_dir" && + # Clean up the state from the previous one + git reset --hard pre-rebase && + test_must_fail git rebase --merge master && + test_path_is_dir .git/rebase-merge && + head_before=$(git rev-parse HEAD) && + git rebase --quit && + test $(git rev-parse HEAD) = $head_before && + test ! -d .git/rebase-merge +' + test_done diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh index d5b896d445..ebf4f5e4b2 100755 --- a/t/t3426-rebase-submodule.sh +++ b/t/t3426-rebase-submodule.sh @@ -38,9 +38,6 @@ git_rebase_interactive () { git rebase -i "$1" } -KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1 -# The real reason "replace directory with submodule" fails is because a -# directory "sub1" exists, but we reuse the suppression added for merge here test_submodule_switch "git_rebase_interactive" test_done diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh index 51f3bbb8af..4f2a263b63 100755 --- a/t/t3501-revert-cherry-pick.sh +++ b/t/t3501-revert-cherry-pick.sh @@ -96,7 +96,7 @@ test_expect_success 'revert forbidden on dirty working tree' ' echo content >extra_file && git add extra_file && test_must_fail git revert HEAD 2>errors && - test_i18ngrep "Your local changes would be overwritten by " errors + test_i18ngrep "your local changes would be overwritten by " errors ' @@ -141,4 +141,16 @@ test_expect_success 'cherry-pick "-" works with arguments' ' test_cmp expect actual ' +test_expect_success 'cherry-pick works with dirty renamed file' ' + test_commit to-rename && + git checkout -b unrelated && + test_commit unrelated && + git checkout @{-1} && + git mv to-rename.t renamed && + test_tick && + git commit -m renamed && + echo modified >renamed && + git cherry-pick refs/heads/unrelated +' + test_done diff --git a/t/t3510-cherry-pick-sequence.sh b/t/t3510-cherry-pick-sequence.sh index 7b7a89dbd5..0acf4b1461 100755 --- a/t/t3510-cherry-pick-sequence.sh +++ b/t/t3510-cherry-pick-sequence.sh @@ -147,6 +147,16 @@ test_expect_success '--abort to cancel single cherry-pick' ' git diff-index --exit-code HEAD ' +test_expect_success '--abort does not unsafely change HEAD' ' + pristine_detach initial && + test_must_fail git cherry-pick picked anotherpick && + git reset --hard base && + test_must_fail git cherry-pick picked anotherpick && + git cherry-pick --abort 2>actual && + test_i18ngrep "You seem to have moved HEAD" actual && + test_cmp_rev base HEAD +' + test_expect_success 'cherry-pick --abort to cancel multiple revert' ' pristine_detach anotherpick && test_expect_code 1 git revert base..picked && @@ -375,7 +385,7 @@ test_expect_success '--continue respects opts' ' git cat-file commit HEAD~1 >picked_msg && git cat-file commit HEAD~2 >unrelatedpick_msg && git cat-file commit HEAD~3 >initial_msg && - test_must_fail grep "cherry picked from" initial_msg && + ! grep "cherry picked from" initial_msg && grep "cherry picked from" unrelatedpick_msg && grep "cherry picked from" picked_msg && grep "cherry picked from" anotherpick_msg @@ -416,9 +426,9 @@ test_expect_failure '--signoff is automatically propagated to resolved conflict' git cat-file commit HEAD~1 >picked_msg && git cat-file commit HEAD~2 >unrelatedpick_msg && git cat-file commit HEAD~3 >initial_msg && - test_must_fail grep "Signed-off-by:" initial_msg && + ! grep "Signed-off-by:" initial_msg && grep "Signed-off-by:" unrelatedpick_msg && - test_must_fail grep "Signed-off-by:" picked_msg && + ! grep "Signed-off-by:" picked_msg && grep "Signed-off-by:" anotherpick_msg ' diff --git a/t/t3511-cherry-pick-x.sh b/t/t3511-cherry-pick-x.sh index 9cce5ae881..bf0a5c9887 100755 --- a/t/t3511-cherry-pick-x.sh +++ b/t/t3511-cherry-pick-x.sh @@ -25,9 +25,8 @@ 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>" +This is not recognized as a footer because Myfooter is not a recognized token. +Myfooter: A.U. Thor <author@example.com>" mesg_with_footer_sob="$mesg_with_footer Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" @@ -112,6 +111,17 @@ test_expect_success 'cherry-pick -s inserts blank line after non-conforming foot test_cmp expect actual ' +test_expect_success 'cherry-pick -s recognizes trailer config' ' + pristine_detach initial && + git -c "trailer.Myfooter.ifexists=add" 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) && diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh index d046d98aec..5aa6db584c 100755 --- a/t/t3600-rm.sh +++ b/t/t3600-rm.sh @@ -111,21 +111,21 @@ test_expect_success 'Remove nonexistent file with --ignore-unmatch' ' ' test_expect_success '"rm" command printed' ' - echo frotz > test-file && + echo frotz >test-file && git add test-file && git commit -m "add file for rm test" && - git rm test-file > rm-output && + git rm test-file >rm-output && test $(grep "^rm " rm-output | wc -l) = 1 && rm -f test-file rm-output && git commit -m "remove file from rm test" ' test_expect_success '"rm" command suppressed with --quiet' ' - echo frotz > test-file && + echo frotz >test-file && git add test-file && git commit -m "add file for rm --quiet test" && - git rm --quiet test-file > rm-output && - test $(wc -l < rm-output) = 0 && + git rm --quiet test-file >rm-output && + test_must_be_empty rm-output && rm -f test-file rm-output && git commit -m "remove file from rm --quiet test" ' @@ -221,7 +221,7 @@ test_expect_success 'Call "rm" from outside the work tree' ' mkdir repo && (cd repo && git init && - echo something > somefile && + echo something >somefile && git add somefile && git commit -m "add a file" && (cd .. && @@ -287,7 +287,7 @@ test_expect_success 'rm removes empty submodules from work tree' ' git commit -m "add submodule" && git rm submod && test ! -e submod && - git status -s -uno --ignore-submodules=none > actual && + 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 @@ -298,7 +298,7 @@ test_expect_success 'rm removes removed submodule from index and .gitmodules' ' git submodule update && rm -rf submod && git rm submod && - git status -s -uno --ignore-submodules=none > actual && + 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 @@ -309,7 +309,7 @@ test_expect_success 'rm removes work tree of unmodified submodules' ' git submodule update && git rm submod && test ! -d submod && - git status -s -uno --ignore-submodules=none > actual && + 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 @@ -320,7 +320,7 @@ test_expect_success 'rm removes a submodule with a trailing /' ' git submodule update && git rm submod/ && test ! -d submod && - git status -s -uno --ignore-submodules=none > actual && + git status -s -uno --ignore-submodules=none >actual && test_cmp expect actual ' @@ -335,17 +335,15 @@ test_expect_success 'rm succeeds when given a directory with a trailing /' ' 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^ - ) && + git -C submod checkout HEAD^ && test_must_fail git rm submod && test -d submod && test -f submod/.git && - git status -s -uno --ignore-submodules=none > actual && + 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 && + 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 @@ -418,34 +416,30 @@ test_expect_success 'rm issues a warning when section is not found in .gitmodule test_expect_success 'rm of a populated submodule with modifications fails unless forced' ' git reset --hard && git submodule update && - (cd submod && - echo X >empty - ) && + echo X >submod/empty && test_must_fail git rm submod && test -d submod && test -f submod/.git && - git status -s -uno --ignore-submodules=none > actual && + 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 && + 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 - ) && + echo X >submod/untracked && test_must_fail git rm submod && test -d submod && test -f submod/.git && - git status -s -uno --ignore-submodules=none > actual && + 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 && + git status -s -uno --ignore-submodules=none >actual && test_cmp expect actual ' @@ -461,16 +455,12 @@ test_expect_success 'setup submodule conflict' ' git add nitfol && git commit -m "added nitfol 2" && git checkout -b conflict1 master && - (cd submod && - git fetch && - git checkout branch1 - ) && + git -C submod fetch && + git -C submod checkout branch1 && git add submod && git commit -m "submod 1" && git checkout -b conflict2 master && - (cd submod && - git checkout branch2 - ) && + git -C submod checkout branch2 && git add submod && git commit -m "submod 2" ' @@ -486,7 +476,7 @@ test_expect_success 'rm removes work tree of unmodified conflicted submodule' ' test_must_fail git merge conflict2 && git rm submod && test ! -d submod && - git status -s -uno --ignore-submodules=none > actual && + git status -s -uno --ignore-submodules=none >actual && test_cmp expect actual ' @@ -494,18 +484,16 @@ test_expect_success 'rm of a conflicted populated submodule with different HEAD git checkout conflict1 && git reset --hard && git submodule update && - (cd submod && - git checkout HEAD^ - ) && + git -C submod 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 && + 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 && + 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 @@ -515,18 +503,16 @@ test_expect_success 'rm of a conflicted populated submodule with modifications f git checkout conflict1 && git reset --hard && git submodule update && - (cd submod && - echo X >empty - ) && + echo X >submod/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 && + 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 && + 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 @@ -536,18 +522,16 @@ test_expect_success 'rm of a conflicted populated submodule with untracked files git checkout conflict1 && git reset --hard && git submodule update && - (cd submod && - echo X >untracked - ) && + echo X >submod/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 && + 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 && + git status -s -uno --ignore-submodules=none >actual && test_cmp expect actual ' @@ -564,12 +548,12 @@ test_expect_success 'rm of a conflicted populated submodule with a .git director test_must_fail git rm submod && test -d submod && test -d submod/.git && - git status -s -uno --ignore-submodules=none > actual && + 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 && + git status -s -uno --ignore-submodules=none >actual && test_cmp expect.conflict actual && git merge --abort && rm -rf submod @@ -581,30 +565,26 @@ test_expect_success 'rm of a conflicted unpopulated submodule succeeds' ' test_must_fail git merge conflict2 && git rm submod && test ! -d submod && - git status -s -uno --ignore-submodules=none > actual && + 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' ' +test_expect_success 'rm of a populated submodule with a .git directory migrates git dir' ' 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 + GIT_WORK_TREE=. git config --unset core.worktree && + rm -r ../.git/modules/sub ) && - 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 + git rm submod 2>output.err && + ! test -d submod && + ! test -d submod/.git && + git status -s -uno --ignore-submodules=none >actual && + test -s actual && + test_i18ngrep Migrating output.err ' cat >expect.deepmodified <<EOF @@ -629,58 +609,52 @@ test_expect_success 'setup subsubmodule' ' 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 && + 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^ - ) && + git -C submod/subsubmod checkout HEAD^ && test_must_fail git rm submod && test -d submod && test -f submod/.git && - git status -s -uno --ignore-submodules=none > actual && + 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 && + 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 - ) && + echo X >submod/subsubmod/empty && test_must_fail git rm submod && test -d submod && test -f submod/.git && - git status -s -uno --ignore-submodules=none > actual && + 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 && + 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 - ) && + echo X >submod/subsubmod/untracked && test_must_fail git rm submod && test -d submod && test -f submod/.git && - git status -s -uno --ignore-submodules=none > actual && + 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 && + git status -s -uno --ignore-submodules=none >actual && test_cmp expect actual ' @@ -689,34 +663,29 @@ test_expect_success 'rm of a populated nested submodule with a nested .git direc git submodule update --recursive && (cd submod/subsubmod && rm .git && - cp -R ../../.git/modules/sub/modules/sub .git && + mv ../../.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 + git rm submod 2>output.err && + ! test -d submod && + ! test -d submod/subsubmod/.git && + git status -s -uno --ignore-submodules=none >actual && + test -s actual && + test_i18ngrep Migrating output.err ' test_expect_success 'checking out a commit after submodule removal needs manual updates' ' - git commit -m "submodule removal" submod && + git commit -m "submodule removal" submod .gitmodules && git checkout HEAD^ && git submodule update && - git checkout -q HEAD^ 2>actual && + git checkout -q HEAD^ && git checkout -q master 2>actual && test_i18ngrep "^warning: unable to rmdir submod:" actual && git status -s submod >actual && echo "?? submod/" >expected && test_cmp expected actual && rm -rf submod && - git status -s -uno --ignore-submodules=none > actual && + git status -s -uno --ignore-submodules=none >actual && ! test -s actual ' @@ -881,4 +850,9 @@ test_expect_success 'rm files with two different errors' ' test_i18ncmp expect actual ' +test_expect_success 'rm empty string should invoke warning' ' + git rm -rf "" 2>output && + test_i18ngrep "warning: empty strings" output +' + test_done diff --git a/t/t3700-add.sh b/t/t3700-add.sh index 53c0cb6dea..f3a4b4a913 100755 --- a/t/t3700-add.sh +++ b/t/t3700-add.sh @@ -331,6 +331,11 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file out test_i18ncmp expect.err actual.err ' +test_expect_success 'git add empty string should invoke warning' ' + git add "" 2>output && + test_i18ngrep "warning: empty strings" output +' + test_expect_success 'git add --chmod=[+-]x stages correctly' ' rm -f foo1 && echo foo >foo1 && diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index deae948c76..5ffe78e920 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -380,4 +380,18 @@ test_expect_success 'patch mode ignores unmerged entries' ' test_cmp expected diff ' +test_expect_success 'diffs can be colorized' ' + git reset --hard && + + # force color even though the test script has no terminal + test_config color.ui always && + + echo content >test && + printf y | git add -p >output 2>&1 && + + # We do not want to depend on the exact coloring scheme + # git uses for diffs, so just check that we saw some kind of color. + grep "$(printf "\\033")" output +' + test_done diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh index 4bf1dbe9c9..3b94283e35 100755 --- a/t/t3900-i18n-commit.sh +++ b/t/t3900-i18n-commit.sh @@ -45,7 +45,7 @@ test_expect_success 'UTF-8 invalid characters refused' ' 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_i18ngrep "did not conform" "$HOME"/stderr ' test_expect_success 'UTF-8 overlong sequences rejected' ' @@ -55,7 +55,7 @@ test_expect_success 'UTF-8 overlong sequences rejected' ' 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_i18ngrep "did not conform" "$HOME"/stderr ' test_expect_success 'UTF-8 non-characters refused' ' @@ -64,7 +64,7 @@ test_expect_success 'UTF-8 non-characters refused' ' 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_i18ngrep "did not conform" "$HOME"/stderr ' test_expect_success 'UTF-8 non-characters refused' ' @@ -73,7 +73,7 @@ test_expect_success 'UTF-8 non-characters refused' ' 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 + test_i18ngrep "did not conform" "$HOME"/stderr ' for H in ISO8859-1 eucJP ISO-2022-JP diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh index 509084e1a7..f663d567c8 100755 --- a/t/t3901-i18n-patch.sh +++ b/t/t3901-i18n-patch.sh @@ -295,7 +295,7 @@ test_expect_success 'am --no-utf8 (U/L)' ' # commit-tree will warn that the commit message does not contain valid UTF-8 # as mailinfo did not convert it - grep "did not conform" err && + test_i18ngrep "did not conform" err && check_encoding 2 ' diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh index 2142c1fa92..2de3e18ce6 100755 --- a/t/t3903-stash.sh +++ b/t/t3903-stash.sh @@ -131,6 +131,26 @@ test_expect_success 'drop middle stash' ' test 1 = $(git show HEAD:file) ' +test_expect_success 'drop middle stash by index' ' + git reset --hard && + echo 8 >file && + git stash && + echo 9 >file && + git stash && + git stash drop 1 && + test 2 = $(git stash list | wc -l) && + git stash apply && + test 9 = $(cat file) && + test 1 = $(git show :file) && + test 1 = $(git show HEAD:file) && + git reset --hard && + git stash drop && + git stash apply && + test 3 = $(cat file) && + test 1 = $(git show :file) && + test 1 = $(git show HEAD:file) +' + test_expect_success 'stash pop' ' git reset --hard && git stash pop && @@ -604,6 +624,21 @@ test_expect_success 'invalid ref of the form stash@{n}, n >= N' ' git stash drop ' +test_expect_success 'invalid ref of the form "n", n >= N' ' + git stash clear && + test_must_fail git stash drop 0 && + echo bar5 >file && + echo bar6 >file2 && + git add file2 && + git stash && + test_must_fail git stash drop 1 && + test_must_fail git stash pop 1 && + test_must_fail git stash apply 1 && + test_must_fail git stash show 1 && + test_must_fail git stash branch tmp 1 && + git stash drop +' + test_expect_success 'stash branch should not drop the stash if the branch exists' ' git stash clear && echo foo >file && @@ -731,4 +766,13 @@ test_expect_success 'stash list --cc shows combined diff' ' test_cmp expect actual ' +test_expect_success 'stash is not confused by partial renames' ' + mv file renamed && + git add renamed && + git stash && + git stash apply && + test_path_is_file renamed && + test_path_is_missing file +' + test_done diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh index 643d729157..0a8af76aab 100755 --- a/t/t4012-diff-binary.sh +++ b/t/t4012-diff-binary.sh @@ -68,7 +68,7 @@ test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' ' 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=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") && detected=$(sed -ne "${detected}p" broken) && test "$detected" = xCIT ' @@ -77,7 +77,7 @@ 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=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") && detected=$(sed -ne "${detected}p" broken) && test "$detected" = xCIT ' diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh index 94ef5000e7..d09acfe48e 100755 --- a/t/t4013-diff-various.sh +++ b/t/t4013-diff-various.sh @@ -306,9 +306,18 @@ diff --no-index --name-status dir2 dir diff --no-index --name-status -- dir2 dir diff --no-index dir dir3 diff master master^ side +# Can't use spaces... +diff --line-prefix=abc master master^ side diff --dirstat master~1 master~2 diff --dirstat initial rearrange diff --dirstat-by-file initial rearrange +# No-index --abbrev and --no-abbrev +diff --raw initial +diff --raw --abbrev=4 initial +diff --raw --no-abbrev initial +diff --no-index --raw dir2 dir +diff --no-index --raw --abbrev=4 dir2 dir +diff --no-index --raw --no-abbrev dir2 dir EOF test_expect_success 'log -S requires an argument' ' @@ -325,6 +334,10 @@ test_expect_success 'diff --cached -- file on unborn branch' ' git diff --cached -- file0 >result && test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--cached_--_file0" result ' +test_expect_success 'diff --line-prefix with spaces' ' + git diff --line-prefix="| | | " --cached -- file0 >result && + test_cmp "$TEST_DIRECTORY/t4013/diff.diff_--line-prefix_--cached_--_file0" result +' test_expect_success 'diff-tree --stdin with log formatting' ' cat >expect <<-\EOF && diff --git a/t/t4013/diff.diff_--line-prefix=abc_master_master^_side b/t/t4013/diff.diff_--line-prefix=abc_master_master^_side new file mode 100644 index 0000000000..99f91e7f0e --- /dev/null +++ b/t/t4013/diff.diff_--line-prefix=abc_master_master^_side @@ -0,0 +1,29 @@ +$ git diff --line-prefix=abc master master^ side +abcdiff --cc dir/sub +abcindex cead32e,7289e35..992913c +abc--- a/dir/sub +abc+++ b/dir/sub +abc@@@ -1,6 -1,4 +1,8 @@@ +abc A +abc B +abc +C +abc +D +abc +E +abc +F +abc+ 1 +abc+ 2 +abcdiff --cc file0 +abcindex b414108,f4615da..10a8a9f +abc--- a/file0 +abc+++ b/file0 +abc@@@ -1,6 -1,6 +1,9 @@@ +abc 1 +abc 2 +abc 3 +abc +4 +abc +5 +abc +6 +abc+ A +abc+ B +abc+ C +$ diff --git a/t/t4013/diff.diff_--line-prefix_--cached_--_file0 b/t/t4013/diff.diff_--line-prefix_--cached_--_file0 new file mode 100644 index 0000000000..f41ba4d36a --- /dev/null +++ b/t/t4013/diff.diff_--line-prefix_--cached_--_file0 @@ -0,0 +1,15 @@ +| | | diff --git a/file0 b/file0 +| | | new file mode 100644 +| | | index 0000000..10a8a9f +| | | --- /dev/null +| | | +++ b/file0 +| | | @@ -0,0 +1,9 @@ +| | | +1 +| | | +2 +| | | +3 +| | | +4 +| | | +5 +| | | +6 +| | | +A +| | | +B +| | | +C diff --git a/t/t4013/diff.diff_--no-index_--raw_--abbrev=4_dir2_dir b/t/t4013/diff.diff_--no-index_--raw_--abbrev=4_dir2_dir new file mode 100644 index 0000000000..a71b38a833 --- /dev/null +++ b/t/t4013/diff.diff_--no-index_--raw_--abbrev=4_dir2_dir @@ -0,0 +1,3 @@ +$ git diff --no-index --raw --abbrev=4 dir2 dir +:000000 100644 0000... 0000... A dir/sub +$ diff --git a/t/t4013/diff.diff_--no-index_--raw_--no-abbrev_dir2_dir b/t/t4013/diff.diff_--no-index_--raw_--no-abbrev_dir2_dir new file mode 100644 index 0000000000..e0f00977c8 --- /dev/null +++ b/t/t4013/diff.diff_--no-index_--raw_--no-abbrev_dir2_dir @@ -0,0 +1,3 @@ +$ git diff --no-index --raw --no-abbrev dir2 dir +:000000 100644 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 A dir/sub +$ diff --git a/t/t4013/diff.diff_--no-index_--raw_dir2_dir b/t/t4013/diff.diff_--no-index_--raw_dir2_dir new file mode 100644 index 0000000000..3cb4ee7a9a --- /dev/null +++ b/t/t4013/diff.diff_--no-index_--raw_dir2_dir @@ -0,0 +1,3 @@ +$ git diff --no-index --raw dir2 dir +:000000 100644 0000000... 0000000... A dir/sub +$ diff --git a/t/t4013/diff.diff_--raw_--abbrev=4_initial b/t/t4013/diff.diff_--raw_--abbrev=4_initial new file mode 100644 index 0000000000..c3641db31d --- /dev/null +++ b/t/t4013/diff.diff_--raw_--abbrev=4_initial @@ -0,0 +1,6 @@ +$ git diff --raw --abbrev=4 initial +:100644 100644 35d2... 9929... M dir/sub +:100644 100644 01e7... 10a8... M file0 +:000000 100644 0000... b1e6... A file1 +:100644 000000 01e7... 0000... D file2 +$ diff --git a/t/t4013/diff.diff_--raw_--no-abbrev_initial b/t/t4013/diff.diff_--raw_--no-abbrev_initial new file mode 100644 index 0000000000..c87a1258e3 --- /dev/null +++ b/t/t4013/diff.diff_--raw_--no-abbrev_initial @@ -0,0 +1,6 @@ +$ git diff --raw --no-abbrev initial +:100644 100644 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e 992913c5aa0a5476d10c49ed0f21fc0c6d1aedf3 M dir/sub +:100644 100644 01e79c32a8c99c557f0757da7cb6d65b3414466d 10a8a9f3657f91a156b9f0184ed79a20adef9f7f M file0 +:000000 100644 0000000000000000000000000000000000000000 b1e67221afe8461efd244b487afca22d46b95eb8 A file1 +:100644 000000 01e79c32a8c99c557f0757da7cb6d65b3414466d 0000000000000000000000000000000000000000 D file2 +$ diff --git a/t/t4013/diff.diff_--raw_initial b/t/t4013/diff.diff_--raw_initial new file mode 100644 index 0000000000..a3e978040d --- /dev/null +++ b/t/t4013/diff.diff_--raw_initial @@ -0,0 +1,6 @@ +$ git diff --raw initial +:100644 100644 35d242b... 992913c... M dir/sub +:100644 100644 01e79c3... 10a8a9f... M file0 +:000000 100644 0000000... b1e6722... A file1 +:100644 000000 01e79c3... 0000000... D file2 +$ diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 8d90a6e500..482112ca33 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -1086,6 +1086,15 @@ test_expect_success 'empty subject prefix does not have extra space' ' test_cmp expect actual ' +test_expect_success '--rfc' ' + cat >expect <<-\EOF && + Subject: [RFC PATCH 1/1] header with . in it + EOF + git format-patch -n -1 --stdout --rfc >patch && + grep ^Subject: patch >actual && + test_cmp expect actual +' + test_expect_success '--from=ident notices bogus ident' ' test_must_fail git format-patch -1 --stdout --from=foo >patch ' @@ -1285,8 +1294,7 @@ 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> +11:Signed-off-by: C O Mitter <committer@example.com> EOF test_cmp expected actual ' @@ -1359,7 +1367,7 @@ EOF test_cmp expected actual ' -test_expect_success 'signoff: detect garbage in non-conforming footer' ' +test_expect_success 'signoff: tolerate garbage in conforming footer' ' append_signoff <<\EOF >actual && subject @@ -1374,8 +1382,36 @@ EOF 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: respect trailer config' ' + append_signoff <<\EOF >actual && +subject + +Myfooter: x +Some Trash +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_config trailer.Myfooter.ifexists add && + append_signoff <<\EOF >actual && +subject + +Myfooter: x +Some Trash +EOF + cat >expected <<\EOF && +4:Subject: [PATCH] subject +8: +11:Signed-off-by: C O Mitter <committer@example.com> EOF test_cmp expected actual ' diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh index 2434157aa7..289806d0c7 100755 --- a/t/t4015-diff-whitespace.sh +++ b/t/t4015-diff-whitespace.sh @@ -869,7 +869,8 @@ test_expect_success 'diff that introduces and removes ws breakages' ' test_cmp expected current ' -test_expect_success 'the same with --ws-error-highlight' ' +test_expect_success 'ws-error-highlight test setup' ' + git reset --hard && { echo "0. blank-at-eol " && @@ -882,10 +883,7 @@ test_expect_success 'the same with --ws-error-highlight' ' echo "2. and a new line " } >x && - git -c color.diff=always diff --ws-error-highlight=default,old | - test_decode_color >current && - - cat >expected <<-\EOF && + cat >expect.default-old <<-\EOF && <BOLD>diff --git a/x b/x<RESET> <BOLD>index d0233a2..700886e 100644<RESET> <BOLD>--- a/x<RESET> @@ -897,12 +895,7 @@ test_expect_success 'the same with --ws-error-highlight' ' <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET> EOF - test_cmp expected current && - - git -c color.diff=always diff --ws-error-highlight=all | - test_decode_color >current && - - cat >expected <<-\EOF && + cat >expect.all <<-\EOF && <BOLD>diff --git a/x b/x<RESET> <BOLD>index d0233a2..700886e 100644<RESET> <BOLD>--- a/x<RESET> @@ -914,12 +907,7 @@ test_expect_success 'the same with --ws-error-highlight' ' <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET> EOF - test_cmp expected current && - - git -c color.diff=always diff --ws-error-highlight=none | - test_decode_color >current && - - cat >expected <<-\EOF && + cat >expect.none <<-\EOF <BOLD>diff --git a/x b/x<RESET> <BOLD>index d0233a2..700886e 100644<RESET> <BOLD>--- a/x<RESET> @@ -931,7 +919,57 @@ test_expect_success 'the same with --ws-error-highlight' ' <GREEN>+2. and a new line <RESET> EOF - test_cmp expected current +' + +test_expect_success 'test --ws-error-highlight option' ' + + git -c color.diff=always diff --ws-error-highlight=default,old | + test_decode_color >current && + test_cmp expect.default-old current && + + git -c color.diff=always diff --ws-error-highlight=all | + test_decode_color >current && + test_cmp expect.all current && + + git -c color.diff=always diff --ws-error-highlight=none | + test_decode_color >current && + test_cmp expect.none current + +' + +test_expect_success 'test diff.wsErrorHighlight config' ' + + git -c color.diff=always -c diff.wsErrorHighlight=default,old diff | + test_decode_color >current && + test_cmp expect.default-old current && + + git -c color.diff=always -c diff.wsErrorHighlight=all diff | + test_decode_color >current && + test_cmp expect.all current && + + git -c color.diff=always -c diff.wsErrorHighlight=none diff | + test_decode_color >current && + test_cmp expect.none current + +' + +test_expect_success 'option overrides diff.wsErrorHighlight' ' + + git -c color.diff=always -c diff.wsErrorHighlight=none \ + diff --ws-error-highlight=default,old | + test_decode_color >current && + test_cmp expect.default-old current && + + git -c color.diff=always -c diff.wsErrorHighlight=default \ + diff --ws-error-highlight=all | + test_decode_color >current && + test_cmp expect.all current && + + git -c color.diff=always -c diff.wsErrorHighlight=all \ + diff --ws-error-highlight=none | + test_decode_color >current && + test_cmp expect.none current + ' test_done diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh index 886494b58f..9be65fd444 100755 --- a/t/t4021-format-patch-numbered.sh +++ b/t/t4021-format-patch-numbered.sh @@ -36,6 +36,11 @@ test_no_numbered() { test_num_no_numbered $1 2 } +test_single_cover_letter_numbered() { + grep "^Subject: \[PATCH 0/1\]" $1 && + grep "^Subject: \[PATCH 1/1\]" $1 +} + test_single_numbered() { grep "^Subject: \[PATCH 1/1\]" $1 } @@ -121,4 +126,16 @@ test_expect_success '--start-number && --numbered' ' grep "^Subject: \[PATCH 3/3\]" patch8 ' +test_expect_success 'single patch with cover-letter defaults to numbers' ' + git format-patch --cover-letter --stdout HEAD~1 >patch9.single && + test_single_cover_letter_numbered patch9.single +' + +test_expect_success 'Use --no-numbered and --cover-letter single patch' ' + git format-patch --no-numbered --stdout --cover-letter HEAD~1 >patch10 && + test_no_numbered patch10 +' + + + test_done diff --git a/t/t4026-color.sh b/t/t4026-color.sh index ec78c5e3ac..671e951ee5 100755 --- a/t/t4026-color.sh +++ b/t/t4026-color.sh @@ -6,10 +6,11 @@ test_description='Test diff/status color escape codes' . ./test-lib.sh +ESC=$(printf '\033') color() { actual=$(git config --get-color no.such.slot "$1") && - test "$actual" = "$2" + test "$actual" = "${2:+$ESC}$2" } invalid_color() @@ -21,6 +22,10 @@ test_expect_success 'reset' ' color "reset" "[m" ' +test_expect_success 'empty color is empty' ' + color "" "" +' + test_expect_success 'attribute before color name' ' color "bold red" "[1;31m" ' diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh index e4e3e28fc7..bada0cbd32 100755 --- a/t/t4032-diff-inter-hunk-context.sh +++ b/t/t4032-diff-inter-hunk-context.sh @@ -16,11 +16,15 @@ f() { } t() { + use_config= + git config --unset diff.interHunkContext + case $# in 4) hunks=$4; cmd="diff -U$3";; 5) hunks=$5; cmd="diff -U$3 --inter-hunk-context=$4";; + 6) hunks=$5; cmd="diff -U$3"; git config diff.interHunkContext $4; use_config="(diff.interHunkContext=$4) ";; esac - label="$cmd, $1 common $2" + label="$use_config$cmd, $1 common $2" file=f$1 expected=expected.$file.$3.$hunks @@ -89,4 +93,25 @@ t 9 lines 3 2 t 9 lines 3 2 2 t 9 lines 3 3 1 +# use diff.interHunkContext? +t 1 line 0 0 2 config +t 1 line 0 1 1 config +t 1 line 0 2 1 config +t 9 lines 3 3 1 config +t 2 lines 0 0 2 config +t 2 lines 0 1 2 config +t 2 lines 0 2 1 config +t 3 lines 1 0 2 config +t 3 lines 1 1 1 config +t 3 lines 1 2 1 config +t 9 lines 3 2 2 config +t 9 lines 3 3 1 config + +test_expect_success 'diff.interHunkContext invalid' ' + git config diff.interHunkContext asdf && + test_must_fail git diff && + git config diff.interHunkContext -1 && + test_must_fail git diff +' + test_done diff --git a/t/t4053-diff-no-index.sh b/t/t4053-diff-no-index.sh index 6eb83211b5..453e6c35eb 100755 --- a/t/t4053-diff-no-index.sh +++ b/t/t4053-diff-no-index.sh @@ -89,4 +89,42 @@ test_expect_success 'turning a file into a directory' ' ) ' +test_expect_success 'diff from repo subdir shows real paths (explicit)' ' + echo "diff --git a/../../non/git/a b/../../non/git/b" >expect && + test_expect_code 1 \ + git -C repo/sub \ + diff --no-index ../../non/git/a ../../non/git/b >actual && + head -n 1 <actual >actual.head && + test_cmp expect actual.head +' + +test_expect_success 'diff from repo subdir shows real paths (implicit)' ' + echo "diff --git a/../../non/git/a b/../../non/git/b" >expect && + test_expect_code 1 \ + git -C repo/sub \ + diff ../../non/git/a ../../non/git/b >actual && + head -n 1 <actual >actual.head && + test_cmp expect actual.head +' + +test_expect_success 'diff --no-index from repo subdir respects config (explicit)' ' + echo "diff --git ../../non/git/a ../../non/git/b" >expect && + test_config -C repo diff.noprefix true && + test_expect_code 1 \ + git -C repo/sub \ + diff --no-index ../../non/git/a ../../non/git/b >actual && + head -n 1 <actual >actual.head && + test_cmp expect actual.head +' + +test_expect_success 'diff --no-index from repo subdir respects config (implicit)' ' + echo "diff --git ../../non/git/a ../../non/git/b" >expect && + test_config -C repo diff.noprefix true && + test_expect_code 1 \ + git -C repo/sub \ + diff ../../non/git/a ../../non/git/b >actual && + head -n 1 <actual >actual.head && + test_cmp expect actual.head +' + test_done diff --git a/t/t4059-diff-submodule-not-initialized.sh b/t/t4059-diff-submodule-not-initialized.sh new file mode 100755 index 0000000000..cd70fd5192 --- /dev/null +++ b/t/t4059-diff-submodule-not-initialized.sh @@ -0,0 +1,127 @@ +#!/bin/sh +# +# Copyright (c) 2016 Jacob Keller, based on t4041 by Jens Lehmann +# + +test_description='Test for submodule diff on non-checked out submodule + +This test tries to verify that add_submodule_odb works when the submodule was +initialized previously but the checkout has since been removed. +' + +. ./test-lib.sh + +# Tested non-UTF-8 encoding +test_encoding="ISO8859-1" + +# 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 () { + ( + cd "$1" && + shift && + for name + do + echo "$name" >"$name" && + git add "$name" && + test_tick && + # "git commit -m" would break MinGW, as Windows refuse to pass + # $test_encoding encoded parameter to git. + echo "Add $name ($added $name)" | iconv -f utf-8 -t $test_encoding | + git -c "i18n.commitEncoding=$test_encoding" commit -F - + done >/dev/null && + git rev-parse --short --verify HEAD + ) +} + +commit_file () { + test_tick && + git commit "$@" -m "Commit $*" >/dev/null +} + +test_expect_success 'setup - submodules' ' + test_create_repo sm2 && + add_file . foo && + add_file sm2 foo1 foo2 && + smhead1=$(git -C sm2 rev-parse --short --verify HEAD) +' + +test_expect_success 'setup - git submodule add' ' + git submodule add ./sm2 sm1 && + commit_file sm1 .gitmodules && + git diff-tree -p --no-commit-id --submodule=log HEAD -- sm1 >actual && + cat >expected <<-EOF && + Submodule sm1 0000000...$smhead1 (new submodule) + EOF + test_cmp expected actual +' + +test_expect_success 'submodule directory removed' ' + rm -rf sm1 && + git diff-tree -p --no-commit-id --submodule=log HEAD -- sm1 >actual && + cat >expected <<-EOF && + Submodule sm1 0000000...$smhead1 (new submodule) + EOF + test_cmp expected actual +' + +test_expect_success 'setup - submodule multiple commits' ' + git submodule update --checkout sm1 && + smhead2=$(add_file sm1 foo3 foo4) && + commit_file sm1 && + git diff-tree -p --no-commit-id --submodule=log HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 $smhead1..$smhead2: + > Add foo4 ($added foo4) + > Add foo3 ($added foo3) + EOF + test_cmp expected actual +' + +test_expect_success 'submodule removed multiple commits' ' + rm -rf sm1 && + git diff-tree -p --no-commit-id --submodule=log HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 $smhead1..$smhead2: + > Add foo4 ($added foo4) + > Add foo3 ($added foo3) + EOF + test_cmp expected actual +' + +test_expect_success 'submodule not initialized in new clone' ' + git clone . sm3 && + git -C sm3 diff-tree -p --no-commit-id --submodule=log HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 $smhead1...$smhead2 (not initialized) + EOF + test_cmp expected actual +' + +test_expect_success 'setup submodule moved' ' + git submodule update --checkout sm1 && + git mv sm1 sm4 && + commit_file sm4 && + git diff-tree -p --no-commit-id --submodule=log HEAD >actual && + cat >expected <<-EOF && + Submodule sm4 0000000...$smhead2 (new submodule) + EOF + test_cmp expected actual +' + +test_expect_success 'submodule moved then removed' ' + smhead3=$(add_file sm4 foo6 foo7) && + commit_file sm4 && + rm -rf sm4 && + git diff-tree -p --no-commit-id --submodule=log HEAD >actual && + cat >expected <<-EOF && + Submodule sm4 $smhead2..$smhead3: + > Add foo7 ($added foo7) + > Add foo6 ($added foo6) + EOF + test_cmp expected actual +' + +test_done diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh new file mode 100755 index 0000000000..7e23b55ea4 --- /dev/null +++ b/t/t4060-diff-submodule-option-diff-format.sh @@ -0,0 +1,749 @@ +#!/bin/sh +# +# Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin +# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests) +# Copyright (c) 2016 Jacob Keller (copy + convert to --submodule=diff) +# + +test_description='Support for diff format verbose submodule difference in git diff + +This test tries to verify the sanity of --submodule=diff option of git diff. +' + +. ./test-lib.sh + +# Tested non-UTF-8 encoding +test_encoding="ISO8859-1" + +# 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 () { + ( + cd "$1" && + shift && + for name + do + echo "$name" >"$name" && + git add "$name" && + test_tick && + # "git commit -m" would break MinGW, as Windows refuse to pass + # $test_encoding encoded parameter to git. + echo "Add $name ($added $name)" | iconv -f utf-8 -t $test_encoding | + git -c "i18n.commitEncoding=$test_encoding" commit -F - + done >/dev/null && + git rev-parse --short --verify HEAD + ) +} + +commit_file () { + test_tick && + git commit "$@" -m "Commit $*" >/dev/null +} + +test_expect_success 'setup repository' ' + test_create_repo sm1 && + add_file . foo && + head1=$(add_file sm1 foo1 foo2) && + fullhead1=$(git -C sm1 rev-parse --verify HEAD) +' + +test_expect_success 'added submodule' ' + git add sm1 && + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 0000000...$head1 (new submodule) + diff --git a/sm1/foo1 b/sm1/foo1 + new file mode 100644 + index 0000000..1715acd + --- /dev/null + +++ b/sm1/foo1 + @@ -0,0 +1 @@ + +foo1 + diff --git a/sm1/foo2 b/sm1/foo2 + new file mode 100644 + index 0000000..54b060e + --- /dev/null + +++ b/sm1/foo2 + @@ -0,0 +1 @@ + +foo2 + EOF + test_cmp expected actual +' + +test_expect_success 'added submodule, set diff.submodule' ' + test_config diff.submodule log && + git add sm1 && + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 0000000...$head1 (new submodule) + diff --git a/sm1/foo1 b/sm1/foo1 + new file mode 100644 + index 0000000..1715acd + --- /dev/null + +++ b/sm1/foo1 + @@ -0,0 +1 @@ + +foo1 + diff --git a/sm1/foo2 b/sm1/foo2 + new file mode 100644 + index 0000000..54b060e + --- /dev/null + +++ b/sm1/foo2 + @@ -0,0 +1 @@ + +foo2 + EOF + 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)' ' + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 $head1..$head2: + diff --git a/sm1/foo3 b/sm1/foo3 + new file mode 100644 + index 0000000..c1ec6c6 + --- /dev/null + +++ b/sm1/foo3 + @@ -0,0 +1 @@ + +foo3 + EOF + test_cmp expected actual +' + +test_expect_success 'modified submodule(forward)' ' + git diff --submodule=diff >actual && + cat >expected <<-EOF && + Submodule sm1 $head1..$head2: + diff --git a/sm1/foo3 b/sm1/foo3 + new file mode 100644 + index 0000000..c1ec6c6 + --- /dev/null + +++ b/sm1/foo3 + @@ -0,0 +1 @@ + +foo3 + EOF + test_cmp expected actual +' + +test_expect_success 'modified submodule(forward) --submodule' ' + git diff --submodule >actual && + cat >expected <<-EOF && + Submodule sm1 $head1..$head2: + > Add foo3 ($added foo3) + EOF + test_cmp expected actual +' + +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 + test_cmp expected actual +' + +commit_file sm1 && +head3=$( + cd sm1 && + git reset --hard HEAD~2 >/dev/null && + git rev-parse --short --verify HEAD +) + +test_expect_success 'modified submodule(backward)' ' + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 $head2..$head3 (rewind): + diff --git a/sm1/foo2 b/sm1/foo2 + deleted file mode 100644 + index 54b060e..0000000 + --- a/sm1/foo2 + +++ /dev/null + @@ -1 +0,0 @@ + -foo2 + diff --git a/sm1/foo3 b/sm1/foo3 + deleted file mode 100644 + index c1ec6c6..0000000 + --- a/sm1/foo3 + +++ /dev/null + @@ -1 +0,0 @@ + -foo3 + EOF + test_cmp expected actual +' + +head4=$(add_file sm1 foo4 foo5) +test_expect_success 'modified submodule(backward and forward)' ' + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 $head2...$head4: + diff --git a/sm1/foo2 b/sm1/foo2 + deleted file mode 100644 + index 54b060e..0000000 + --- a/sm1/foo2 + +++ /dev/null + @@ -1 +0,0 @@ + -foo2 + diff --git a/sm1/foo3 b/sm1/foo3 + deleted file mode 100644 + index c1ec6c6..0000000 + --- a/sm1/foo3 + +++ /dev/null + @@ -1 +0,0 @@ + -foo3 + diff --git a/sm1/foo4 b/sm1/foo4 + new file mode 100644 + index 0000000..a0016db + --- /dev/null + +++ b/sm1/foo4 + @@ -0,0 +1 @@ + +foo4 + diff --git a/sm1/foo5 b/sm1/foo5 + new file mode 100644 + index 0000000..d6f2413 + --- /dev/null + +++ b/sm1/foo5 + @@ -0,0 +1 @@ + +foo5 + EOF + test_cmp expected actual +' + +commit_file sm1 && +mv sm1 sm1-bak && +echo sm1 >sm1 && +head5=$(git hash-object sm1 | cut -c1-7) && +git add sm1 && +rm -f sm1 && +mv sm1-bak sm1 + +test_expect_success 'typechanged submodule(submodule->blob), --cached' ' + git diff --submodule=diff --cached >actual && + cat >expected <<-EOF && + Submodule sm1 $head4...0000000 (submodule deleted) + diff --git a/sm1/foo1 b/sm1/foo1 + deleted file mode 100644 + index 1715acd..0000000 + --- a/sm1/foo1 + +++ /dev/null + @@ -1 +0,0 @@ + -foo1 + diff --git a/sm1/foo4 b/sm1/foo4 + deleted file mode 100644 + index a0016db..0000000 + --- a/sm1/foo4 + +++ /dev/null + @@ -1 +0,0 @@ + -foo4 + diff --git a/sm1/foo5 b/sm1/foo5 + deleted file mode 100644 + index d6f2413..0000000 + --- a/sm1/foo5 + +++ /dev/null + @@ -1 +0,0 @@ + -foo5 + diff --git a/sm1 b/sm1 + new file mode 100644 + index 0000000..9da5fb8 + --- /dev/null + +++ b/sm1 + @@ -0,0 +1 @@ + +sm1 + EOF + test_cmp expected actual +' + +test_expect_success 'typechanged submodule(submodule->blob)' ' + git diff --submodule=diff >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) + diff --git a/sm1/foo1 b/sm1/foo1 + new file mode 100644 + index 0000000..1715acd + --- /dev/null + +++ b/sm1/foo1 + @@ -0,0 +1 @@ + +foo1 + diff --git a/sm1/foo4 b/sm1/foo4 + new file mode 100644 + index 0000000..a0016db + --- /dev/null + +++ b/sm1/foo4 + @@ -0,0 +1 @@ + +foo4 + diff --git a/sm1/foo5 b/sm1/foo5 + new file mode 100644 + index 0000000..d6f2413 + --- /dev/null + +++ b/sm1/foo5 + @@ -0,0 +1 @@ + +foo5 + EOF + test_cmp expected actual +' + +rm -rf sm1 && +git checkout-index sm1 +test_expect_success 'typechanged submodule(submodule->blob)' ' + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 $head4...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 + test_cmp expected actual +' + +rm -f sm1 && +test_create_repo sm1 && +head6=$(add_file sm1 foo6 foo7) +fullhead6=$(cd sm1; git rev-parse --verify HEAD) +test_expect_success 'nonexistent commit' ' + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 $head4...$head6 (commits not present) + EOF + test_cmp expected actual +' + +commit_file +test_expect_success 'typechanged submodule(blob->submodule)' ' + git diff-index -p --submodule=diff HEAD >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...$head6 (new submodule) + diff --git a/sm1/foo6 b/sm1/foo6 + new file mode 100644 + index 0000000..462398b + --- /dev/null + +++ b/sm1/foo6 + @@ -0,0 +1 @@ + +foo6 + diff --git a/sm1/foo7 b/sm1/foo7 + new file mode 100644 + index 0000000..6e9262c + --- /dev/null + +++ b/sm1/foo7 + @@ -0,0 +1 @@ + +foo7 + EOF + test_cmp expected actual +' + +commit_file sm1 && +test_expect_success 'submodule is up to date' ' + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + EOF + test_cmp expected actual +' + +test_expect_success 'submodule contains untracked content' ' + echo new > sm1/new-file && + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 contains untracked content + EOF + test_cmp expected actual +' + +test_expect_success 'submodule contains untracked content (untracked ignored)' ' + git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual && + ! test -s actual +' + +test_expect_success 'submodule contains untracked content (dirty ignored)' ' + git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual && + ! test -s actual +' + +test_expect_success 'submodule contains untracked content (all ignored)' ' + git diff-index -p --ignore-submodules=all --submodule=diff HEAD >actual && + ! test -s actual +' + +test_expect_success 'submodule contains untracked and modified content' ' + echo new > sm1/foo6 && + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 contains untracked content + Submodule sm1 contains modified content + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..3e75765 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1 @@ + -foo6 + +new + EOF + test_cmp expected actual +' + +# NOT OK +test_expect_success 'submodule contains untracked and modified content (untracked ignored)' ' + echo new > sm1/foo6 && + git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 contains modified content + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..3e75765 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1 @@ + -foo6 + +new + EOF + test_cmp expected actual +' + +test_expect_success 'submodule contains untracked and modified content (dirty ignored)' ' + echo new > sm1/foo6 && + git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual && + ! test -s actual +' + +test_expect_success 'submodule contains untracked and modified content (all ignored)' ' + echo new > sm1/foo6 && + git diff-index -p --ignore-submodules --submodule=diff HEAD >actual && + ! test -s actual +' + +test_expect_success 'submodule contains modified content' ' + rm -f sm1/new-file && + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 contains modified content + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..3e75765 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1 @@ + -foo6 + +new + EOF + test_cmp expected actual +' + +(cd sm1; git commit -mchange foo6 >/dev/null) && +head8=$(cd sm1; git rev-parse --short --verify HEAD) && +test_expect_success 'submodule is modified' ' + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 17243c9..$head8: + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..3e75765 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1 @@ + -foo6 + +new + EOF + test_cmp expected actual +' + +test_expect_success 'modified submodule contains untracked content' ' + echo new > sm1/new-file && + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 contains untracked content + Submodule sm1 17243c9..$head8: + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..3e75765 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1 @@ + -foo6 + +new + EOF + test_cmp expected actual +' + +test_expect_success 'modified submodule contains untracked content (untracked ignored)' ' + git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 17243c9..$head8: + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..3e75765 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1 @@ + -foo6 + +new + EOF + test_cmp expected actual +' + +test_expect_success 'modified submodule contains untracked content (dirty ignored)' ' + git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 17243c9..cfce562: + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..3e75765 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1 @@ + -foo6 + +new + EOF + test_cmp expected actual +' + +test_expect_success 'modified submodule contains untracked content (all ignored)' ' + git diff-index -p --ignore-submodules=all --submodule=diff HEAD >actual && + ! test -s actual +' + +test_expect_success 'modified submodule contains untracked and modified content' ' + echo modification >> sm1/foo6 && + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 contains untracked content + Submodule sm1 contains modified content + Submodule sm1 17243c9..cfce562: + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..dfda541 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1,2 @@ + -foo6 + +new + +modification + EOF + test_cmp expected actual +' + +test_expect_success 'modified submodule contains untracked and modified content (untracked ignored)' ' + echo modification >> sm1/foo6 && + git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 contains modified content + Submodule sm1 17243c9..cfce562: + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..e20e2d9 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1,3 @@ + -foo6 + +new + +modification + +modification + EOF + test_cmp expected actual +' + +test_expect_success 'modified submodule contains untracked and modified content (dirty ignored)' ' + echo modification >> sm1/foo6 && + git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 17243c9..cfce562: + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..3e75765 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1 @@ + -foo6 + +new + EOF + test_cmp expected actual +' + +test_expect_success 'modified submodule contains untracked and modified content (all ignored)' ' + echo modification >> sm1/foo6 && + git diff-index -p --ignore-submodules --submodule=diff HEAD >actual && + ! test -s actual +' + +# NOT OK +test_expect_success 'modified submodule contains modified content' ' + rm -f sm1/new-file && + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 contains modified content + Submodule sm1 17243c9..cfce562: + diff --git a/sm1/foo6 b/sm1/foo6 + index 462398b..ac466ca 100644 + --- a/sm1/foo6 + +++ b/sm1/foo6 + @@ -1 +1,5 @@ + -foo6 + +new + +modification + +modification + +modification + +modification + EOF + test_cmp expected actual +' + +rm -rf sm1 +test_expect_success 'deleted submodule' ' + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 17243c9...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' ' + git diff-index -p --submodule=diff HEAD >actual && + cat >expected <<-EOF && + Submodule sm1 17243c9...0000000 (submodule deleted) + Submodule sm2 0000000...a5a65c9 (new submodule) + diff --git a/sm2/foo8 b/sm2/foo8 + new file mode 100644 + index 0000000..db9916b + --- /dev/null + +++ b/sm2/foo8 + @@ -0,0 +1 @@ + +foo8 + diff --git a/sm2/foo9 b/sm2/foo9 + new file mode 100644 + index 0000000..9c3b4f6 + --- /dev/null + +++ b/sm2/foo9 + @@ -0,0 +1 @@ + +foo9 + EOF + test_cmp expected actual +' + +test_expect_success 'path filter' ' + git diff-index -p --submodule=diff HEAD sm2 >actual && + cat >expected <<-EOF && + Submodule sm2 0000000...a5a65c9 (new submodule) + diff --git a/sm2/foo8 b/sm2/foo8 + new file mode 100644 + index 0000000..db9916b + --- /dev/null + +++ b/sm2/foo8 + @@ -0,0 +1 @@ + +foo8 + diff --git a/sm2/foo9 b/sm2/foo9 + new file mode 100644 + index 0000000..9c3b4f6 + --- /dev/null + +++ b/sm2/foo9 + @@ -0,0 +1 @@ + +foo9 + EOF + test_cmp expected actual +' + +commit_file sm2 +test_expect_success 'given commit' ' + git diff-index -p --submodule=diff HEAD^ >actual && + cat >expected <<-EOF && + Submodule sm1 17243c9...0000000 (submodule deleted) + Submodule sm2 0000000...a5a65c9 (new submodule) + diff --git a/sm2/foo8 b/sm2/foo8 + new file mode 100644 + index 0000000..db9916b + --- /dev/null + +++ b/sm2/foo8 + @@ -0,0 +1 @@ + +foo8 + diff --git a/sm2/foo9 b/sm2/foo9 + new file mode 100644 + index 0000000..9c3b4f6 + --- /dev/null + +++ b/sm2/foo9 + @@ -0,0 +1 @@ + +foo9 + EOF + test_cmp expected actual +' + +test_expect_success 'setup .git file for sm2' ' + (cd sm2 && + REAL="$(pwd)/../.real" && + mv .git "$REAL" + echo "gitdir: $REAL" >.git) +' + +test_expect_success 'diff --submodule=diff with .git file' ' + git diff --submodule=diff HEAD^ >actual && + cat >expected <<-EOF && + Submodule sm1 17243c9...0000000 (submodule deleted) + Submodule sm2 0000000...a5a65c9 (new submodule) + diff --git a/sm2/foo8 b/sm2/foo8 + new file mode 100644 + index 0000000..db9916b + --- /dev/null + +++ b/sm2/foo8 + @@ -0,0 +1 @@ + +foo8 + diff --git a/sm2/foo9 b/sm2/foo9 + new file mode 100644 + index 0000000..9c3b4f6 + --- /dev/null + +++ b/sm2/foo9 + @@ -0,0 +1 @@ + +foo9 + EOF + test_cmp expected actual +' + +test_done diff --git a/t/t4061-diff-indent.sh b/t/t4061-diff-indent.sh new file mode 100755 index 0000000000..556450609b --- /dev/null +++ b/t/t4061-diff-indent.sh @@ -0,0 +1,216 @@ +#!/bin/sh + +test_description='Test diff indent heuristic. + +' +. ./test-lib.sh +. "$TEST_DIRECTORY"/diff-lib.sh + +# Compare two diff outputs. Ignore "index" lines, because we don't +# care about SHA-1s or file modes. +compare_diff () { + sed -e "/^index /d" <"$1" >.tmp-1 + sed -e "/^index /d" <"$2" >.tmp-2 + test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 +} + +# Compare blame output using the expectation for a diff as reference. +# Only look for the lines coming from non-boundary commits. +compare_blame () { + sed -n -e "1,4d" -e "s/^\+//p" <"$1" >.tmp-1 + sed -ne "s/^[^^][^)]*) *//p" <"$2" >.tmp-2 + test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2 +} + +test_expect_success 'prepare' ' + cat <<-\EOF >spaces.txt && + 1 + 2 + a + + b + 3 + 4 + EOF + + cat <<-\EOF >functions.c && + 1 + 2 + /* function */ + foo() { + foo + } + + 3 + 4 + EOF + + git add spaces.txt functions.c && + test_tick && + git commit -m initial && + git branch old && + + cat <<-\EOF >spaces.txt && + 1 + 2 + a + + b + a + + b + 3 + 4 + EOF + + cat <<-\EOF >functions.c && + 1 + 2 + /* function */ + bar() { + foo + } + + /* function */ + foo() { + foo + } + + 3 + 4 + EOF + + git add spaces.txt functions.c && + test_tick && + git commit -m initial && + git branch new && + + tr "_" " " <<-\EOF >spaces-expect && + diff --git a/spaces.txt b/spaces.txt + --- a/spaces.txt + +++ b/spaces.txt + @@ -3,5 +3,8 @@ + a + _ + b + +a + + + +b + 3 + 4 + EOF + + tr "_" " " <<-\EOF >spaces-compacted-expect && + diff --git a/spaces.txt b/spaces.txt + --- a/spaces.txt + +++ b/spaces.txt + @@ -2,6 +2,9 @@ + 2 + a + _ + +b + +a + + + b + 3 + 4 + EOF + + tr "_" " " <<-\EOF >functions-expect && + diff --git a/functions.c b/functions.c + --- a/functions.c + +++ b/functions.c + @@ -1,6 +1,11 @@ + 1 + 2 + /* function */ + +bar() { + + foo + +} + + + +/* function */ + foo() { + foo + } + EOF + + tr "_" " " <<-\EOF >functions-compacted-expect + diff --git a/functions.c b/functions.c + --- a/functions.c + +++ b/functions.c + @@ -1,5 +1,10 @@ + 1 + 2 + +/* function */ + +bar() { + + foo + +} + + + /* function */ + foo() { + foo + EOF +' + +test_expect_success 'diff: ugly spaces' ' + git diff old new -- spaces.txt >out && + compare_diff spaces-expect out +' + +test_expect_success 'diff: nice spaces with --indent-heuristic' ' + git diff --indent-heuristic old new -- spaces.txt >out-compacted && + compare_diff spaces-compacted-expect out-compacted +' + +test_expect_success 'diff: nice spaces with diff.indentHeuristic' ' + git -c diff.indentHeuristic=true diff old new -- spaces.txt >out-compacted2 && + compare_diff spaces-compacted-expect out-compacted2 +' + +test_expect_success 'diff: --no-indent-heuristic overrides config' ' + git -c diff.indentHeuristic=true diff --no-indent-heuristic old new -- spaces.txt >out2 && + compare_diff spaces-expect out2 +' + +test_expect_success 'diff: --indent-heuristic with --patience' ' + git diff --indent-heuristic --patience old new -- spaces.txt >out-compacted3 && + compare_diff spaces-compacted-expect out-compacted3 +' + +test_expect_success 'diff: --indent-heuristic with --histogram' ' + git diff --indent-heuristic --histogram old new -- spaces.txt >out-compacted4 && + compare_diff spaces-compacted-expect out-compacted4 +' + +test_expect_success 'diff: ugly functions' ' + git diff old new -- functions.c >out && + compare_diff functions-expect out +' + +test_expect_success 'diff: nice functions with --indent-heuristic' ' + git diff --indent-heuristic old new -- functions.c >out-compacted && + compare_diff functions-compacted-expect out-compacted +' + +test_expect_success 'blame: ugly spaces' ' + git blame old..new -- spaces.txt >out-blame && + compare_blame spaces-expect out-blame +' + +test_expect_success 'blame: nice spaces with --indent-heuristic' ' + git blame --indent-heuristic old..new -- spaces.txt >out-blame-compacted && + compare_blame spaces-compacted-expect out-blame-compacted +' + +test_expect_success 'blame: nice spaces with diff.indentHeuristic' ' + git -c diff.indentHeuristic=true blame old..new -- spaces.txt >out-blame-compacted2 && + compare_blame spaces-compacted-expect out-blame-compacted2 +' + +test_expect_success 'blame: --no-indent-heuristic overrides config' ' + git -c diff.indentHeuristic=true blame --no-indent-heuristic old..new -- spaces.txt >out-blame2 && + git blame old..new -- spaces.txt >out-blame && + compare_blame spaces-expect out-blame2 +' + +test_done diff --git a/t/t4150-am.sh b/t/t4150-am.sh index 9ce9424d15..89a5bacac5 100755 --- a/t/t4150-am.sh +++ b/t/t4150-am.sh @@ -977,4 +977,27 @@ test_expect_success 'am --patch-format=mboxrd handles mboxrd' ' test_cmp msg out ' +test_expect_success 'am works with multi-line in-body headers' ' + FORTY="String that has a length of more than forty characters" && + LONG="$FORTY $FORTY" && + rm -fr .git/rebase-apply && + git checkout -f first && + echo one >> file && + git commit -am "$LONG" --author="$LONG <long@example.com>" && + git format-patch --stdout -1 >patch && + # bump from, date, and subject down to in-body header + perl -lpe " + if (/^From:/) { + print \"From: x <x\@example.com>\"; + print \"Date: Sat, 1 Jan 2000 00:00:00 +0000\"; + print \"Subject: x\n\"; + } + " patch >msg && + git checkout HEAD^ && + git am msg && + # Ensure that the author and full message are present + git cat-file commit HEAD | grep "^author.*long@example.com" && + git cat-file commit HEAD | grep "^$LONG" +' + test_done diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh index ae08b57712..9df054bf05 100755 --- a/t/t4201-shortlog.sh +++ b/t/t4201-shortlog.sh @@ -190,4 +190,23 @@ test_expect_success 'shortlog with --output=<file>' ' test_line_count = 3 shortlog ' +test_expect_success 'shortlog --committer (internal)' ' + git checkout --orphan side && + git commit --allow-empty -m one && + git commit --allow-empty -m two && + GIT_COMMITTER_NAME="Sin Nombre" git commit --allow-empty -m three && + + cat >expect <<-\EOF && + 2 C O Mitter + 1 Sin Nombre + EOF + git shortlog -nsc HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'shortlog --committer (external)' ' + git log --format=full | git shortlog -nsc >actual && + test_cmp expect actual +' + test_done diff --git a/t/t4202-log.sh b/t/t4202-log.sh index e2db47c36e..48b55bfd27 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -188,6 +188,16 @@ test_expect_success 'git log --no-walk=sorted <commits> sorts by commit time' ' ' cat > expect << EOF +=== 804a787 sixth +=== 394ef78 fifth +=== 5d31159 fourth +EOF +test_expect_success 'git log --line-prefix="=== " --no-walk <commits> sorts by commit time' ' + git log --line-prefix="=== " --no-walk --oneline 5d31159 804a787 394ef78 > actual && + test_cmp expect actual +' + +cat > expect << EOF 5d31159 fourth 804a787 sixth 394ef78 fifth @@ -284,6 +294,21 @@ test_expect_success 'simple log --graph' ' test_cmp expect actual ' +cat > expect <<EOF +123 * Second +123 * sixth +123 * fifth +123 * fourth +123 * third +123 * second +123 * initial +EOF + +test_expect_success 'simple log --graph --line-prefix="123 "' ' + git log --graph --line-prefix="123 " --pretty=tformat:%s >actual && + test_cmp expect actual +' + test_expect_success 'set up merge history' ' git checkout -b side HEAD~4 && test_commit side-1 1 1 && @@ -313,6 +338,49 @@ test_expect_success 'log --graph with merge' ' test_cmp expect actual ' +cat > expect <<\EOF +| | | * Merge branch 'side' +| | | |\ +| | | | * side-2 +| | | | * side-1 +| | | * | Second +| | | * | sixth +| | | * | fifth +| | | * | fourth +| | | |/ +| | | * third +| | | * second +| | | * initial +EOF + +test_expect_success 'log --graph --line-prefix="| | | " with merge' ' + git log --line-prefix="| | | " --graph --date-order --pretty=tformat:%s | + sed "s/ *\$//" >actual && + test_cmp expect actual +' + +cat > expect.colors <<\EOF +* Merge branch 'side' +<BLUE>|<RESET><CYAN>\<RESET> +<BLUE>|<RESET> * side-2 +<BLUE>|<RESET> * side-1 +* <CYAN>|<RESET> Second +* <CYAN>|<RESET> sixth +* <CYAN>|<RESET> fifth +* <CYAN>|<RESET> fourth +<CYAN>|<RESET><CYAN>/<RESET> +* third +* second +* initial +EOF + +test_expect_success 'log --graph with merge with log.graphColors' ' + test_config log.graphColors " blue,invalid-color, cyan, red , " && + git log --color=always --graph --date-order --pretty=tformat:%s | + test_decode_color | sed "s/ *\$//" >actual && + test_cmp expect.colors actual +' + test_expect_success 'log --raw --graph -m with merge' ' git log --raw --graph --oneline -m master | head -n 500 >actual && grep "initial" actual @@ -867,6 +935,331 @@ test_expect_success 'log --graph with diff and stats' ' test_i18ncmp expect actual.sanitized ' +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 + +test_expect_success 'log --line-prefix="*** " --graph with diff and stats' ' + git log --line-prefix="*** " --no-renames --graph --pretty=short --stat -p >actual && + sanitize_output >actual.sanitized <actual && + test_i18ncmp expect actual.sanitized +' + +cat >expect <<-\EOF +* reach +| +| A reach.t +* Merge branch 'tangle' +* Merge branch 'side' +|\ +| * side-2 +| +| A 2 +* Second +| +| A one +* sixth + + D a/two +EOF + +test_expect_success 'log --graph with --name-status' ' + git log --graph --format=%s --name-status tangle..reach >actual && + sanitize_output <actual >actual.sanitized && + test_cmp expect actual.sanitized +' + +cat >expect <<-\EOF +* reach +| +| reach.t +* Merge branch 'tangle' +* Merge branch 'side' +|\ +| * side-2 +| +| 2 +* Second +| +| one +* sixth + + a/two +EOF + +test_expect_success 'log --graph with --name-only' ' + git log --graph --format=%s --name-only tangle..reach >actual && + sanitize_output <actual >actual.sanitized && + test_cmp expect actual.sanitized +' + test_expect_success 'dotdot is a parent directory' ' mkdir -p a/b && ( echo sixth && echo fifth ) >expect && diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh index 84a809690e..0288c17ec6 100755 --- a/t/t4204-patch-id.sh +++ b/t/t4204-patch-id.sh @@ -143,6 +143,20 @@ test_expect_success 'patch-id supports git-format-patch MIME output' ' test_cmp patch-id_master patch-id_same ' +test_expect_success 'patch-id respects config from subdir' ' + test_config patchid.stable true && + mkdir subdir && + + # copy these because test_patch_id() looks for them in + # the current directory + cp bar-then-foo foo-then-bar subdir && + + ( + cd subdir && + test_patch_id irrelevant patchid.stable=true + ) +' + cat >nonl <<\EOF diff --git i/a w/a index e69de29..2e65efe 100644 diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh index f5435fd250..21eb8c8587 100755 --- a/t/t4205-log-pretty-formats.sh +++ b/t/t4205-log-pretty-formats.sh @@ -535,4 +535,30 @@ test_expect_success 'clean log decoration' ' test_cmp expected actual1 ' +cat >trailers <<EOF +Signed-off-by: A U Thor <author@example.com> +Acked-by: A U Thor <author@example.com> +[ v2 updated patch description ] +Signed-off-by: A U Thor <author@example.com> +EOF + +test_expect_success 'pretty format %(trailers) shows trailers' ' + echo "Some contents" >trailerfile && + git add trailerfile && + git commit -F - <<-EOF && + trailers: this commit message has trailers + + This commit is a test commit with trailers at the end. We parse this + message and display the trailers using %bT + + $(cat trailers) + EOF + git log --no-walk --pretty="%(trailers)" >actual && + cat >expect <<-EOF && + $(cat trailers) + + EOF + test_cmp expect actual +' + test_done diff --git a/t/t4254-am-corrupt.sh b/t/t4254-am-corrupt.sh index 85716dd6ec..168739c721 100755 --- a/t/t4254-am-corrupt.sh +++ b/t/t4254-am-corrupt.sh @@ -29,9 +29,9 @@ test_expect_success 'try to apply corrupted patch' ' ' test_expect_success 'compare diagnostic; ensure file is still here' ' - echo "fatal: git diff header lacks filename information (line 4)" >expected && + echo "error: git diff header lacks filename information (line 4)" >expected && test_path_is_file f && - test_cmp expected actual + test_i18ncmp expected actual ' test_done diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh index 80b2387341..886b6953e4 100755 --- a/t/t5000-tar-tree.sh +++ b/t/t5000-tar-tree.sh @@ -179,6 +179,15 @@ test_expect_success 'git archive --remote' \ 'git archive --remote=. HEAD >b5.tar && test_cmp_bin b.tar b5.tar' +test_expect_success 'git archive --remote with configured remote' ' + git config remote.foo.url . && + ( + cd a && + git archive --remote=foo --output=../b5-nick.tar HEAD + ) && + test_cmp_bin b.tar b5-nick.tar +' + test_expect_success \ 'validate file modification time' \ 'mkdir extract && @@ -197,9 +206,15 @@ test_expect_success 'git archive with --output, override inferred format' ' test_cmp_bin b.tar d4.zip ' -test_expect_success \ - 'git archive --list outside of a git repo' \ - 'GIT_DIR=some/non-existing/directory git archive --list' +test_expect_success 'git archive --list outside of a git repo' ' + nongit git archive --list +' + +test_expect_success 'git archive --remote outside of a git repo' ' + git archive HEAD >expect.tar && + nongit git archive --remote="$PWD" HEAD >actual.tar && + test_cmp_bin expect.tar actual.tar +' test_expect_success 'clients cannot access unreachable commits' ' test_commit unreachable && diff --git a/t/t5003-archive-zip.sh b/t/t5003-archive-zip.sh index 14744b2a4b..55c7870997 100755 --- a/t/t5003-archive-zip.sh +++ b/t/t5003-archive-zip.sh @@ -64,6 +64,12 @@ check_zip() { test_cmp_bin $original/nodiff.crlf $extracted/nodiff.crlf && test_cmp_bin $original/nodiff.lf $extracted/nodiff.lf " + + test_expect_success UNZIP " validate that custom diff is unchanged " " + test_cmp_bin $original/custom.cr $extracted/custom.cr && + test_cmp_bin $original/custom.crlf $extracted/custom.crlf && + test_cmp_bin $original/custom.lf $extracted/custom.lf + " } test_expect_success \ @@ -78,6 +84,9 @@ test_expect_success \ printf "text\r" >a/nodiff.cr && printf "text\r\n" >a/nodiff.crlf && printf "text\n" >a/nodiff.lf && + printf "text\r" >a/custom.cr && + printf "text\r\n" >a/custom.crlf && + printf "text\n" >a/custom.lf && printf "\0\r" >a/binary.cr && printf "\0\r\n" >a/binary.crlf && printf "\0\n" >a/binary.lf && @@ -112,15 +121,20 @@ test_expect_success 'add files to repository' ' test_expect_success 'setup export-subst and diff attributes' ' echo "a/nodiff.* -diff" >>.git/info/attributes && echo "a/diff.* diff" >>.git/info/attributes && + echo "a/custom.* diff=custom" >>.git/info/attributes && + git config diff.custom.binary true && 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 'create bare clone' ' + git clone --bare . bare.git && + cp .git/info/attributes bare.git/info/attributes && + # Recreate our changes to .git/config rather than just copying it, as + # we do not want to clobber core.bare or other settings. + git -C bare.git config diff.custom.binary true +' test_expect_success \ 'remove ignored file' \ diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh index 45d228ebc8..7171f67539 100755 --- a/t/t5100-mailinfo.sh +++ b/t/t5100-mailinfo.sh @@ -13,7 +13,7 @@ test_expect_success 'split sample box' \ 'git mailsplit -o. "$DATA/sample.mbox" >last && last=$(cat last) && echo total is $last && - test $(cat last) = 17' + test $(cat last) = 18' check_mailinfo () { mail=$1 opt=$2 @@ -158,4 +158,17 @@ test_expect_success 'mailinfo handles rfc2822 comment' ' test_cmp "$DATA/comment.expect" comment/info ' +test_expect_success 'mailinfo with mailinfo.scissors config' ' + test_config mailinfo.scissors true && + ( + mkdir sub && + cd sub && + git mailinfo ../msg0014.sc ../patch0014.sc <../0014 >../info0014.sc + ) && + test_cmp "$DATA/msg0014--scissors" msg0014.sc && + test_cmp "$DATA/patch0014--scissors" patch0014.sc && + test_cmp "$DATA/info0014--scissors" info0014.sc +' + + test_done diff --git a/t/t5100/info0018 b/t/t5100/info0018 new file mode 100644 index 0000000000..d53e7491c7 --- /dev/null +++ b/t/t5100/info0018 @@ -0,0 +1,5 @@ +Author: Another Thor +Email: a.thor@example.com +Subject: This one contains a tab and a space +Date: Fri, 9 Jun 2006 00:44:16 -0700 + diff --git a/t/t5100/info0018--no-inbody-headers b/t/t5100/info0018--no-inbody-headers new file mode 100644 index 0000000000..30b17bd913 --- /dev/null +++ b/t/t5100/info0018--no-inbody-headers @@ -0,0 +1,5 @@ +Author: A U Thor +Email: a.u.thor@example.com +Subject: check multiline inbody headers +Date: Fri, 9 Jun 2006 00:44:16 -0700 + diff --git a/t/t5100/msg0015 b/t/t5100/msg0015 index 4abb3d5c6c..e69de29bb2 100644 --- a/t/t5100/msg0015 +++ b/t/t5100/msg0015 @@ -1,2 +0,0 @@ - - a list - - of stuff diff --git a/t/t5100/msg0018 b/t/t5100/msg0018 new file mode 100644 index 0000000000..56de83d7fc --- /dev/null +++ b/t/t5100/msg0018 @@ -0,0 +1,2 @@ +a commit message + diff --git a/t/t5100/msg0018--no-inbody-headers b/t/t5100/msg0018--no-inbody-headers new file mode 100644 index 0000000000..b1e05d3862 --- /dev/null +++ b/t/t5100/msg0018--no-inbody-headers @@ -0,0 +1,8 @@ +From: Another Thor + <a.thor@example.com> +Subject: This one contains + a tab + and a space + +a commit message + diff --git a/t/t5100/patch0018 b/t/t5100/patch0018 new file mode 100644 index 0000000000..789df6d030 --- /dev/null +++ b/t/t5100/patch0018 @@ -0,0 +1,6 @@ +diff --git a/foo b/foo +index e69de29..d95f3ad 100644 +--- a/foo ++++ b/foo +@@ -0,0 +1 @@ ++content diff --git a/t/t5100/patch0018--no-inbody-headers b/t/t5100/patch0018--no-inbody-headers new file mode 100644 index 0000000000..789df6d030 --- /dev/null +++ b/t/t5100/patch0018--no-inbody-headers @@ -0,0 +1,6 @@ +diff --git a/foo b/foo +index e69de29..d95f3ad 100644 +--- a/foo ++++ b/foo +@@ -0,0 +1 @@ ++content diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox index 8b2ae064c3..6d4d0e4474 100644 --- a/t/t5100/sample.mbox +++ b/t/t5100/sample.mbox @@ -699,3 +699,22 @@ index e69de29..d95f3ad 100644 +++ b/foo @@ -0,0 +1 @@ +New content +From nobody Mon Sep 17 00:00:00 2001 +From: A U Thor <a.u.thor@example.com> +Subject: check multiline inbody headers +Date: Fri, 9 Jun 2006 00:44:16 -0700 + +From: Another Thor + <a.thor@example.com> +Subject: This one contains + a tab + and a space + +a commit message + +diff --git a/foo b/foo +index e69de29..d95f3ad 100644 +--- a/foo ++++ b/foo +@@ -0,0 +1 @@ ++content diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 899e52d50f..43a672c345 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -406,6 +406,21 @@ test_expect_success 'verify resulting packs' ' git verify-pack test-11-*.pack ' +test_expect_success 'set up pack for non-repo tests' ' + # make sure we have a pack with no matching index file + cp test-1-*.pack foo.pack +' + +test_expect_success 'index-pack --stdin complains of non-repo' ' + nongit test_must_fail git index-pack --stdin <foo.pack && + test_path_is_missing non-repo/.git +' + +test_expect_success 'index-pack <pack> works in non-repo' ' + nongit git index-pack ../foo.pack && + test_path_is_file foo.idx +' + # # WARNING! # diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index 3893afd687..424bec7d77 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -7,6 +7,18 @@ objpath () { echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')" } +# show objects present in pack ($1 should be associated *.idx) +list_packed_objects () { + git show-index <"$1" | cut -d' ' -f2 +} + +# has_any pattern-file content-file +# tests whether content-file has any entry from pattern-file with entries being +# whole lines. +has_any () { + grep -Ff "$1" "$2" +} + test_expect_success 'setup repo with moderate-sized history' ' for i in $(test_seq 1 10); do test_commit $i @@ -16,6 +28,7 @@ test_expect_success 'setup repo with moderate-sized history' ' test_commit side-$i done && git checkout master && + bitmaptip=$(git rev-parse master) && blob=$(echo tagged-blob | git hash-object -w --stdin) && git tag tagged-blob $blob && git config repack.writebitmaps true && @@ -105,12 +118,10 @@ test_expect_success 'fetch (partial bitmap)' ' test_cmp expect actual ' -test_expect_success 'incremental repack cannot create bitmaps' ' +test_expect_success 'incremental repack fails when bitmaps are requested' ' test_commit more-1 && - find .git/objects/pack -name "*.bitmap" >expect && - git repack -d && - find .git/objects/pack -name "*.bitmap" >actual && - test_cmp expect actual + test_must_fail git repack -d 2>err && + test_i18ngrep "Incremental repacks are incompatible with bitmap" err ' test_expect_success 'incremental repack can disable bitmaps' ' @@ -118,6 +129,83 @@ test_expect_success 'incremental repack can disable bitmaps' ' git repack -d --no-write-bitmap-index ' +test_expect_success 'pack-objects respects --local (non-local loose)' ' + git init --bare alt.git && + echo $(pwd)/alt.git/objects >.git/objects/info/alternates && + echo content1 >file1 && + # non-local loose object which is not present in bitmapped pack + altblob=$(GIT_DIR=alt.git git hash-object -w file1) && + # non-local loose object which is also present in bitmapped pack + git cat-file blob $blob | GIT_DIR=alt.git git hash-object -w --stdin && + git add file1 && + test_tick && + git commit -m commit_file1 && + echo HEAD | git pack-objects --local --stdout --revs >1.pack && + git index-pack 1.pack && + list_packed_objects 1.idx >1.objects && + printf "%s\n" "$altblob" "$blob" >nonlocal-loose && + ! has_any nonlocal-loose 1.objects +' + +test_expect_success 'pack-objects respects --honor-pack-keep (local non-bitmapped pack)' ' + echo content2 >file2 && + blob2=$(git hash-object -w file2) && + git add file2 && + test_tick && + git commit -m commit_file2 && + printf "%s\n" "$blob2" "$bitmaptip" >keepobjects && + pack2=$(git pack-objects pack2 <keepobjects) && + mv pack2-$pack2.* .git/objects/pack/ && + >.git/objects/pack/pack2-$pack2.keep && + rm $(objpath $blob2) && + echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >2a.pack && + git index-pack 2a.pack && + list_packed_objects 2a.idx >2a.objects && + ! has_any keepobjects 2a.objects +' + +test_expect_success 'pack-objects respects --local (non-local pack)' ' + mv .git/objects/pack/pack2-$pack2.* alt.git/objects/pack/ && + echo HEAD | git pack-objects --local --stdout --revs >2b.pack && + git index-pack 2b.pack && + list_packed_objects 2b.idx >2b.objects && + ! has_any keepobjects 2b.objects +' + +test_expect_success 'pack-objects respects --honor-pack-keep (local bitmapped pack)' ' + ls .git/objects/pack/ | grep bitmap >output && + test_line_count = 1 output && + packbitmap=$(basename $(cat output) .bitmap) && + list_packed_objects .git/objects/pack/$packbitmap.idx >packbitmap.objects && + test_when_finished "rm -f .git/objects/pack/$packbitmap.keep" && + >.git/objects/pack/$packbitmap.keep && + echo HEAD | git pack-objects --honor-pack-keep --stdout --revs >3a.pack && + git index-pack 3a.pack && + list_packed_objects 3a.idx >3a.objects && + ! has_any packbitmap.objects 3a.objects +' + +test_expect_success 'pack-objects respects --local (non-local bitmapped pack)' ' + mv .git/objects/pack/$packbitmap.* alt.git/objects/pack/ && + test_when_finished "mv alt.git/objects/pack/$packbitmap.* .git/objects/pack/" && + echo HEAD | git pack-objects --local --stdout --revs >3b.pack && + git index-pack 3b.pack && + list_packed_objects 3b.idx >3b.objects && + ! has_any packbitmap.objects 3b.objects +' + +test_expect_success 'pack-objects to file can use bitmap' ' + # make sure we still have 1 bitmap index from previous tests + ls .git/objects/pack/ | grep bitmap >output && + test_line_count = 1 output && + # verify equivalent packs are generated with/without using bitmap index + packasha1=$(git pack-objects --no-use-bitmap-index --all packa </dev/null) && + packbsha1=$(git pack-objects --use-bitmap-index --all packb </dev/null) && + list_packed_objects <packa-$packasha1.idx >packa.objects && + list_packed_objects <packb-$packbsha1.idx >packb.objects && + test_cmp packa.objects packb.objects +' + test_expect_success 'full repack, reusing previous bitmaps' ' git repack -ad && ls .git/objects/pack/ | grep bitmap >output && @@ -143,6 +231,20 @@ test_expect_success 'create objects for missing-HAVE tests' ' EOF ' +test_expect_success 'pack-objects respects --incremental' ' + cat >revs2 <<-EOF && + HEAD + $commit + EOF + git pack-objects --incremental --stdout --revs <revs2 >4.pack && + git index-pack 4.pack && + list_packed_objects 4.idx >4.objects && + test_line_count = 4 4.objects && + git rev-list --objects $commit >revlist && + cut -d" " -f1 revlist |sort >objects && + test_cmp 4.objects objects +' + test_expect_success 'pack with missing blob' ' rm $(objpath $blob) && git pack-objects --stdout --revs <revs >/dev/null @@ -158,10 +260,6 @@ test_expect_success 'pack with missing parent' ' git pack-objects --stdout --revs <revs >/dev/null ' -test_lazy_prereq JGIT ' - type jgit -' - test_expect_success JGIT 'we can read jgit bitmaps' ' git clone . compat-jgit && ( diff --git a/t/t5314-pack-cycle-detection.sh b/t/t5314-pack-cycle-detection.sh new file mode 100755 index 0000000000..f7dbdfb412 --- /dev/null +++ b/t/t5314-pack-cycle-detection.sh @@ -0,0 +1,113 @@ +#!/bin/sh + +test_description='test handling of inter-pack delta cycles during repack + +The goal here is to create a situation where we have two blobs, A and B, with A +as a delta against B in one pack, and vice versa in the other. Then if we can +persuade a full repack to find A from one pack and B from the other, that will +give us a cycle when we attempt to reuse those deltas. + +The trick is in the "persuade" step, as it depends on the internals of how +pack-objects picks which pack to reuse the deltas from. But we can assume +that it does so in one of two general strategies: + + 1. Using a static ordering of packs. In this case, no inter-pack cycles can + happen. Any objects with a delta relationship must be present in the same + pack (i.e., no "--thin" packs on disk), so we will find all related objects + from that pack. So assuming there are no cycles within a single pack (and + we avoid generating them via pack-objects or importing them via + index-pack), then our result will have no cycles. + + So this case should pass the tests no matter how we arrange things. + + 2. Picking the next pack to examine based on locality (i.e., where we found + something else recently). + + In this case, we want to make sure that we find the delta versions of A and + B and not their base versions. We can do this by putting two blobs in each + pack. The first is a "dummy" blob that can only be found in the pack in + question. And then the second is the actual delta we want to find. + + The two blobs must be present in the same tree, not present in other trees, + and the dummy pathname must sort before the delta path. + +The setup below focuses on case 2. We have two commits HEAD and HEAD^, each +which has two files: "dummy" and "file". Then we can make two packs which +contain: + + [pack one] + HEAD:dummy + HEAD:file (as delta against HEAD^:file) + HEAD^:file (as base) + + [pack two] + HEAD^:dummy + HEAD^:file (as delta against HEAD:file) + HEAD:file (as base) + +Then no matter which order we start looking at the packs in, we know that we +will always find a delta for "file", because its lookup will always come +immediately after the lookup for "dummy". +' +. ./test-lib.sh + + + +# Create a pack containing the the tree $1 and blob $1:file, with +# the latter stored as a delta against $2:file. +# +# We convince pack-objects to make the delta in the direction of our choosing +# by marking $2 as a preferred-base edge. That results in $1:file as a thin +# delta, and index-pack completes it by adding $2:file as a base. +# +# Note that the two variants of "file" must be similar enough to convince git +# to create the delta. +make_pack () { + { + printf '%s\n' "-$(git rev-parse $2)" + printf '%s dummy\n' "$(git rev-parse $1:dummy)" + printf '%s file\n' "$(git rev-parse $1:file)" + } | + git pack-objects --stdout | + git index-pack --stdin --fix-thin +} + +test_expect_success 'setup' ' + test-genrandom base 4096 >base && + for i in one two + do + # we want shared content here to encourage deltas... + cp base file && + echo $i >>file && + + # ...whereas dummy should be short, because we do not want + # deltas that would create duplicates when we --fix-thin + echo $i >dummy && + + git add file dummy && + test_tick && + git commit -m $i || + return 1 + done && + + make_pack HEAD^ HEAD && + make_pack HEAD HEAD^ +' + +test_expect_success 'repack' ' + # We first want to check that we do not have any internal errors, + # and also that we do not hit the last-ditch cycle-breaking code + # in write_object(), which will issue a warning to stderr. + >expect && + git repack -ad 2>stderr && + test_cmp expect stderr && + + # And then double-check that the resulting pack is usable (i.e., + # we did not fail to notice any cycles). We know we are accessing + # the objects via the new pack here, because "repack -d" will have + # removed the others. + git cat-file blob HEAD:file >/dev/null && + git cat-file blob HEAD^:file >/dev/null +' + +test_done diff --git a/t/t5315-pack-objects-compression.sh b/t/t5315-pack-objects-compression.sh new file mode 100755 index 0000000000..34c47dae09 --- /dev/null +++ b/t/t5315-pack-objects-compression.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +test_description='pack-object compression configuration' + +. ./test-lib.sh + +# This should be moved to test-lib.sh together with the +# copy in t0021 after both topics have graduated to 'master'. +file_size () { + perl -e 'print -s $ARGV[0]' "$1" +} + +test_expect_success setup ' + printf "%2000000s" X | + git hash-object -w --stdin >object-name && + # make sure it resulted in a loose object + ob=$(sed -e "s/\(..\).*/\1/" object-name) && + ject=$(sed -e "s/..\(.*\)/\1/" object-name) && + test -f .git/objects/$ob/$ject +' + +while read expect config +do + test_expect_success "pack-objects with $config" ' + test_when_finished "rm -f pack-*.*" && + git $config pack-objects pack <object-name && + sz=$(file_size pack-*.pack) && + case "$expect" in + small) test "$sz" -le 100000 ;; + large) test "$sz" -ge 100000 ;; + esac + ' +done <<\EOF +large -c core.compression=0 +small -c core.compression=9 +large -c core.compression=0 -c pack.compression=0 +large -c core.compression=9 -c pack.compression=0 +small -c core.compression=0 -c pack.compression=9 +small -c core.compression=9 -c pack.compression=9 +large -c pack.compression=0 +small -c pack.compression=9 +EOF + +test_done diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh new file mode 100755 index 0000000000..37143ea0ac --- /dev/null +++ b/t/t5316-pack-delta-depth.sh @@ -0,0 +1,93 @@ +#!/bin/sh + +test_description='pack-objects breaks long cross-pack delta chains' +. ./test-lib.sh + +# This mirrors a repeated push setup: +# +# 1. A client repeatedly modifies some files, makes a +# commit, and pushes the result. It does this N times +# before we get around to repacking. +# +# 2. Each push generates a thin pack with the new version of +# various objects. Let's consider some file in the root tree +# which is updated in each commit. +# +# When generating push number X, we feed commit X-1 (and +# thus blob X-1) as a preferred base. The resulting pack has +# blob X as a thin delta against blob X-1. +# +# On the receiving end, "index-pack --fix-thin" will +# complete the pack with a base copy of blob X-1. +# +# 3. In older versions of git, if we used the delta from +# pack X, then we'd always find blob X-1 as a base in the +# same pack (and generate a fresh delta). +# +# But with the pack mru, we jump from delta to delta +# following the traversal order: +# +# a. We grab blob X from pack X as a delta, putting it at +# the tip of our mru list. +# +# b. Eventually we move onto commit X-1. We need other +# objects which are only in pack X-1 (in the test code +# below, it's the containing tree). That puts pack X-1 +# at the tip of our mru list. +# +# c. Eventually we look for blob X-1, and we find the +# version in pack X-1 (because it's the mru tip). +# +# Now we have blob X as a delta against X-1, which is a delta +# against X-2, and so forth. +# +# In the real world, these small pushes would get exploded by +# unpack-objects rather than "index-pack --fix-thin", but the +# same principle applies to larger pushes (they only need one +# repeatedly-modified file to generate the delta chain). + +test_expect_success 'create series of packs' ' + test-genrandom foo 4096 >content && + prev= && + for i in $(test_seq 1 10) + do + cat content >file && + echo $i >>file && + git add file && + git commit -m $i && + cur=$(git rev-parse HEAD^{tree}) && + { + test -n "$prev" && echo "-$prev" + echo $cur + echo "$(git rev-parse :file) file" + } | git pack-objects --stdout >tmp && + git index-pack --stdin --fix-thin <tmp || return 1 + prev=$cur + done +' + +max_chain() { + git index-pack --verify-stat-only "$1" >output && + perl -lne ' + /chain length = (\d+)/ and $len = $1; + END { print $len } + ' output +} + +# Note that this whole setup is pretty reliant on the current +# packing heuristics. We double-check that our test case +# actually produces a long chain. If it doesn't, it should be +# adjusted (or scrapped if the heuristics have become too unreliable) +test_expect_success 'packing produces a long delta' ' + # Use --window=0 to make sure we are seeing reused deltas, + # not computing a new long chain. + pack=$(git pack-objects --all --window=0 </dev/null pack) && + test 9 = "$(max_chain pack-$pack.pack)" +' + +test_expect_success '--depth limits depth' ' + pack=$(git pack-objects --all --depth=5 </dev/null pack) && + test 5 = "$(max_chain pack-$pack.pack)" +' + +test_done diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 305ca7a930..3331e0f534 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -255,4 +255,42 @@ test_expect_success 'deny pushing to delete current branch' ' ) ' +extract_ref_advertisement () { + perl -lne ' + # \\ is there to skip capabilities after \0 + /push< ([^\\]+)/ or next; + exit 0 if $1 eq "0000"; + print $1; + ' +} + +test_expect_success 'receive-pack de-dupes .have lines' ' + git init shared && + git -C shared commit --allow-empty -m both && + git clone -s shared fork && + ( + cd shared && + git checkout -b only-shared && + git commit --allow-empty -m only-shared && + git update-ref refs/heads/foo HEAD + ) && + + # Notable things in this expectation: + # - local refs are not de-duped + # - .have does not duplicate locals + # - .have does not duplicate itself + local=$(git -C fork rev-parse HEAD) && + shared=$(git -C shared rev-parse only-shared) && + cat >expect <<-EOF && + $local refs/heads/master + $local refs/remotes/origin/HEAD + $local refs/remotes/origin/master + $shared .have + EOF + + GIT_TRACE_PACKET=$(pwd)/trace git push fork HEAD:foo && + extract_ref_advertisement <trace >refs && + test_cmp expect refs +' + test_done diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh index 82d913a6a8..505e1b4a7f 100755 --- a/t/t5500-fetch-pack.sh +++ b/t/t5500-fetch-pack.sh @@ -652,4 +652,72 @@ test_expect_success MINGW 'fetch-pack --diag-url c:repo' ' check_prot_path c:repo file c:repo ' +test_expect_success 'clone shallow since ...' ' + test_create_repo shallow-since && + ( + cd shallow-since && + GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one && + GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two && + GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three && + git clone --shallow-since "300000000 +0700" "file://$(pwd)/." ../shallow11 && + git -C ../shallow11 log --pretty=tformat:%s HEAD >actual && + echo three >expected && + test_cmp expected actual + ) +' + +test_expect_success 'fetch shallow since ...' ' + git -C shallow11 fetch --shallow-since "200000000 +0700" origin && + git -C shallow11 log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + three + two + EOF + test_cmp expected actual +' + +test_expect_success 'shallow clone exclude tag two' ' + test_create_repo shallow-exclude && + ( + cd shallow-exclude && + test_commit one && + test_commit two && + test_commit three && + git clone --shallow-exclude two "file://$(pwd)/." ../shallow12 && + git -C ../shallow12 log --pretty=tformat:%s HEAD >actual && + echo three >expected && + test_cmp expected actual + ) +' + +test_expect_success 'fetch exclude tag one' ' + git -C shallow12 fetch --shallow-exclude one origin && + git -C shallow12 log --pretty=tformat:%s origin/master >actual && + test_write_lines three two >expected && + test_cmp expected actual +' + +test_expect_success 'fetching deepen' ' + test_create_repo shallow-deepen && + ( + cd shallow-deepen && + test_commit one && + test_commit two && + test_commit three && + git clone --depth 1 "file://$(pwd)/." deepen && + test_commit four && + git -C deepen log --pretty=tformat:%s master >actual && + echo three >expected && + test_cmp expected actual && + git -C deepen fetch --deepen=1 && + git -C deepen log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + four + three + two + EOF + test_cmp expected actual + ) +' + test_done diff --git a/t/t5504-fetch-receive-strict.sh b/t/t5504-fetch-receive-strict.sh index 9b19cff729..49d3621a92 100755 --- a/t/t5504-fetch-receive-strict.sh +++ b/t/t5504-fetch-receive-strict.sh @@ -152,7 +152,7 @@ test_expect_success 'push with receive.fsck.missingEmail=warn' ' git --git-dir=dst/.git config --add \ receive.fsck.badDate warn && git push --porcelain dst bogus >act 2>&1 && - test_must_fail grep "missingEmail" act + ! grep "missingEmail" act ' test_expect_success \ diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh index 8198d8eb05..535d53fa63 100755 --- a/t/t5505-remote.sh +++ b/t/t5505-remote.sh @@ -725,7 +725,7 @@ test_expect_success 'rename a remote' ' ( cd four && git remote rename origin upstream && - rmdir .git/refs/remotes/origin && + test -z "$(git for-each-ref 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/*" && @@ -764,6 +764,13 @@ test_expect_success 'rename a remote with name prefix of other remote' ' ) ' +test_expect_success 'rename succeeds with existing remote.<target>.prune' ' + git clone one four.four && + test_when_finished git config --global --unset remote.upstream.prune && + git config --global remote.upstream.prune true && + git -C four.four remote rename origin upstream +' + cat >remotes_origin <<EOF URL: $(pwd)/one Push: refs/heads/master:refs/heads/upstream diff --git a/t/t5509-fetch-push-namespaces.sh b/t/t5509-fetch-push-namespaces.sh index bc44ac36d5..75c570adca 100755 --- a/t/t5509-fetch-push-namespaces.sh +++ b/t/t5509-fetch-push-namespaces.sh @@ -4,6 +4,7 @@ test_description='fetch/push involving ref namespaces' . ./test-lib.sh test_expect_success setup ' + git config --global protocol.ext.allow user && test_tick && git init original && ( diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh index 819b9ddd0f..94fc9be9ce 100755 --- a/t/t5512-ls-remote.sh +++ b/t/t5512-ls-remote.sh @@ -99,7 +99,7 @@ test_expect_success 'confuses pattern as remote when no remote specified' ' # 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 && - test_cmp exp actual + test_i18ncmp exp actual ' test_expect_success 'die with non-2 for wrong repository even with --exit-code' ' @@ -207,5 +207,54 @@ test_expect_success 'ls-remote --symref omits filtered-out matches' ' test_cmp expect actual ' +test_lazy_prereq GIT_DAEMON ' + test_tristate GIT_TEST_GIT_DAEMON && + test "$GIT_TEST_GIT_DAEMON" != false +' + +# This test spawns a daemon, so run it only if the user would be OK with +# testing with git-daemon. +test_expect_success PIPE,JGIT,GIT_DAEMON 'indicate no refs in standards-compliant empty remote' ' + JGIT_DAEMON_PORT=${JGIT_DAEMON_PORT-${this_test#t}} && + JGIT_DAEMON_PID= && + git init --bare empty.git && + >empty.git/git-daemon-export-ok && + mkfifo jgit_daemon_output && + { + jgit daemon --port="$JGIT_DAEMON_PORT" . >jgit_daemon_output & + JGIT_DAEMON_PID=$! + } && + test_when_finished kill "$JGIT_DAEMON_PID" && + { + read line && + case $line in + Exporting*) + ;; + *) + echo "Expected: Exporting" && + false;; + esac && + read line && + case $line in + "Listening on"*) + ;; + *) + echo "Expected: Listening on" && + false;; + esac + } <jgit_daemon_output && + # --exit-code asks the command to exit with 2 when no + # matching refs are found. + test_expect_code 2 git ls-remote --exit-code git://localhost:$JGIT_DAEMON_PORT/empty.git +' + +test_expect_success 'ls-remote works outside repository' ' + # It is important for this repo to be inside the nongit + # area, as we want a repo name that does not include + # slashes (because those inhibit some of our configuration + # lookups). + nongit git init --bare dst.git && + nongit git ls-remote dst.git +' test_done diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index 26b2cafc47..0fc5a7c596 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -1004,7 +1004,7 @@ test_expect_success 'push --porcelain' ' test_expect_success 'push --porcelain bad url' ' 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 + ! grep -q Done .git/bar ' test_expect_success 'push --porcelain rejected' ' diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh index 551844584f..17f4d0fe4e 100755 --- a/t/t5520-pull.sh +++ b/t/t5520-pull.sh @@ -255,6 +255,23 @@ test_expect_success '--rebase' ' test new = "$(git show HEAD:file2)" ' +test_expect_success '--rebase fast forward' ' + git reset --hard before-rebase && + git checkout -b ff && + echo another modification >file && + git commit -m third file && + + git checkout to-rebase && + git pull --rebase . ff && + test "$(git rev-parse HEAD)" = "$(git rev-parse ff)" && + + # The above only validates the result. Did we actually bypass rebase? + git reflog -1 >reflog.actual && + sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy && + echo "OBJID HEAD@{0}: pull --rebase . ff: Fast-forward" >reflog.expected && + test_cmp reflog.expected reflog.fuzzy +' + test_expect_success '--rebase with conflicts shows advice' ' test_when_finished "git rebase --abort; git checkout -f to-rebase" && git checkout -b seq && diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh index 73f4bb6346..44309566f1 100755 --- a/t/t5528-push-default.sh +++ b/t/t5528-push-default.sh @@ -98,6 +98,16 @@ test_expect_success 'push from/to new branch with upstream, matching and simple' test_push_failure upstream ' +test_expect_success 'push ambiguously named branch with upstream, matching and simple' ' + git checkout -b ambiguous && + test_config branch.ambiguous.remote parent1 && + test_config branch.ambiguous.merge refs/heads/ambiguous && + git tag ambiguous && + test_push_success simple ambiguous && + test_push_success matching ambiguous && + test_push_success upstream ambiguous +' + test_expect_success 'push from/to new branch with current creates remote branch' ' test_config branch.new-branch.remote repo1 && git checkout new-branch && diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh index 198ce84754..f55137f76f 100755 --- a/t/t5531-deep-submodule-push.sh +++ b/t/t5531-deep-submodule-push.sh @@ -427,7 +427,52 @@ test_expect_success 'push unpushable submodule recursively fails' ' cd submodule.git && git rev-parse master >../actual ) && + test_when_finished git -C work reset --hard master^ && test_cmp expected actual ' +test_expect_success 'push --dry-run does not recursively update submodules' ' + ( + cd work/gar/bage && + git checkout master && + git rev-parse master >../../../expected_submodule && + > junk9 && + git add junk9 && + git commit -m "Ninth junk" && + + # Go up to 'work' directory + cd ../.. && + git checkout master && + git rev-parse master >../expected_pub && + git add gar/bage && + git commit -m "Ninth commit for gar/bage" && + git push --dry-run --recurse-submodules=on-demand ../pub.git master + ) && + git -C submodule.git rev-parse master >actual_submodule && + git -C pub.git rev-parse master >actual_pub && + test_cmp expected_pub actual_pub && + test_cmp expected_submodule actual_submodule +' + +test_expect_success 'push --dry-run does not recursively update submodules' ' + git -C work push --dry-run --recurse-submodules=only ../pub.git master && + + git -C submodule.git rev-parse master >actual_submodule && + git -C pub.git rev-parse master >actual_pub && + test_cmp expected_pub actual_pub && + test_cmp expected_submodule actual_submodule +' + +test_expect_success 'push only unpushed submodules recursively' ' + git -C work/gar/bage rev-parse master >expected_submodule && + git -C pub.git rev-parse master >expected_pub && + + git -C work push --recurse-submodules=only ../pub.git master && + + git -C submodule.git rev-parse master >actual_submodule && + git -C pub.git rev-parse master >actual_pub && + test_cmp expected_submodule actual_submodule && + test_cmp expected_pub actual_pub +' + test_done diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh index 37a433504e..5fbf67c446 100755 --- a/t/t5539-fetch-http-shallow.sh +++ b/t/t5539-fetch-http-shallow.sh @@ -73,5 +73,78 @@ test_expect_success 'no shallow lines after receiving ACK ready' ' ) ' +test_expect_success 'clone shallow since ...' ' + test_create_repo shallow-since && + ( + cd shallow-since && + GIT_COMMITTER_DATE="100000000 +0700" git commit --allow-empty -m one && + GIT_COMMITTER_DATE="200000000 +0700" git commit --allow-empty -m two && + GIT_COMMITTER_DATE="300000000 +0700" git commit --allow-empty -m three && + mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-since.git" && + git clone --shallow-since "300000000 +0700" $HTTPD_URL/smart/shallow-since.git ../shallow11 && + git -C ../shallow11 log --pretty=tformat:%s HEAD >actual && + echo three >expected && + test_cmp expected actual + ) +' + +test_expect_success 'fetch shallow since ...' ' + git -C shallow11 fetch --shallow-since "200000000 +0700" origin && + git -C shallow11 log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + three + two + EOF + test_cmp expected actual +' + +test_expect_success 'shallow clone exclude tag two' ' + test_create_repo shallow-exclude && + ( + cd shallow-exclude && + test_commit one && + test_commit two && + test_commit three && + mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-exclude.git" && + git clone --shallow-exclude two $HTTPD_URL/smart/shallow-exclude.git ../shallow12 && + git -C ../shallow12 log --pretty=tformat:%s HEAD >actual && + echo three >expected && + test_cmp expected actual + ) +' + +test_expect_success 'fetch exclude tag one' ' + git -C shallow12 fetch --shallow-exclude one origin && + git -C shallow12 log --pretty=tformat:%s origin/master >actual && + test_write_lines three two >expected && + test_cmp expected actual +' + +test_expect_success 'fetching deepen' ' + test_create_repo shallow-deepen && + ( + cd shallow-deepen && + test_commit one && + test_commit two && + test_commit three && + mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" && + git clone --depth 1 $HTTPD_URL/smart/shallow-deepen.git deepen && + mv "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" .git && + test_commit four && + git -C deepen log --pretty=tformat:%s master >actual && + echo three >expected && + test_cmp expected actual && + mv .git "$HTTPD_DOCUMENT_ROOT_PATH/shallow-deepen.git" && + git -C deepen fetch --deepen=1 && + git -C deepen log --pretty=tformat:%s origin/master >actual && + cat >expected <<-\EOF && + four + three + two + EOF + test_cmp expected actual + ) +' + stop_httpd test_done diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh index ea813b9383..9a57a7d8f2 100755 --- a/t/t5545-push-options.sh +++ b/t/t5545-push-options.sh @@ -3,6 +3,8 @@ test_description='pushing to a repository using push options' . ./test-lib.sh +. "$TEST_DIRECTORY"/lib-httpd.sh +start_httpd mk_repo_pair () { rm -rf workbench upstream && @@ -100,4 +102,17 @@ test_expect_success 'two push options work' ' test_cmp expect upstream/.git/hooks/post-receive.push_options ' +test_expect_success 'push option denied properly by http remote helper' '\ + mk_repo_pair && + git -C upstream config receive.advertisePushOptions false && + git -C upstream config http.receivepack true && + cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git && + git clone "$HTTPD_URL"/smart/upstream test_http_clone && + test_commit -C test_http_clone one && + test_must_fail git -C test_http_clone push --push-option=asdf origin master && + git -C test_http_clone push origin master +' + +stop_httpd + test_done diff --git a/t/t5546-receive-limits.sh b/t/t5546-receive-limits.sh new file mode 100755 index 0000000000..10cb0be2b7 --- /dev/null +++ b/t/t5546-receive-limits.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +test_description='check receive input limits' +. ./test-lib.sh + +# Let's run tests with different unpack limits: 1 and 10000 +# When the limit is 1, `git receive-pack` will call `git index-pack`. +# When the limit is 10000, `git receive-pack` will call `git unpack-objects`. + +test_pack_input_limit () { + case "$1" in + index) unpack_limit=1 ;; + unpack) unpack_limit=10000 ;; + esac + + test_expect_success 'prepare destination repository' ' + rm -fr dest && + git --bare init dest + ' + + test_expect_success "set unpacklimit to $unpack_limit" ' + git --git-dir=dest config receive.unpacklimit "$unpack_limit" + ' + + test_expect_success 'setting receive.maxInputSize to 512 rejects push' ' + git --git-dir=dest config receive.maxInputSize 512 && + test_must_fail git push dest HEAD + ' + + test_expect_success 'bumping limit to 4k allows push' ' + git --git-dir=dest config receive.maxInputSize 4k && + git push dest HEAD + ' + + test_expect_success 'prepare destination repository (again)' ' + rm -fr dest && + git --bare init dest + ' + + test_expect_success 'lifting the limit allows push' ' + git --git-dir=dest config receive.maxInputSize 0 && + git push dest HEAD + ' +} + +test_expect_success "create known-size (1024 bytes) commit" ' + test-genrandom foo 1024 >one-k && + git add one-k && + test_commit one-k +' + +test_pack_input_limit index +test_pack_input_limit unpack + +test_done diff --git a/t/t5547-push-quarantine.sh b/t/t5547-push-quarantine.sh new file mode 100755 index 0000000000..af9fcd833a --- /dev/null +++ b/t/t5547-push-quarantine.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +test_description='check quarantine of objects during push' +. ./test-lib.sh + +test_expect_success 'create picky dest repo' ' + git init --bare dest.git && + write_script dest.git/hooks/pre-receive <<-\EOF + while read old new ref; do + test "$(git log -1 --format=%s $new)" = reject && exit 1 + done + exit 0 + EOF +' + +test_expect_success 'accepted objects work' ' + test_commit ok && + git push dest.git HEAD && + commit=$(git rev-parse HEAD) && + git --git-dir=dest.git cat-file commit $commit +' + +test_expect_success 'rejected objects are not installed' ' + test_commit reject && + commit=$(git rev-parse HEAD) && + test_must_fail git push dest.git reject && + test_must_fail git --git-dir=dest.git cat-file commit $commit +' + +test_expect_success 'rejected objects are removed' ' + echo "incoming-*" >expect && + (cd dest.git/objects && echo incoming-*) >actual && + test_cmp expect actual +' + +test_expect_success 'push to repo path with path separator (colon)' ' + # The interesting failure case here is when the + # receiving end cannot access its original object directory, + # so make it likely for us to generate a delta by having + # a non-trivial file with multiple versions. + + test-genrandom foo 4096 >file.bin && + git add file.bin && + git commit -m bin && + + if test_have_prereq MINGW + then + pathsep=";" + else + pathsep=":" + fi && + git clone --bare . "xxx${pathsep}yyy.git" && + + echo change >>file.bin && + git commit -am change && + # Note that we have to use the full path here, or it gets confused + # with the ssh host:path syntax. + git push "$(pwd)/xxx${pathsep}yyy.git" HEAD +' + +test_done diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh index 7641417b4a..b69ece1d66 100755 --- a/t/t5550-http-fetch-dumb.sh +++ b/t/t5550-http-fetch-dumb.sh @@ -34,6 +34,15 @@ test_expect_success 'clone http repository' ' test_cmp file clone/file ' +test_expect_success 'list refs from outside any repository' ' + cat >expect <<-EOF && + $(git rev-parse master) HEAD + $(git rev-parse master) refs/heads/master + EOF + nongit git ls-remote "$HTTPD_URL/dumb/repo.git" >actual && + test_cmp expect actual +' + test_expect_success 'create password-protected repository' ' mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH/auth/dumb/" && cp -Rf "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \ @@ -307,5 +316,76 @@ test_expect_success 'remote-http complains cleanly about malformed urls' ' test_must_fail git remote-http http::/example.com/repo.git ' +test_expect_success 'redirects can be forbidden/allowed' ' + test_must_fail git -c http.followRedirects=false \ + clone $HTTPD_URL/dumb-redir/repo.git dumb-redir && + git -c http.followRedirects=true \ + clone $HTTPD_URL/dumb-redir/repo.git dumb-redir 2>stderr +' + +test_expect_success 'redirects are reported to stderr' ' + # just look for a snippet of the redirected-to URL + test_i18ngrep /dumb/ stderr +' + +test_expect_success 'non-initial redirects can be forbidden' ' + test_must_fail git -c http.followRedirects=initial \ + clone $HTTPD_URL/redir-objects/repo.git redir-objects && + git -c http.followRedirects=true \ + clone $HTTPD_URL/redir-objects/repo.git redir-objects +' + +test_expect_success 'http.followRedirects defaults to "initial"' ' + test_must_fail git clone $HTTPD_URL/redir-objects/repo.git default +' + +# The goal is for a clone of the "evil" repository, which has no objects +# itself, to cause the client to fetch objects from the "victim" repository. +test_expect_success 'set up evil alternates scheme' ' + victim=$HTTPD_DOCUMENT_ROOT_PATH/victim.git && + git init --bare "$victim" && + git -C "$victim" --work-tree=. commit --allow-empty -m secret && + git -C "$victim" repack -ad && + git -C "$victim" update-server-info && + sha1=$(git -C "$victim" rev-parse HEAD) && + + evil=$HTTPD_DOCUMENT_ROOT_PATH/evil.git && + git init --bare "$evil" && + # do this by hand to avoid object existence check + printf "%s\\t%s\\n" $sha1 refs/heads/master >"$evil/info/refs" +' + +# Here we'll just redirect via HTTP. In a real-world attack these would be on +# different servers, but we should reject it either way. +test_expect_success 'http-alternates is a non-initial redirect' ' + echo "$HTTPD_URL/dumb/victim.git/objects" \ + >"$evil/objects/info/http-alternates" && + test_must_fail git -c http.followRedirects=initial \ + clone $HTTPD_URL/dumb/evil.git evil-initial && + git -c http.followRedirects=true \ + clone $HTTPD_URL/dumb/evil.git evil-initial +' + +# Curl supports a lot of protocols that we'd prefer not to allow +# http-alternates to use, but it's hard to test whether curl has +# accessed, say, the SMTP protocol, because we are not running an SMTP server. +# But we can check that it does not allow access to file://, which would +# otherwise allow this clone to complete. +test_expect_success 'http-alternates cannot point at funny protocols' ' + echo "file://$victim/objects" >"$evil/objects/info/http-alternates" && + test_must_fail git -c http.followRedirects=true \ + clone "$HTTPD_URL/dumb/evil.git" evil-file +' + +test_expect_success 'http-alternates triggers not-from-user protocol check' ' + echo "$HTTPD_URL/dumb/victim.git/objects" \ + >"$evil/objects/info/http-alternates" && + test_config_global http.followRedirects true && + test_must_fail git -c protocol.http.allow=user \ + clone $HTTPD_URL/dumb/evil.git evil-user && + git -c protocol.http.allow=always \ + clone $HTTPD_URL/dumb/evil.git evil-user +' + stop_httpd test_done diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh index 1ec5b2747a..a51b7e20d3 100755 --- a/t/t5551-http-fetch-smart.sh +++ b/t/t5551-http-fetch-smart.sh @@ -119,6 +119,10 @@ test_expect_success 'redirects re-root further requests' ' git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited ' +test_expect_success 're-rooting dies on insane schemes' ' + test_must_fail git clone $HTTPD_URL/insane-redir/repo.git insane +' + test_expect_success 'clone from password-protected repository' ' echo two >expect && set_askpass user@host pass@host && @@ -276,6 +280,58 @@ test_expect_success 'large fetch-pack requests can be split across POSTs' ' test_line_count = 2 posts ' +test_expect_success 'test allowreachablesha1inwant' ' + test_when_finished "rm -rf test_reachable.git" && + server="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + master_sha=$(git -C "$server" rev-parse refs/heads/master) && + git -C "$server" config uploadpack.allowreachablesha1inwant 1 && + + git init --bare test_reachable.git && + git -C test_reachable.git remote add origin "$HTTPD_URL/smart/repo.git" && + git -C test_reachable.git fetch origin "$master_sha" +' + +test_expect_success 'test allowreachablesha1inwant with unreachable' ' + test_when_finished "rm -rf test_reachable.git; git reset --hard $(git rev-parse HEAD)" && + + #create unreachable sha + echo content >file2 && + git add file2 && + git commit -m two && + git push public HEAD:refs/heads/doomed && + git push public :refs/heads/doomed && + + server="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + master_sha=$(git -C "$server" rev-parse refs/heads/master) && + git -C "$server" config uploadpack.allowreachablesha1inwant 1 && + + git init --bare test_reachable.git && + git -C test_reachable.git remote add origin "$HTTPD_URL/smart/repo.git" && + test_must_fail git -C test_reachable.git fetch origin "$(git rev-parse HEAD)" +' + +test_expect_success 'test allowanysha1inwant with unreachable' ' + test_when_finished "rm -rf test_reachable.git; git reset --hard $(git rev-parse HEAD)" && + + #create unreachable sha + echo content >file2 && + git add file2 && + git commit -m two && + git push public HEAD:refs/heads/doomed && + git push public :refs/heads/doomed && + + server="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && + master_sha=$(git -C "$server" rev-parse refs/heads/master) && + git -C "$server" config uploadpack.allowreachablesha1inwant 1 && + + git init --bare test_reachable.git && + git -C test_reachable.git remote add origin "$HTTPD_URL/smart/repo.git" && + test_must_fail git -C test_reachable.git fetch origin "$(git rev-parse HEAD)" && + + git -C "$server" config uploadpack.allowanysha1inwant 1 && + git -C test_reachable.git fetch origin "$(git rev-parse HEAD)" +' + test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' ' ( cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" && diff --git a/t/t5580-clone-push-unc.sh b/t/t5580-clone-push-unc.sh new file mode 100755 index 0000000000..b195f71ea9 --- /dev/null +++ b/t/t5580-clone-push-unc.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +test_description='various UNC path tests (Windows-only)' +. ./test-lib.sh + +if ! test_have_prereq MINGW; then + skip_all='skipping UNC path tests, requires Windows' + test_done +fi + +UNCPATH="$(pwd)" +case "$UNCPATH" in +[A-Z]:*) + # Use administrative share e.g. \\localhost\C$\git-sdk-64\usr\src\git + # (we use forward slashes here because MSYS2 and Git accept them, and + # they are easier on the eyes) + UNCPATH="//localhost/${UNCPATH%%:*}\$/${UNCPATH#?:}" + test -d "$UNCPATH" || { + skip_all='could not access administrative share; skipping' + test_done + } + ;; +*) + skip_all='skipping UNC path tests, cannot determine current path as UNC' + test_done + ;; +esac + +test_expect_success setup ' + test_commit initial +' + +test_expect_success clone ' + git clone "file://$UNCPATH" clone +' + +test_expect_success push ' + ( + cd clone && + git checkout -b to-push && + test_commit to-push && + git push origin HEAD + ) && + rev="$(git -C clone rev-parse --verify refs/heads/to-push)" && + test "$rev" = "$(git rev-parse --verify refs/heads/to-push)" +' + +test_done diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index a433394200..b52b8acf98 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -151,7 +151,7 @@ test_expect_success 'clone --mirror does not repeat tags' ' git clone --mirror src mirror2 && (cd mirror2 && git show-ref 2> clone.err > clone.out) && - test_must_fail grep Duplicate mirror2/clone.err && + ! grep Duplicate mirror2/clone.err && grep some-tag mirror2/clone.out ' @@ -386,6 +386,47 @@ test_expect_success 'tortoiseplink is like putty, with extra arguments' ' expect_ssh "-batch -P 123" myhost src ' +test_expect_success 'double quoted plink.exe in GIT_SSH_COMMAND' ' + copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" && + GIT_SSH_COMMAND="\"$TRASH_DIRECTORY/plink.exe\" -v" \ + git clone "[myhost:123]:src" ssh-bracket-clone-plink-3 && + expect_ssh "-v -P 123" myhost src +' + +SQ="'" +test_expect_success 'single quoted plink.exe in GIT_SSH_COMMAND' ' + copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" && + GIT_SSH_COMMAND="$SQ$TRASH_DIRECTORY/plink.exe$SQ -v" \ + git clone "[myhost:123]:src" ssh-bracket-clone-plink-4 && + expect_ssh "-v -P 123" myhost src +' + +test_expect_success 'GIT_SSH_VARIANT overrides plink detection' ' + copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" && + GIT_SSH_VARIANT=ssh \ + git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 && + expect_ssh "-p 123" myhost src +' + +test_expect_success 'ssh.variant overrides plink detection' ' + copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" && + git -c ssh.variant=ssh \ + clone "[myhost:123]:src" ssh-bracket-clone-variant-2 && + expect_ssh "-p 123" myhost src +' + +test_expect_success 'GIT_SSH_VARIANT overrides plink detection to plink' ' + GIT_SSH_VARIANT=plink \ + git clone "[myhost:123]:src" ssh-bracket-clone-variant-3 && + expect_ssh "-P 123" myhost src +' + +test_expect_success 'GIT_SSH_VARIANT overrides plink to tortoiseplink' ' + GIT_SSH_VARIANT=tortoiseplink \ + git clone "[myhost:123]:src" ssh-bracket-clone-variant-4 && + expect_ssh "-batch -P 123" myhost src +' + # Reset the GIT_SSH environment variable for clone tests. setup_ssh_wrapper diff --git a/t/t5613-info-alternate.sh b/t/t5613-info-alternate.sh index 9cd2626dba..895f46bb91 100755 --- a/t/t5613-info-alternate.sh +++ b/t/t5613-info-alternate.sh @@ -6,107 +6,134 @@ test_description='test transitive info/alternate entries' . ./test-lib.sh -# test that a file is not reachable in the current repository -# but that it is after creating a info/alternate entry -reachable_via() { - alternate="$1" - file="$2" - if git cat-file -e "HEAD:$file"; then return 1; fi - echo "$alternate" >> .git/objects/info/alternate - git cat-file -e "HEAD:$file" -} - -test_valid_repo() { - git fsck --full > fsck.log && - test_line_count = 0 fsck.log -} - -base_dir=$(pwd) - -test_expect_success 'preparing first repository' \ -'test_create_repo A && cd A && -echo "Hello World" > file1 && -git add file1 && -git commit -m "Initial commit" file1 && -git repack -a -d && -git prune' - -cd "$base_dir" - -test_expect_success 'preparing second repository' \ -'git clone -l -s A B && cd B && -echo "foo bar" > file2 && -git add file2 && -git commit -m "next commit" file2 && -git repack -a -d -l && -git prune' - -cd "$base_dir" - -test_expect_success 'preparing third repository' \ -'git clone -l -s B C && cd C && -echo "Goodbye, cruel world" > file3 && -git add file3 && -git commit -m "one more" file3 && -git repack -a -d -l && -git prune' - -cd "$base_dir" - -test_expect_success 'creating too deep nesting' \ -'git clone -l -s C D && -git clone -l -s D E && -git clone -l -s E F && -git clone -l -s F G && -git clone --bare -l -s G H' - -test_expect_success 'invalidity of deepest repository' \ -'cd H && { - test_valid_repo - test $? -ne 0 -}' - -cd "$base_dir" +test_expect_success 'preparing first repository' ' + test_create_repo A && ( + cd A && + echo "Hello World" > file1 && + git add file1 && + git commit -m "Initial commit" file1 && + git repack -a -d && + git prune + ) +' -test_expect_success 'validity of third repository' \ -'cd C && -test_valid_repo' +test_expect_success 'preparing second repository' ' + git clone -l -s A B && ( + cd B && + echo "foo bar" > file2 && + git add file2 && + git commit -m "next commit" file2 && + git repack -a -d -l && + git prune + ) +' -cd "$base_dir" +test_expect_success 'preparing third repository' ' + git clone -l -s B C && ( + cd C && + echo "Goodbye, cruel world" > file3 && + git add file3 && + git commit -m "one more" file3 && + git repack -a -d -l && + git prune + ) +' -test_expect_success 'validity of fourth repository' \ -'cd D && -test_valid_repo' +test_expect_success 'count-objects shows the alternates' ' + cat >expect <<-EOF && + alternate: $(pwd)/B/.git/objects + alternate: $(pwd)/A/.git/objects + EOF + git -C C count-objects -v >actual && + grep ^alternate: actual >actual.alternates && + test_cmp expect actual.alternates +' -cd "$base_dir" +# Note: These tests depend on the hard-coded value of 5 as the maximum depth +# we will follow recursion. We start the depth at 0 and count links, not +# repositories. This means that in a chain like: +# +# A --> B --> C --> D --> E --> F --> G --> H +# 0 1 2 3 4 5 6 +# +# we are OK at "G", but break at "H", even though "H" is actually the 8th +# repository, not the 6th, which you might expect. Counting the links allows +# N+1 repositories, and counting from 0 to 5 inclusive allows 6 links. +# +# Note also that we must use "--bare -l" to make the link to H. The "-l" +# ensures we do not do a connectivity check, and the "--bare" makes sure +# we do not try to checkout the result (which needs objects), either of +# which would cause the clone to fail. +test_expect_success 'creating too deep nesting' ' + git clone -l -s C D && + git clone -l -s D E && + git clone -l -s E F && + git clone -l -s F G && + git clone --bare -l -s G H +' -test_expect_success 'breaking of loops' \ -'echo "$base_dir"/B/.git/objects >> "$base_dir"/A/.git/objects/info/alternates&& -cd C && -test_valid_repo' +test_expect_success 'validity of seventh repository' ' + git -C G fsck +' -cd "$base_dir" +test_expect_success 'invalidity of eighth repository' ' + test_must_fail git -C H fsck +' -test_expect_success 'that info/alternates is necessary' \ -'cd C && -rm -f .git/objects/info/alternates && -! (test_valid_repo)' +test_expect_success 'breaking of loops' ' + echo "$(pwd)"/B/.git/objects >>A/.git/objects/info/alternates && + git -C C fsck +' -cd "$base_dir" +test_expect_success 'that info/alternates is necessary' ' + rm -f C/.git/objects/info/alternates && + test_must_fail git -C C fsck +' -test_expect_success 'that relative alternate is possible for current dir' \ -'cd C && -echo "../../../B/.git/objects" > .git/objects/info/alternates && -test_valid_repo' +test_expect_success 'that relative alternate is possible for current dir' ' + echo "../../../B/.git/objects" >C/.git/objects/info/alternates && + git fsck +' -cd "$base_dir" +test_expect_success 'that relative alternate is recursive' ' + git -C D fsck +' -test_expect_success \ - 'that relative alternate is only possible for current dir' ' - cd D && - ! (test_valid_repo) +# we can reach "A" from our new repo both directly, and via "C". +# The deep/subdir is there to make sure we are not doing a stupid +# pure-text comparison of the alternate names. +test_expect_success 'relative duplicates are eliminated' ' + mkdir -p deep/subdir && + git init --bare deep/subdir/duplicate.git && + cat >deep/subdir/duplicate.git/objects/info/alternates <<-\EOF && + ../../../../C/.git/objects + ../../../../A/.git/objects + EOF + cat >expect <<-EOF && + alternate: $(pwd)/C/.git/objects + alternate: $(pwd)/B/.git/objects + alternate: $(pwd)/A/.git/objects + EOF + git -C deep/subdir/duplicate.git count-objects -v >actual && + grep ^alternate: actual >actual.alternates && + test_cmp expect actual.alternates ' -cd "$base_dir" +test_expect_success CASE_INSENSITIVE_FS 'dup finding can be case-insensitive' ' + git init --bare insensitive.git && + # the previous entry for "A" will have used uppercase + cat >insensitive.git/objects/info/alternates <<-\EOF && + ../../C/.git/objects + ../../a/.git/objects + EOF + cat >expect <<-EOF && + alternate: $(pwd)/C/.git/objects + alternate: $(pwd)/B/.git/objects + alternate: $(pwd)/A/.git/objects + EOF + git -C insensitive.git count-objects -v >actual && + grep ^alternate: actual >actual.alternates && + test_cmp expect actual.alternates +' test_done diff --git a/t/t5615-alternate-env.sh b/t/t5615-alternate-env.sh new file mode 100755 index 0000000000..26ebb0375d --- /dev/null +++ b/t/t5615-alternate-env.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +test_description='handling of alternates in environment variables' +. ./test-lib.sh + +check_obj () { + alt=$1; shift + while read obj expect + do + echo "$obj" >&3 && + echo "$obj $expect" >&4 + done 3>input 4>expect && + GIT_ALTERNATE_OBJECT_DIRECTORIES=$alt \ + git "$@" cat-file --batch-check='%(objectname) %(objecttype)' \ + <input >actual && + test_cmp expect actual +} + +test_expect_success 'create alternate repositories' ' + git init --bare one.git && + one=$(echo one | git -C one.git hash-object -w --stdin) && + git init --bare two.git && + two=$(echo two | git -C two.git hash-object -w --stdin) +' + +test_expect_success 'objects inaccessible without alternates' ' + check_obj "" <<-EOF + $one missing + $two missing + EOF +' + +test_expect_success 'access alternate via absolute path' ' + check_obj "$PWD/one.git/objects" <<-EOF + $one blob + $two missing + EOF +' + +test_expect_success 'access multiple alternates' ' + check_obj "$PWD/one.git/objects:$PWD/two.git/objects" <<-EOF + $one blob + $two blob + EOF +' + +# bare paths are relative from $GIT_DIR +test_expect_success 'access alternate via relative path (bare)' ' + git init --bare bare.git && + check_obj "../one.git/objects" -C bare.git <<-EOF + $one blob + EOF +' + +# non-bare paths are relative to top of worktree +test_expect_success 'access alternate via relative path (worktree)' ' + git init worktree && + check_obj "../one.git/objects" -C worktree <<-EOF + $one blob + EOF +' + +# path is computed after moving to top-level of worktree +test_expect_success 'access alternate via relative path (subdir)' ' + mkdir subdir && + check_obj "one.git/objects" -C subdir <<-EOF + $one blob + EOF +' + +# set variables outside test to avoid quote insanity; the \057 is '/', +# which doesn't need quoting, but just confirms that de-quoting +# is working. +quoted='"one.git\057objects"' +unquoted='two.git/objects' +test_expect_success 'mix of quoted and unquoted alternates' ' + check_obj "$quoted:$unquoted" <<-EOF + $one blob + $two blob +' + +test_expect_success !MINGW 'broken quoting falls back to interpreting raw' ' + mv one.git \"one.git && + check_obj \"one.git/objects <<-EOF + $one blob + EOF +' + +test_done diff --git a/t/t5802-connect-helper.sh b/t/t5802-connect-helper.sh index b7a7f9d588..c6c2661878 100755 --- a/t/t5802-connect-helper.sh +++ b/t/t5802-connect-helper.sh @@ -4,6 +4,7 @@ test_description='ext::cmd remote "connect" helper' . ./test-lib.sh test_expect_success setup ' + git config --global protocol.ext.allow user && test_tick && git commit --allow-empty -m initial && test_tick && diff --git a/t/t5812-proto-disable-http.sh b/t/t5812-proto-disable-http.sh index 0d105d5417..d911afd24c 100755 --- a/t/t5812-proto-disable-http.sh +++ b/t/t5812-proto-disable-http.sh @@ -18,6 +18,7 @@ test_proto "smart http" http "$HTTPD_URL/smart/repo.git" test_expect_success 'curl redirects respect whitelist' ' test_must_fail env GIT_ALLOW_PROTOCOL=http:https \ + GIT_SMART_HTTP=0 \ git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr && { test_i18ngrep "ftp.*disabled" stderr || @@ -29,5 +30,12 @@ test_expect_success 'curl limits redirects' ' test_must_fail git clone "$HTTPD_URL/loop-redir/smart/repo.git" ' +test_expect_success 'http can be limited to from-user' ' + git -c protocol.http.allow=user \ + clone "$HTTPD_URL/smart/repo.git" plain.git && + test_must_fail git -c protocol.http.allow=user \ + clone "$HTTPD_URL/smart-redir-perm/repo.git" redir.git +' + stop_httpd test_done diff --git a/t/t6000-rev-list-misc.sh b/t/t6000-rev-list-misc.sh index 3e752ce032..969e4e9e52 100755 --- a/t/t6000-rev-list-misc.sh +++ b/t/t6000-rev-list-misc.sh @@ -100,4 +100,18 @@ test_expect_success '--bisect and --first-parent can not be combined' ' test_must_fail git rev-list --bisect --first-parent HEAD ' +test_expect_success '--header shows a NUL after each commit' ' + # We know that there is no Q in the true payload; names and + # addresses of the authors and the committers do not have + # any, and object names or header names do not, either. + git rev-list --header --max-count=2 HEAD | + nul_to_q | + grep "^Q" >actual && + cat >expect <<-EOF && + Q$(git rev-parse HEAD~1) + Q + EOF + test_cmp expect actual +' + test_done diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh index 1408b608eb..2959745196 100755 --- a/t/t6007-rev-list-cherry-pick-file.sh +++ b/t/t6007-rev-list-cherry-pick-file.sh @@ -99,6 +99,44 @@ test_expect_success '--cherry-pick bar does not come up empty (II)' ' test_cmp actual.named expect ' +test_expect_success 'name-rev multiple --refs combine inclusive' ' + git rev-list --left-right --cherry-pick F...E -- bar >actual && + git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" \ + <actual >actual.named && + test_cmp actual.named expect +' + +cat >expect <<EOF +<tags/F +EOF + +test_expect_success 'name-rev --refs excludes non-matched patterns' ' + git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect && + git rev-list --left-right --cherry-pick F...E -- bar >actual && + git name-rev --stdin --name-only --refs="*tags/F" \ + <actual >actual.named && + test_cmp actual.named expect +' + +cat >expect <<EOF +<tags/F +EOF + +test_expect_success 'name-rev --exclude excludes matched patterns' ' + git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect && + git rev-list --left-right --cherry-pick F...E -- bar >actual && + git name-rev --stdin --name-only --refs="*tags/*" --exclude="*E" \ + <actual >actual.named && + test_cmp actual.named expect +' + +test_expect_success 'name-rev --no-refs clears the refs list' ' + git rev-list --left-right --cherry-pick F...E -- bar >expect && + git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \ + <expect >actual && + test_cmp actual expect +' + cat >expect <<EOF +tags/F =tags/D diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh index 5e5370feb4..8c2c6eaef8 100755 --- a/t/t6030-bisect-porcelain.sh +++ b/t/t6030-bisect-porcelain.sh @@ -407,7 +407,7 @@ test_expect_success 'good merge base when good and bad are siblings' ' test_i18ngrep "merge base must be tested" my_bisect_log.txt && grep $HASH4 my_bisect_log.txt && git bisect good > my_bisect_log.txt && - test_must_fail grep "merge base must be tested" my_bisect_log.txt && + ! grep "merge base must be tested" my_bisect_log.txt && grep $HASH6 my_bisect_log.txt && git bisect reset ' diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh index 3d5c238c81..97a07655a0 100755 --- a/t/t6040-tracking-info.sh +++ b/t/t6040-tracking-info.sh @@ -44,7 +44,7 @@ b1 [ahead 1, behind 1] d b2 [ahead 1, behind 1] d b3 [behind 1] b b4 [ahead 2] f -b5 g +b5 [gone] g b6 c EOF diff --git a/t/t6045-merge-rename-delete.sh b/t/t6045-merge-rename-delete.sh new file mode 100755 index 0000000000..5d33577d2f --- /dev/null +++ b/t/t6045-merge-rename-delete.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +test_description='Merge-recursive rename/delete conflict message' +. ./test-lib.sh + +test_expect_success 'rename/delete' ' + echo foo >A && + git add A && + git commit -m "initial" && + + git checkout -b rename && + git mv A B && + git commit -m "rename" && + + git checkout master && + git rm A && + git commit -m "delete" && + + test_must_fail git merge --strategy=recursive rename >output && + test_i18ngrep "CONFLICT (rename/delete): A deleted in HEAD and renamed to B in rename. Version rename of B left in tree." output +' + +test_done diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh index 1c6952d049..8c617981a3 100755 --- a/t/t6101-rev-parse-parents.sh +++ b/t/t6101-rev-parse-parents.sh @@ -83,12 +83,24 @@ test_expect_success 'final^1^@ = final^1^1 final^1^2' ' test_cmp expect actual ' +test_expect_success 'symbolic final^1^@ = final^1^1 final^1^2' ' + git rev-parse --symbolic final^1^1 final^1^2 >expect && + git rev-parse --symbolic 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 'symbolic final^1^! = final^1 ^final^1^1 ^final^1^2' ' + git rev-parse --symbolic final^1 ^final^1^1 ^final^1^2 >expect && + git rev-parse --symbolic final^1^! >actual && + test_cmp expect actual +' + test_expect_success 'large graft octopus' ' test_cmp_rev_output b31 "git rev-parse --verify b1^30" ' @@ -102,4 +114,104 @@ test_expect_success 'short SHA-1 works' ' test_cmp_rev_output start "git rev-parse ${start%?}" ' +# rev^- tests; we can use a simpler setup for these + +test_expect_success 'setup for rev^- tests' ' + test_commit one && + test_commit two && + test_commit three && + + # Merge in a branch for testing rev^- + git checkout -b branch && + git checkout HEAD^^ && + git merge -m merge --no-edit --no-ff branch && + git checkout -b merge +' + +# The merged branch has 2 commits + the merge +test_expect_success 'rev-list --count merge^- = merge^..merge' ' + git rev-list --count merge^..merge >expect && + echo 3 >actual && + test_cmp expect actual +' + +# All rev^- rev-parse tests + +test_expect_success 'rev-parse merge^- = merge^..merge' ' + git rev-parse merge^..merge >expect && + git rev-parse merge^- >actual && + test_cmp expect actual +' + +test_expect_success 'rev-parse merge^-1 = merge^..merge' ' + git rev-parse merge^1..merge >expect && + git rev-parse merge^-1 >actual && + test_cmp expect actual +' + +test_expect_success 'rev-parse merge^-2 = merge^2..merge' ' + git rev-parse merge^2..merge >expect && + git rev-parse merge^-2 >actual && + test_cmp expect actual +' + +test_expect_success 'symbolic merge^-1 = merge^1..merge' ' + git rev-parse --symbolic merge^1..merge >expect && + git rev-parse --symbolic merge^-1 >actual && + test_cmp expect actual +' + +test_expect_success 'rev-parse merge^-0 (invalid parent)' ' + test_must_fail git rev-parse merge^-0 +' + +test_expect_success 'rev-parse merge^-3 (invalid parent)' ' + test_must_fail git rev-parse merge^-3 +' + +test_expect_success 'rev-parse merge^-^ (garbage after ^-)' ' + test_must_fail git rev-parse merge^-^ +' + +test_expect_success 'rev-parse merge^-1x (garbage after ^-1)' ' + test_must_fail git rev-parse merge^-1x +' + +# All rev^- rev-list tests (should be mostly the same as rev-parse; the reason +# for the duplication is that rev-parse and rev-list use different parsers). + +test_expect_success 'rev-list merge^- = merge^..merge' ' + git rev-list merge^..merge >expect && + git rev-list merge^- >actual && + test_cmp expect actual +' + +test_expect_success 'rev-list merge^-1 = merge^1..merge' ' + git rev-list merge^1..merge >expect && + git rev-list merge^-1 >actual && + test_cmp expect actual +' + +test_expect_success 'rev-list merge^-2 = merge^2..merge' ' + git rev-list merge^2..merge >expect && + git rev-list merge^-2 >actual && + test_cmp expect actual +' + +test_expect_success 'rev-list merge^-0 (invalid parent)' ' + test_must_fail git rev-list merge^-0 +' + +test_expect_success 'rev-list merge^-3 (invalid parent)' ' + test_must_fail git rev-list merge^-3 +' + +test_expect_success 'rev-list merge^-^ (garbage after ^-)' ' + test_must_fail git rev-list merge^-^ +' + +test_expect_success 'rev-list merge^-1x (garbage after ^-1)' ' + test_must_fail git rev-list merge^-1x +' + test_done diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 85f269411c..167491fd5b 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -182,6 +182,10 @@ check_describe "test2-lightweight-*" --tags --match="test2-*" check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^ +check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^ + +check_describe "test2-lightweight-*" --long --tags --match="test1-*" --no-match --match="test2-*" HEAD^ + test_expect_success 'name-rev with exact tags' ' echo A >expect && tag_object=$(git rev-parse refs/tags/A) && @@ -206,4 +210,27 @@ test_expect_success 'describe --contains with the exact tags' ' test_cmp expect actual ' +test_expect_success 'describe --contains and --match' ' + echo "A^0" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + test_must_fail git describe --contains --match="B" $tagged_commit && + git describe --contains --match="B" --match="A" $tagged_commit >actual && + test_cmp expect actual +' + +test_expect_success 'describe --exclude' ' + echo "c~1" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + test_must_fail git describe --contains --match="B" $tagged_commit && + git describe --contains --match="?" --exclude="A" $tagged_commit >actual && + test_cmp expect actual +' + +test_expect_success 'describe --contains and --no-match' ' + echo "A^0" >expect && + tagged_commit=$(git rev-parse "refs/tags/A^0") && + git describe --contains --match="B" --no-match $tagged_commit >actual && + test_cmp expect actual +' + test_done diff --git a/t/t6132-pathspec-exclude.sh b/t/t6132-pathspec-exclude.sh index d51595cf6b..9dd5cde5fc 100755 --- a/t/t6132-pathspec-exclude.sh +++ b/t/t6132-pathspec-exclude.sh @@ -25,8 +25,10 @@ EOF test_cmp expect actual ' -test_expect_success 'exclude only should error out' ' - test_must_fail git log --oneline --format=%s -- ":(exclude)sub" +test_expect_success 'exclude only no longer errors out' ' + git log --oneline --format=%s -- . ":(exclude)sub" >expect && + git log --oneline --format=%s -- ":(exclude)sub" >actual && + test_cmp expect actual ' test_expect_success 't_e_i() exclude sub' ' diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh new file mode 100755 index 0000000000..fd401ca605 --- /dev/null +++ b/t/t6134-pathspec-in-submodule.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +test_description='test case exclude pathspec' + +. ./test-lib.sh + +test_expect_success 'setup a submodule' ' + test_create_repo pretzel && + : >pretzel/a && + git -C pretzel add a && + git -C pretzel commit -m "add a file" -- a && + git submodule add ./pretzel sub && + git commit -a -m "add submodule" && + git submodule deinit --all +' + +cat <<EOF >expect +fatal: Pathspec 'sub/a' is in submodule 'sub' +EOF + +test_expect_success 'error message for path inside submodule' ' + echo a >sub/a && + test_must_fail git add sub/a 2>actual && + test_cmp expect actual +' + +cat <<EOF >expect +fatal: Pathspec '.' is in submodule 'sub' +EOF + +test_expect_success 'error message for path inside submodule from within submodule' ' + test_must_fail git -C sub add . 2>actual && + test_cmp expect actual +' + +test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index 19a2823025..c87dc1f8bc 100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@ -38,6 +38,7 @@ test_atom() { case "$1" in head) ref=refs/heads/master ;; tag) ref=refs/tags/testtag ;; + sym) ref=refs/heads/sym ;; *) ref=$1 ;; esac printf '%s\n' "$3" >expected @@ -50,16 +51,40 @@ test_atom() { test_atom head refname refs/heads/master test_atom head refname:short master +test_atom head refname:lstrip=1 heads/master +test_atom head refname:lstrip=2 master +test_atom head refname:lstrip=-1 master +test_atom head refname:lstrip=-2 heads/master +test_atom head refname:rstrip=1 refs/heads +test_atom head refname:rstrip=2 refs +test_atom head refname:rstrip=-1 refs +test_atom head refname:rstrip=-2 refs/heads test_atom head refname:strip=1 heads/master test_atom head refname:strip=2 master +test_atom head refname:strip=-1 master +test_atom head refname:strip=-2 heads/master test_atom head upstream refs/remotes/origin/master test_atom head upstream:short origin/master +test_atom head upstream:lstrip=2 origin/master +test_atom head upstream:lstrip=-2 origin/master +test_atom head upstream:rstrip=2 refs/remotes +test_atom head upstream:rstrip=-2 refs/remotes +test_atom head upstream:strip=2 origin/master +test_atom head upstream:strip=-2 origin/master test_atom head push refs/remotes/myfork/master test_atom head push:short myfork/master +test_atom head push:lstrip=1 remotes/myfork/master +test_atom head push:lstrip=-1 master +test_atom head push:rstrip=1 refs/remotes/myfork +test_atom head push:rstrip=-1 refs +test_atom head push:strip=1 remotes/myfork/master +test_atom head push:strip=-1 master test_atom head objecttype commit test_atom head objectsize 171 test_atom head objectname $(git rev-parse refs/heads/master) test_atom head objectname:short $(git rev-parse --short refs/heads/master) +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master) +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master) test_atom head tree $(git rev-parse refs/heads/master^{tree}) test_atom head parent '' test_atom head numparent 0 @@ -99,6 +124,8 @@ test_atom tag objecttype tag test_atom tag objectsize 154 test_atom tag objectname $(git rev-parse refs/tags/testtag) test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag) +test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master) +test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master) test_atom tag tree '' test_atom tag parent '' test_atom tag numparent '' @@ -134,16 +161,6 @@ test_expect_success 'Check invalid atoms names are errors' ' test_must_fail git for-each-ref --format="%(INVALID)" refs/heads ' -test_expect_success 'arguments to :strip must be positive integers' ' - test_must_fail git for-each-ref --format="%(refname:strip=0)" && - test_must_fail git for-each-ref --format="%(refname:strip=-1)" && - test_must_fail git for-each-ref --format="%(refname:strip=foo)" -' - -test_expect_success 'stripping refnames too far gives an error' ' - test_must_fail git for-each-ref --format="%(refname:strip=3)" -' - test_expect_success 'Check format specifiers are ignored in naming date atoms' ' git for-each-ref --format="%(authordate)" refs/heads && git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads && @@ -164,6 +181,12 @@ test_expect_success 'Check invalid format specifiers are errors' ' test_must_fail git for-each-ref --format="%(authordate:INVALID)" refs/heads ' +test_expect_success 'arguments to %(objectname:short=) must be positive integers' ' + test_must_fail git for-each-ref --format="%(objectname:short=0)" && + test_must_fail git for-each-ref --format="%(objectname:short=-1)" && + test_must_fail git for-each-ref --format="%(objectname:short=foo)" +' + test_date () { f=$1 && committer_date=$2 && @@ -362,6 +385,8 @@ test_expect_success 'setup for upstream:track[short]' ' test_atom head upstream:track '[ahead 1]' test_atom head upstream:trackshort '>' +test_atom head upstream:track,nobracket 'ahead 1' +test_atom head upstream:nobracket,track 'ahead 1' test_atom head push:track '[ahead 1]' test_atom head push:trackshort '>' @@ -372,7 +397,7 @@ test_expect_success 'Check that :track[short] cannot be used with other atoms' ' test_expect_success 'Check that :track[short] works when upstream is invalid' ' cat >expected <<-\EOF && - + [gone] EOF test_when_finished "git config branch.master.merge refs/heads/master" && @@ -553,4 +578,88 @@ test_expect_success 'Verify sort with multiple keys' ' refs/tags/bogo refs/tags/master > actual && test_cmp expected actual ' + + +test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' ' + test_when_finished "git checkout master" && + git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && + sed -e "s/^\* / /" actual >expect && + git checkout --orphan HEAD && + git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual && + test_cmp expect actual +' + +cat >trailers <<EOF +Reviewed-by: A U Thor <author@example.com> +Signed-off-by: A U Thor <author@example.com> +EOF + +test_expect_success 'basic atom: head contents:trailers' ' + echo "Some contents" > two && + git add two && + git commit -F - <<-EOF && + trailers: this commit message has trailers + + Some message contents + + $(cat trailers) + EOF + git for-each-ref --format="%(contents:trailers)" refs/heads/master >actual && + sanitize_pgp <actual >actual.clean && + # git for-each-ref ends with a blank line + cat >expect <<-EOF && + $(cat trailers) + + EOF + test_cmp expect actual.clean +' + +test_expect_success 'Add symbolic ref for the following tests' ' + git symbolic-ref refs/heads/sym refs/heads/master +' + +cat >expected <<EOF +refs/heads/master +EOF + +test_expect_success 'Verify usage of %(symref) atom' ' + git for-each-ref --format="%(symref)" refs/heads/sym >actual && + test_cmp expected actual +' + +cat >expected <<EOF +heads/master +EOF + +test_expect_success 'Verify usage of %(symref:short) atom' ' + git for-each-ref --format="%(symref:short)" refs/heads/sym >actual && + test_cmp expected actual +' + +cat >expected <<EOF +master +heads/master +EOF + +test_expect_success 'Verify usage of %(symref:lstrip) atom' ' + git for-each-ref --format="%(symref:lstrip=2)" refs/heads/sym > actual && + git for-each-ref --format="%(symref:lstrip=-2)" refs/heads/sym >> actual && + test_cmp expected actual && + + git for-each-ref --format="%(symref:strip=2)" refs/heads/sym > actual && + git for-each-ref --format="%(symref:strip=-2)" refs/heads/sym >> actual && + test_cmp expected actual +' + +cat >expected <<EOF +refs +refs/heads +EOF + +test_expect_success 'Verify usage of %(symref:rstrip) atom' ' + git for-each-ref --format="%(symref:rstrip=2)" refs/heads/sym > actual && + git for-each-ref --format="%(symref:rstrip=-2)" refs/heads/sym >> actual && + test_cmp expected actual +' + test_done diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh index d0ab09f4bd..a09a1a46ef 100755 --- a/t/t6302-for-each-ref-filter.sh +++ b/t/t6302-for-each-ref-filter.sh @@ -327,4 +327,98 @@ test_expect_success 'reverse version sort' ' test_cmp expect actual ' +test_expect_success 'improper usage of %(if), %(then), %(else) and %(end) atoms' ' + test_must_fail git for-each-ref --format="%(if)" && + test_must_fail git for-each-ref --format="%(then) %(end)" && + test_must_fail git for-each-ref --format="%(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(then) %(then) %(end)" && + test_must_fail git for-each-ref --format="%(then) %(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(then) %(else)" && + test_must_fail git for-each-ref --format="%(if) %(else) %(then) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(then) %(else) %(else) %(end)" && + test_must_fail git for-each-ref --format="%(if) %(end)" +' + +test_expect_success 'check %(if)...%(then)...%(end) atoms' ' + git for-each-ref --format="%(refname)%(if)%(authorname)%(then) Author: %(authorname)%(end)" >actual && + cat >expect <<-\EOF && + refs/heads/master Author: A U Thor + refs/heads/side Author: A U Thor + refs/odd/spot Author: A U Thor + refs/tags/annotated-tag + refs/tags/doubly-annotated-tag + refs/tags/doubly-signed-tag + refs/tags/foo1.10 Author: A U Thor + refs/tags/foo1.3 Author: A U Thor + refs/tags/foo1.6 Author: A U Thor + refs/tags/four Author: A U Thor + refs/tags/one Author: A U Thor + refs/tags/signed-tag + refs/tags/three Author: A U Thor + refs/tags/two Author: A U Thor + EOF + test_cmp expect actual +' + +test_expect_success 'check %(if)...%(then)...%(else)...%(end) atoms' ' + git for-each-ref --format="%(if)%(authorname)%(then)%(authorname)%(else)No author%(end): %(refname)" >actual && + cat >expect <<-\EOF && + A U Thor: refs/heads/master + A U Thor: refs/heads/side + A U Thor: refs/odd/spot + No author: refs/tags/annotated-tag + No author: refs/tags/doubly-annotated-tag + No author: refs/tags/doubly-signed-tag + A U Thor: refs/tags/foo1.10 + A U Thor: refs/tags/foo1.3 + A U Thor: refs/tags/foo1.6 + A U Thor: refs/tags/four + A U Thor: refs/tags/one + No author: refs/tags/signed-tag + A U Thor: refs/tags/three + A U Thor: refs/tags/two + EOF + test_cmp expect actual +' +test_expect_success 'ignore spaces in %(if) atom usage' ' + git for-each-ref --format="%(refname:short): %(if)%(HEAD)%(then)Head ref%(else)Not Head ref%(end)" >actual && + cat >expect <<-\EOF && + master: Head ref + side: Not Head ref + odd/spot: Not Head ref + annotated-tag: Not Head ref + doubly-annotated-tag: Not Head ref + doubly-signed-tag: Not Head ref + foo1.10: Not Head ref + foo1.3: Not Head ref + foo1.6: Not Head ref + four: Not Head ref + one: Not Head ref + signed-tag: Not Head ref + three: Not Head ref + two: Not Head ref + EOF + test_cmp expect actual +' + +test_expect_success 'check %(if:equals=<string>)' ' + git for-each-ref --format="%(if:equals=master)%(refname:short)%(then)Found master%(else)Not master%(end)" refs/heads/ >actual && + cat >expect <<-\EOF && + Found master + Not master + EOF + test_cmp expect actual +' + +test_expect_success 'check %(if:notequals=<string>)' ' + git for-each-ref --format="%(if:notequals=master)%(refname:short)%(then)Not master%(else)Found master%(end)" refs/heads/ >actual && + cat >expect <<-\EOF && + Found master + Not master + EOF + test_cmp expect actual +' + test_done diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh index 5d7d414617..08de2e8ab0 100755 --- a/t/t6500-gc.sh +++ b/t/t6500-gc.sh @@ -43,4 +43,44 @@ test_expect_success 'gc is not aborted due to a stale symref' ' ) ' +test_expect_success 'auto gc with too many loose objects does not attempt to create bitmaps' ' + test_config gc.auto 3 && + test_config gc.autodetach false && + test_config pack.writebitmaps true && + # We need to create two object whose sha1s start with 17 + # since this is what git gc counts. As it happens, these + # two blobs will do so. + test_commit 263 && + test_commit 410 && + # Our first gc will create a pack; our second will create a second pack + git gc --auto && + ls .git/objects/pack | sort >existing_packs && + test_commit 523 && + test_commit 790 && + + git gc --auto 2>err && + test_i18ngrep ! "^warning:" err && + ls .git/objects/pack/ | sort >post_packs && + comm -1 -3 existing_packs post_packs >new && + comm -2 -3 existing_packs post_packs >del && + test_line_count = 0 del && # No packs are deleted + test_line_count = 2 new # There is one new pack and its .idx +' + +test_expect_success 'background auto gc does not run if gc.log is present and recent but does if it is old' ' + test_commit foo && + test_commit bar && + git repack && + test_config gc.autopacklimit 1 && + test_config gc.autodetach true && + echo fleem >.git/gc.log && + test_must_fail git gc --auto 2>err && + test_i18ngrep "^error:" err && + test_config gc.logexpiry 5.days && + test-chmtime =-345600 .git/gc.log && + test_must_fail git gc --auto && + test_config gc.logexpiry 2.days && + git gc --auto +' + test_done diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh index 8b0f71a2ac..b4698ab5f5 100755 --- a/t/t7004-tag.sh +++ b/t/t7004-tag.sh @@ -27,6 +27,30 @@ test_expect_success 'listing all tags in an empty tree should output nothing' ' test $(git tag | wc -l) -eq 0 ' +test_expect_success 'sort tags, ignore case' ' + ( + git init sort && + cd sort && + test_commit initial && + git tag tag-one && + git tag TAG-two && + git tag -l >actual && + cat >expected <<-\EOF && + TAG-two + initial + tag-one + EOF + test_cmp expected actual && + git tag -l -i >actual && + cat >expected <<-\EOF && + initial + tag-one + TAG-two + EOF + test_cmp expected actual + ) +' + test_expect_success 'looking for a tag in an empty tree should fail' \ '! (tag_exists mytag)' @@ -47,6 +71,7 @@ test_expect_success 'creating a tag for an unknown revision should fail' ' # commit used in the tests, test_tick is also called here to freeze the date: test_expect_success 'creating a tag using default HEAD should succeed' ' + test_config core.logAllRefUpdates true && test_tick && echo foo >foo && git add foo && @@ -56,9 +81,25 @@ test_expect_success 'creating a tag using default HEAD should succeed' ' ' test_expect_success 'creating a tag with --create-reflog should create reflog' ' + git log -1 \ + --format="format:tag: tagging %h (%s, %cd)%n" \ + --date=format:%Y-%m-%d >expected && test_when_finished "git tag -d tag_with_reflog" && git tag --create-reflog tag_with_reflog && - git reflog exists refs/tags/tag_with_reflog + git reflog exists refs/tags/tag_with_reflog && + sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual && + test_cmp expected actual +' + +test_expect_success 'annotated tag with --create-reflog has correct message' ' + git log -1 \ + --format="format:tag: tagging %h (%s, %cd)%n" \ + --date=format:%Y-%m-%d >expected && + test_when_finished "git tag -d tag_with_reflog" && + git tag -m "annotated tag" --create-reflog tag_with_reflog && + git reflog exists refs/tags/tag_with_reflog && + sed -e "s/^.* //" .git/logs/refs/tags/tag_with_reflog >actual && + test_cmp expected actual ' test_expect_success '--create-reflog does not create reflog on failure' ' @@ -66,6 +107,13 @@ test_expect_success '--create-reflog does not create reflog on failure' ' test_must_fail git reflog exists refs/tags/mytag ' +test_expect_success 'option core.logAllRefUpdates=always creates reflog' ' + test_when_finished "git tag -d tag_with_reflog" && + test_config core.logAllRefUpdates always && + git tag tag_with_reflog && + git reflog exists refs/tags/tag_with_reflog +' + test_expect_success 'listing all tags if one exists should succeed' ' git tag -l && git tag @@ -81,6 +129,9 @@ test_expect_success 'listing all tags if one exists should output that tag' ' test_expect_success 'listing a tag using a matching pattern should succeed' \ 'git tag -l mytag' +test_expect_success 'listing a tag with --ignore-case' \ + 'test $(git tag -l --ignore-case MYTAG) = mytag' + test_expect_success \ 'listing a tag using a matching pattern should output that tag' \ 'test $(git tag -l mytag) = mytag' @@ -122,11 +173,11 @@ test_expect_success '--force can create a tag with the name of one existing' ' tag_exists mytag' test_expect_success '--force is moot with a non-existing tag name' ' + test_when_finished git tag -d newtag forcetag && git tag newtag >expect && git tag --force forcetag >actual && test_cmp expect actual ' -git tag -d newtag forcetag # deleting tags: @@ -297,11 +348,9 @@ EOF ' test_expect_success 'listing tags in column with column.*' ' - git config column.tag row && - git config column.ui dense && + test_config column.tag row && + test_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 @@ -314,9 +363,8 @@ test_expect_success 'listing tag with -n --column should fail' ' ' test_expect_success 'listing tags -n in column with column.ui ignored' ' - git config column.ui "row dense" && + test_config column.ui "row dense" && COLUMNS=40 git tag -l -n >actual && - git config --unset column.ui && cat >expected <<\EOF && a1 Foo aa1 Foo @@ -847,6 +895,22 @@ test_expect_success GPG 'verifying a forged tag should fail' ' test_must_fail git tag -v forged-tag ' +test_expect_success 'verifying a proper tag with --format pass and format accordingly' ' + cat >expect <<-\EOF + tagname : signed-tag + EOF && + git tag -v --format="tagname : %(tag)" "signed-tag" >actual && + test_cmp expect actual +' + +test_expect_success 'verifying a forged tag with --format fail and format accordingly' ' + cat >expect <<-\EOF + tagname : forged-tag + EOF && + test_must_fail git tag -v --format="tagname : %(tag)" "forged-tag" >actual && + test_cmp expect actual +' + # blank and empty messages for signed tags: get_tag_header empty-signed-tag $commit commit $time >expect @@ -1200,11 +1264,10 @@ test_expect_success GPG,RFC1991 \ ' # try to sign with bad user.signingkey -git config user.signingkey BobTheMouse test_expect_success GPG \ 'git tag -s fails if gpg is misconfigured (bad key)' \ - 'test_must_fail git tag -s -m tail tag-gpg-failure' -git config --unset user.signingkey + 'test_config user.signingkey BobTheMouse && + test_must_fail git tag -s -m tail tag-gpg-failure' # try to produce invalid signature test_expect_success GPG \ @@ -1484,7 +1547,7 @@ test_expect_success 'reverse lexical sort' ' ' test_expect_success 'configured lexical sort' ' - git config tag.sort "v:refname" && + test_config tag.sort "v:refname" && git tag -l "foo*" >actual && cat >expect <<-\EOF && foo1.3 @@ -1495,6 +1558,7 @@ test_expect_success 'configured lexical sort' ' ' test_expect_success 'option override configured sort' ' + test_config tag.sort "v:refname" && git tag -l --sort=-refname "foo*" >actual && cat >expect <<-\EOF && foo1.6 @@ -1509,13 +1573,12 @@ test_expect_success 'invalid sort parameter on command line' ' ' test_expect_success 'invalid sort parameter in configuratoin' ' - git config tag.sort "v:notvalid" && + test_config tag.sort "v:notvalid" && test_must_fail git tag -l "foo*" ' test_expect_success 'version sort with prerelease reordering' ' - git config --unset tag.sort && - git config versionsort.prereleaseSuffix -rc && + test_config versionsort.prereleaseSuffix -rc && git tag foo1.6-rc1 && git tag foo1.6-rc2 && git tag -l --sort=version:refname "foo*" >actual && @@ -1530,6 +1593,7 @@ test_expect_success 'version sort with prerelease reordering' ' ' test_expect_success 'reverse version sort with prerelease reordering' ' + test_config versionsort.prereleaseSuffix -rc && git tag -l --sort=-version:refname "foo*" >actual && cat >expect <<-\EOF && foo1.10 @@ -1541,6 +1605,103 @@ test_expect_success 'reverse version sort with prerelease reordering' ' test_cmp expect actual ' +test_expect_success 'version sort with prerelease reordering and common leading character' ' + test_config versionsort.prereleaseSuffix -before && + git tag foo1.7-before1 && + git tag foo1.7 && + git tag foo1.7-after1 && + git tag -l --sort=version:refname "foo1.7*" >actual && + cat >expect <<-\EOF && + foo1.7-before1 + foo1.7 + foo1.7-after1 + EOF + test_cmp expect actual +' + +test_expect_success 'version sort with prerelease reordering, multiple suffixes and common leading character' ' + test_config versionsort.prereleaseSuffix -before && + git config --add versionsort.prereleaseSuffix -after && + git tag -l --sort=version:refname "foo1.7*" >actual && + cat >expect <<-\EOF && + foo1.7-before1 + foo1.7-after1 + foo1.7 + EOF + test_cmp expect actual +' + +test_expect_success 'version sort with prerelease reordering, multiple suffixes match the same tag' ' + test_config versionsort.prereleaseSuffix -bar && + git config --add versionsort.prereleaseSuffix -foo-baz && + git config --add versionsort.prereleaseSuffix -foo-bar && + git tag foo1.8-foo-bar && + git tag foo1.8-foo-baz && + git tag foo1.8 && + git tag -l --sort=version:refname "foo1.8*" >actual && + cat >expect <<-\EOF && + foo1.8-foo-baz + foo1.8-foo-bar + foo1.8 + EOF + test_cmp expect actual +' + +test_expect_success 'version sort with prerelease reordering, multiple suffixes match starting at the same position' ' + test_config versionsort.prereleaseSuffix -pre && + git config --add versionsort.prereleaseSuffix -prerelease && + git tag foo1.9-pre1 && + git tag foo1.9-pre2 && + git tag foo1.9-prerelease1 && + git tag -l --sort=version:refname "foo1.9*" >actual && + cat >expect <<-\EOF && + foo1.9-pre1 + foo1.9-pre2 + foo1.9-prerelease1 + EOF + test_cmp expect actual +' + +test_expect_success 'version sort with general suffix reordering' ' + test_config versionsort.suffix -alpha && + git config --add versionsort.suffix -beta && + git config --add versionsort.suffix "" && + git config --add versionsort.suffix -gamma && + git config --add versionsort.suffix -delta && + git tag foo1.10-alpha && + git tag foo1.10-beta && + git tag foo1.10-gamma && + git tag foo1.10-delta && + git tag foo1.10-unlisted-suffix && + git tag -l --sort=version:refname "foo1.10*" >actual && + cat >expect <<-\EOF && + foo1.10-alpha + foo1.10-beta + foo1.10 + foo1.10-unlisted-suffix + foo1.10-gamma + foo1.10-delta + EOF + test_cmp expect actual +' + +test_expect_success 'versionsort.suffix overrides versionsort.prereleaseSuffix' ' + test_config versionsort.suffix -before && + test_config versionsort.prereleaseSuffix -after && + git tag -l --sort=version:refname "foo1.7*" >actual && + cat >expect <<-\EOF && + foo1.7-before1 + foo1.7 + foo1.7-after1 + EOF + test_cmp expect actual +' + +test_expect_success 'version sort with very long prerelease suffix' ' + test_config versionsort.prereleaseSuffix -very-looooooooooooooooooooooooong-prerelease-suffix && + git tag -l --sort=version:refname +' + run_with_limited_stack () { (ulimit -s 128 && "$@") } @@ -1569,13 +1730,11 @@ EOF" test_expect_success '--format should list tags as per format given' ' cat >expect <<-\EOF && - refname : refs/tags/foo1.10 - refname : refs/tags/foo1.3 - refname : refs/tags/foo1.6 - refname : refs/tags/foo1.6-rc1 - refname : refs/tags/foo1.6-rc2 + refname : refs/tags/v1.0 + refname : refs/tags/v1.0.1 + refname : refs/tags/v1.1.3 EOF - git tag -l --format="refname : %(refname)" "foo*" >actual && + git tag -l --format="refname : %(refname)" "v1*" >actual && test_cmp expect actual ' diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh index 07079a41c4..d62ccbb98e 100755 --- a/t/t7030-verify-tag.sh +++ b/t/t7030-verify-tag.sh @@ -125,4 +125,20 @@ test_expect_success GPG 'verify multiple tags' ' test_cmp expect.stderr actual.stderr ' +test_expect_success 'verifying tag with --format' ' + cat >expect <<-\EOF + tagname : fourth-signed + EOF && + git verify-tag --format="tagname : %(tag)" "fourth-signed" >actual && + test_cmp expect actual +' + +test_expect_success 'verifying a forged tag with --format fail and format accordingly' ' + cat >expect <<-\EOF + tagname : 7th forged-signed + EOF && + test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged && + test_cmp expect actual-forged +' + test_done diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh index 4d17363a92..53cf42fac1 100755 --- a/t/t7060-wtstatus.sh +++ b/t/t7060-wtstatus.sh @@ -232,4 +232,25 @@ test_expect_success 'status --branch with detached HEAD' ' test_i18ncmp expected actual ' +## Duplicate the above test and verify --porcelain=v1 arg parsing. +test_expect_success 'status --porcelain=v1 --branch with detached HEAD' ' + git reset --hard && + git checkout master^0 && + git status --branch --porcelain=v1 >actual && + cat >expected <<-EOF && + ## HEAD (no branch) + ?? .gitconfig + ?? actual + ?? expect + ?? expected + ?? mdconflict/ + EOF + test_i18ncmp expected actual +' + +## Verify parser error on invalid --porcelain argument. +test_expect_success 'status --porcelain=bogus' ' + test_must_fail git status --porcelain=bogus +' + test_done diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh new file mode 100755 index 0000000000..e319fa2e84 --- /dev/null +++ b/t/t7064-wtstatus-pv2.sh @@ -0,0 +1,593 @@ +#!/bin/sh + +test_description='git status --porcelain=v2 + +This test exercises porcelain V2 output for git status.' + + +. ./test-lib.sh + + +test_expect_success setup ' + test_tick && + git config core.autocrlf false && + echo x >file_x && + echo y >file_y && + echo z >file_z && + mkdir dir1 && + echo a >dir1/file_a && + echo b >dir1/file_b +' + +test_expect_success 'before initial commit, nothing added, only untracked' ' + cat >expect <<-EOF && + # branch.oid (initial) + # branch.head master + ? actual + ? dir1/ + ? expect + ? file_x + ? file_y + ? file_z + EOF + + git status --porcelain=v2 --branch --untracked-files=normal >actual && + test_cmp expect actual +' + +test_expect_success 'before initial commit, things added' ' + git add file_x file_y file_z dir1 && + OID_A=$(git hash-object -t blob -- dir1/file_a) && + OID_B=$(git hash-object -t blob -- dir1/file_b) && + OID_X=$(git hash-object -t blob -- file_x) && + OID_Y=$(git hash-object -t blob -- file_y) && + OID_Z=$(git hash-object -t blob -- file_z) && + + cat >expect <<-EOF && + # branch.oid (initial) + # branch.head master + 1 A. N... 000000 100644 100644 $_z40 $OID_A dir1/file_a + 1 A. N... 000000 100644 100644 $_z40 $OID_B dir1/file_b + 1 A. N... 000000 100644 100644 $_z40 $OID_X file_x + 1 A. N... 000000 100644 100644 $_z40 $OID_Y file_y + 1 A. N... 000000 100644 100644 $_z40 $OID_Z file_z + ? actual + ? expect + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'before initial commit, things added (-z)' ' + lf_to_nul >expect <<-EOF && + # branch.oid (initial) + # branch.head master + 1 A. N... 000000 100644 100644 $_z40 $OID_A dir1/file_a + 1 A. N... 000000 100644 100644 $_z40 $OID_B dir1/file_b + 1 A. N... 000000 100644 100644 $_z40 $OID_X file_x + 1 A. N... 000000 100644 100644 $_z40 $OID_Y file_y + 1 A. N... 000000 100644 100644 $_z40 $OID_Z file_z + ? actual + ? expect + EOF + + git status -z --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'make first commit, comfirm HEAD oid and branch' ' + git commit -m initial && + H0=$(git rev-parse HEAD) && + cat >expect <<-EOF && + # branch.oid $H0 + # branch.head master + ? actual + ? expect + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'after first commit, create unstaged changes' ' + echo x >>file_x && + OID_X1=$(git hash-object -t blob -- file_x) && + rm file_z && + H0=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $H0 + # branch.head master + 1 .M N... 100644 100644 100644 $OID_X $OID_X file_x + 1 .D N... 100644 100644 000000 $OID_Z $OID_Z file_z + ? actual + ? expect + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'after first commit but omit untracked files and branch' ' + cat >expect <<-EOF && + 1 .M N... 100644 100644 100644 $OID_X $OID_X file_x + 1 .D N... 100644 100644 000000 $OID_Z $OID_Z file_z + EOF + + git status --porcelain=v2 --untracked-files=no >actual && + test_cmp expect actual +' + +test_expect_success 'after first commit, stage existing changes' ' + git add file_x && + git rm file_z && + H0=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $H0 + # branch.head master + 1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x + 1 D. N... 100644 000000 000000 $OID_Z $_z40 file_z + ? actual + ? expect + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'rename causes 2 path lines' ' + git mv file_y renamed_y && + H0=$(git rev-parse HEAD) && + + q_to_tab >expect <<-EOF && + # branch.oid $H0 + # branch.head master + 1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x + 1 D. N... 100644 000000 000000 $OID_Z $_z40 file_z + 2 R. N... 100644 100644 100644 $OID_Y $OID_Y R100 renamed_yQfile_y + ? actual + ? expect + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'rename causes 2 path lines (-z)' ' + H0=$(git rev-parse HEAD) && + + ## Lines use NUL path separator and line terminator, so double transform here. + q_to_nul <<-EOF | lf_to_nul >expect && + # branch.oid $H0 + # branch.head master + 1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x + 1 D. N... 100644 000000 000000 $OID_Z $_z40 file_z + 2 R. N... 100644 100644 100644 $OID_Y $OID_Y R100 renamed_yQfile_y + ? actual + ? expect + EOF + + git status --porcelain=v2 --branch --untracked-files=all -z >actual && + test_cmp expect actual +' + +test_expect_success 'make second commit, confirm clean and new HEAD oid' ' + git commit -m second && + H1=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $H1 + # branch.head master + ? actual + ? expect + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'confirm ignored files are not printed' ' + test_when_finished "rm -f x.ign .gitignore" && + echo x.ign >.gitignore && + echo "ignore me" >x.ign && + + cat >expect <<-EOF && + ? .gitignore + ? actual + ? expect + EOF + + git status --porcelain=v2 --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'ignored files are printed with --ignored' ' + test_when_finished "rm -f x.ign .gitignore" && + echo x.ign >.gitignore && + echo "ignore me" >x.ign && + + cat >expect <<-EOF && + ? .gitignore + ? actual + ? expect + ! x.ign + EOF + + git status --porcelain=v2 --ignored --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'create and commit permanent ignore file' ' + cat >.gitignore <<-EOF && + actual* + expect* + EOF + + git add .gitignore && + git commit -m ignore_trash && + H1=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $H1 + # branch.head master + EOF + + git status --porcelain=v2 --branch >actual && + test_cmp expect actual +' + +test_expect_success 'verify --intent-to-add output' ' + test_when_finished "git rm -f intent1.add intent2.add" && + touch intent1.add && + echo test >intent2.add && + + git add --intent-to-add intent1.add intent2.add && + + cat >expect <<-EOF && + 1 .A N... 000000 000000 100644 $_z40 $_z40 intent1.add + 1 .A N... 000000 000000 100644 $_z40 $_z40 intent2.add + EOF + + git status --porcelain=v2 >actual && + test_cmp expect actual +' + +test_expect_success 'verify AA (add-add) conflict' ' + test_when_finished "git reset --hard" && + + git branch AA_A master && + git checkout AA_A && + echo "Branch AA_A" >conflict.txt && + OID_AA_A=$(git hash-object -t blob -- conflict.txt) && + git add conflict.txt && + git commit -m "branch aa_a" && + + git branch AA_B master && + git checkout AA_B && + echo "Branch AA_B" >conflict.txt && + OID_AA_B=$(git hash-object -t blob -- conflict.txt) && + git add conflict.txt && + git commit -m "branch aa_b" && + + git branch AA_M AA_B && + git checkout AA_M && + test_must_fail git merge AA_A && + + HM=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $HM + # branch.head AA_M + u AA N... 000000 100644 100644 100644 $_z40 $OID_AA_B $OID_AA_A conflict.txt + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'verify UU (edit-edit) conflict' ' + test_when_finished "git reset --hard" && + + git branch UU_ANC master && + git checkout UU_ANC && + echo "Ancestor" >conflict.txt && + OID_UU_ANC=$(git hash-object -t blob -- conflict.txt) && + git add conflict.txt && + git commit -m "UU_ANC" && + + git branch UU_A UU_ANC && + git checkout UU_A && + echo "Branch UU_A" >conflict.txt && + OID_UU_A=$(git hash-object -t blob -- conflict.txt) && + git add conflict.txt && + git commit -m "branch uu_a" && + + git branch UU_B UU_ANC && + git checkout UU_B && + echo "Branch UU_B" >conflict.txt && + OID_UU_B=$(git hash-object -t blob -- conflict.txt) && + git add conflict.txt && + git commit -m "branch uu_b" && + + git branch UU_M UU_B && + git checkout UU_M && + test_must_fail git merge UU_A && + + HM=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $HM + # branch.head UU_M + u UU N... 100644 100644 100644 100644 $OID_UU_ANC $OID_UU_B $OID_UU_A conflict.txt + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual +' + +test_expect_success 'verify upstream fields in branch header' ' + git checkout master && + test_when_finished "rm -rf sub_repo" && + git clone . sub_repo && + ( + ## Confirm local master tracks remote master. + cd sub_repo && + HUF=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $HUF + # branch.head master + # branch.upstream origin/master + # branch.ab +0 -0 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual && + + ## Test ahead/behind. + echo xyz >file_xyz && + git add file_xyz && + git commit -m xyz && + + HUF=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $HUF + # branch.head master + # branch.upstream origin/master + # branch.ab +1 -0 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual && + + ## Repeat the above but without --branch. + cat >expect <<-EOF && + EOF + + git status --porcelain=v2 --untracked-files=all >actual && + test_cmp expect actual && + + ## Test upstream-gone case. Fake this by pointing origin/master at + ## a non-existing commit. + OLD=$(git rev-parse origin/master) && + NEW=$_z40 && + mv .git/packed-refs .git/old-packed-refs && + sed "s/$OLD/$NEW/g" <.git/old-packed-refs >.git/packed-refs && + + HUF=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $HUF + # branch.head master + # branch.upstream origin/master + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + +test_expect_success 'create and add submodule, submodule appears clean (A. S...)' ' + git checkout master && + git clone . sub_repo && + git clone . super_repo && + ( cd super_repo && + git submodule add ../sub_repo sub1 && + + ## Confirm stage/add of clean submodule. + HMOD=$(git hash-object -t blob -- .gitmodules) && + HSUP=$(git rev-parse HEAD) && + HSUB=$HSUP && + + cat >expect <<-EOF && + # branch.oid $HSUP + # branch.head master + # branch.upstream origin/master + # branch.ab +0 -0 + 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules + 1 A. S... 000000 160000 160000 $_z40 $HSUB sub1 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + +test_expect_success 'untracked changes in added submodule (AM S..U)' ' + ( cd super_repo && + ## create untracked file in the submodule. + ( cd sub1 && + echo "xxxx" >file_in_sub + ) && + + HMOD=$(git hash-object -t blob -- .gitmodules) && + HSUP=$(git rev-parse HEAD) && + HSUB=$HSUP && + + cat >expect <<-EOF && + # branch.oid $HSUP + # branch.head master + # branch.upstream origin/master + # branch.ab +0 -0 + 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules + 1 AM S..U 000000 160000 160000 $_z40 $HSUB sub1 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + +test_expect_success 'staged changes in added submodule (AM S.M.)' ' + ( cd super_repo && + ## stage the changes in the submodule. + ( cd sub1 && + git add file_in_sub + ) && + + HMOD=$(git hash-object -t blob -- .gitmodules) && + HSUP=$(git rev-parse HEAD) && + HSUB=$HSUP && + + cat >expect <<-EOF && + # branch.oid $HSUP + # branch.head master + # branch.upstream origin/master + # branch.ab +0 -0 + 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules + 1 AM S.M. 000000 160000 160000 $_z40 $HSUB sub1 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + +test_expect_success 'staged and unstaged changes in added (AM S.M.)' ' + ( cd super_repo && + ( cd sub1 && + ## make additional unstaged changes (on the same file) in the submodule. + ## This does not cause us to get S.MU (because the submodule does not report + ## a "?" line for the unstaged changes). + echo "more changes" >>file_in_sub + ) && + + HMOD=$(git hash-object -t blob -- .gitmodules) && + HSUP=$(git rev-parse HEAD) && + HSUB=$HSUP && + + cat >expect <<-EOF && + # branch.oid $HSUP + # branch.head master + # branch.upstream origin/master + # branch.ab +0 -0 + 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules + 1 AM S.M. 000000 160000 160000 $_z40 $HSUB sub1 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + +test_expect_success 'staged and untracked changes in added submodule (AM S.MU)' ' + ( cd super_repo && + ( cd sub1 && + ## stage new changes in tracked file. + git add file_in_sub && + ## create new untracked file. + echo "yyyy" >>another_file_in_sub + ) && + + HMOD=$(git hash-object -t blob -- .gitmodules) && + HSUP=$(git rev-parse HEAD) && + HSUB=$HSUP && + + cat >expect <<-EOF && + # branch.oid $HSUP + # branch.head master + # branch.upstream origin/master + # branch.ab +0 -0 + 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules + 1 AM S.MU 000000 160000 160000 $_z40 $HSUB sub1 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + +test_expect_success 'commit within the submodule appears as new commit in super (AM SC..)' ' + ( cd super_repo && + ( cd sub1 && + ## Make a new commit in the submodule. + git add file_in_sub && + rm -f another_file_in_sub && + git commit -m "new commit" + ) && + + HMOD=$(git hash-object -t blob -- .gitmodules) && + HSUP=$(git rev-parse HEAD) && + HSUB=$HSUP && + + cat >expect <<-EOF && + # branch.oid $HSUP + # branch.head master + # branch.upstream origin/master + # branch.ab +0 -0 + 1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules + 1 AM SC.. 000000 160000 160000 $_z40 $HSUB sub1 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + +test_expect_success 'stage submodule in super and commit' ' + ( cd super_repo && + ## Stage the new submodule commit in the super. + git add sub1 && + ## Commit the super so that the sub no longer appears as added. + git commit -m "super commit" && + + HSUP=$(git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $HSUP + # branch.head master + # branch.upstream origin/master + # branch.ab +1 -0 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + +test_expect_success 'make unstaged changes in existing submodule (.M S.M.)' ' + ( cd super_repo && + ( cd sub1 && + echo "zzzz" >>file_in_sub + ) && + + HSUP=$(git rev-parse HEAD) && + HSUB=$(cd sub1 && git rev-parse HEAD) && + + cat >expect <<-EOF && + # branch.oid $HSUP + # branch.head master + # branch.upstream origin/master + # branch.ab +1 -0 + 1 .M S.M. 160000 160000 160000 $HSUB $HSUB sub1 + EOF + + git status --porcelain=v2 --branch --untracked-files=all >actual && + test_cmp expect actual + ) +' + +test_done diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index b77cce8e40..c09ce0d4c1 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -152,6 +152,20 @@ test_expect_success 'submodule add to .gitignored path with --force' ' ) ' +test_expect_success 'submodule add to reconfigure existing submodule with --force' ' + ( + cd addtest-ignore && + git submodule add --force bogus-url submod && + git submodule add -b initial "$submodurl" submod-branch && + test "bogus-url" = "$(git config -f .gitmodules submodule.submod.url)" && + test "bogus-url" = "$(git config submodule.submod.url)" && + # Restore the url + git submodule add --force "$submodurl" submod + test "$submodurl" = "$(git config -f .gitmodules submodule.submod.url)" && + test "$submodurl" = "$(git config submodule.submod.url)" + ) +' + test_expect_success 'submodule add --branch' ' echo "refs/heads/initial" >expect-head && cat <<-\EOF >expect-heads && diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 64f322c4cc..347857fa7c 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -140,6 +140,23 @@ test_expect_success 'submodule update --init --recursive from subdirectory' ' test_i18ncmp expect2 actual2 ' +cat <<EOF >expect2 +Submodule 'foo/sub' ($pwd/withsubs/../rebasing) registered for path 'sub' +EOF + +test_expect_success 'submodule update --init from and of subdirectory' ' + git init withsubs && + (cd withsubs && + mkdir foo && + git submodule add "$(pwd)/../rebasing" foo/sub && + (cd foo && + git submodule deinit -f sub && + git submodule update --init sub 2>../../actual2 + ) + ) && + test_i18ncmp expect2 actual2 +' + apos="'"; test_expect_success 'submodule update does not fetch already present commits' ' (cd submodule && @@ -424,6 +441,16 @@ test_expect_success 'submodule update - command in .git/config catches failure - test_i18ncmp actual expect ' +test_expect_success 'submodule update - command run for initial population of submodule' ' + cat <<-\ EOF >expect + Execution of '\''false $submodulesha1'\'' failed in submodule path '\''submodule'\'' + EOF && + rm -rf super/submodule && + test_must_fail git -C super submodule update >../actual && + test_cmp expect actual && + git -C super submodule update --checkout +' + cat << EOF >expect Execution of 'false $submodulesha1' failed in submodule path '../super/submodule' Failed to recurse into submodule path '../super' @@ -476,6 +503,7 @@ test_expect_success 'submodule init picks up merge' ' ' test_expect_success 'submodule update --merge - ignores --merge for new submodules' ' + test_config -C super submodule.submodule.update checkout && (cd super && rm -rf submodule && git submodule update submodule && @@ -488,6 +516,7 @@ test_expect_success 'submodule update --merge - ignores --merge for new submod ' test_expect_success 'submodule update --rebase - ignores --rebase for new submodules' ' + test_config -C super submodule.submodule.update checkout && (cd super && rm -rf submodule && git submodule update submodule && diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh index eaea19b8f2..e159fc5035 100755 --- a/t/t7408-submodule-reference.sh +++ b/t/t7408-submodule-reference.sh @@ -8,74 +8,187 @@ test_description='test clone --reference' base_dir=$(pwd) -U=$base_dir/UPLOAD_LOG - -test_expect_success 'preparing first repository' \ -'test_create_repo A && cd A && -echo first > file1 && -git add file1 && -git commit -m A-initial' - -cd "$base_dir" - -test_expect_success 'preparing second repository' \ -'git clone A B && cd B && -echo second > file2 && -git add file2 && -git commit -m B-addition && -git repack -a -d && -git prune' - -cd "$base_dir" - -test_expect_success 'preparing superproject' \ -'test_create_repo super && cd super && -echo file > file && -git add file && -git commit -m B-super-initial' - -cd "$base_dir" - -test_expect_success 'submodule add --reference' \ -'cd super && git submodule add --reference ../B "file://$base_dir/A" sub && -git commit -m B-super-added' - -cd "$base_dir" - -test_expect_success 'after add: existence of info/alternates' \ -'test_line_count = 1 super/.git/modules/sub/objects/info/alternates' - -cd "$base_dir" - -test_expect_success 'that reference gets used with add' \ -'cd super/sub && -echo "0 objects, 0 kilobytes" > expected && -git count-objects > current && -diff expected current' - -cd "$base_dir" - -test_expect_success 'cloning superproject' \ -'git clone super super-clone' - -cd "$base_dir" - -test_expect_success 'update with reference' \ -'cd super-clone && git submodule update --init --reference ../B' - -cd "$base_dir" - -test_expect_success 'after update: existence of info/alternates' \ -'test_line_count = 1 super-clone/.git/modules/sub/objects/info/alternates' - -cd "$base_dir" - -test_expect_success 'that reference gets used with update' \ -'cd super-clone/sub && -echo "0 objects, 0 kilobytes" > expected && -git count-objects > current && -diff expected current' - -cd "$base_dir" +test_alternate_is_used () { + alternates_file="$1" && + working_dir="$2" && + test_line_count = 1 "$alternates_file" && + echo "0 objects, 0 kilobytes" >expect && + git -C "$working_dir" count-objects >actual && + test_cmp expect actual +} + +test_expect_success 'preparing first repository' ' + test_create_repo A && + ( + cd A && + echo first >file1 && + git add file1 && + git commit -m A-initial + ) +' + +test_expect_success 'preparing second repository' ' + git clone A B && + ( + cd B && + echo second >file2 && + git add file2 && + git commit -m B-addition && + git repack -a -d && + git prune + ) +' + +test_expect_success 'preparing superproject' ' + test_create_repo super && + ( + cd super && + echo file >file && + git add file && + git commit -m B-super-initial + ) +' + +test_expect_success 'submodule add --reference uses alternates' ' + ( + cd super && + git submodule add --reference ../B "file://$base_dir/A" sub && + git commit -m B-super-added && + git repack -ad + ) && + test_alternate_is_used super/.git/modules/sub/objects/info/alternates super/sub +' + +test_expect_success 'that reference gets used with add' ' + ( + cd super/sub && + echo "0 objects, 0 kilobytes" >expected && + git count-objects >current && + diff expected current + ) +' + +# The tests up to this point, and repositories created by them +# (A, B, super and super/sub), are about setting up the stage +# for subsequent tests and meant to be kept throughout the +# remainder of the test. +# Tests from here on, if they create their own test repository, +# are expected to clean after themselves. + +test_expect_success 'updating superproject keeps alternates' ' + test_when_finished "rm -rf super-clone" && + git clone super super-clone && + git -C super-clone submodule update --init --reference ../B && + test_alternate_is_used super-clone/.git/modules/sub/objects/info/alternates super-clone/sub +' + +test_expect_success 'submodules use alternates when cloning a superproject' ' + test_when_finished "rm -rf super-clone" && + git clone --reference super --recursive super super-clone && + ( + cd super-clone && + # test superproject has alternates setup correctly + test_alternate_is_used .git/objects/info/alternates . && + # test submodule has correct setup + test_alternate_is_used .git/modules/sub/objects/info/alternates sub + ) +' + +test_expect_success 'missing submodule alternate fails clone and submodule update' ' + test_when_finished "rm -rf super-clone" && + git clone super super2 && + test_must_fail git clone --recursive --reference super2 super2 super-clone && + ( + cd super-clone && + # test superproject has alternates setup correctly + test_alternate_is_used .git/objects/info/alternates . && + # update of the submodule succeeds + test_must_fail git submodule update --init && + # and we have no alternates: + test_must_fail test_alternate_is_used .git/modules/sub/objects/info/alternates sub && + test_must_fail test_path_is_file sub/file1 + ) +' + +test_expect_success 'ignoring missing submodule alternates passes clone and submodule update' ' + test_when_finished "rm -rf super-clone" && + git clone --reference-if-able super2 --recursive super2 super-clone && + ( + cd super-clone && + # test superproject has alternates setup correctly + test_alternate_is_used .git/objects/info/alternates . && + # update of the submodule succeeds + git submodule update --init && + # and we have no alternates: + test_must_fail test_alternate_is_used .git/modules/sub/objects/info/alternates sub && + test_path_is_file sub/file1 + ) +' + +test_expect_success 'preparing second superproject with a nested submodule plus partial clone' ' + test_create_repo supersuper && + ( + cd supersuper && + echo "I am super super." >file && + git add file && + git commit -m B-super-super-initial + git submodule add "file://$base_dir/super" subwithsub && + git commit -m B-super-super-added && + git submodule update --init --recursive && + git repack -ad + ) && + git clone supersuper supersuper2 && + ( + cd supersuper2 && + git submodule update --init + ) +' + +# At this point there are three root-level positories: A, B, super and super2 + +test_expect_success 'nested submodule alternate in works and is actually used' ' + test_when_finished "rm -rf supersuper-clone" && + git clone --recursive --reference supersuper supersuper supersuper-clone && + ( + cd supersuper-clone && + # test superproject has alternates setup correctly + test_alternate_is_used .git/objects/info/alternates . && + # immediate submodule has alternate: + test_alternate_is_used .git/modules/subwithsub/objects/info/alternates subwithsub && + # nested submodule also has alternate: + test_alternate_is_used .git/modules/subwithsub/modules/sub/objects/info/alternates subwithsub/sub + ) +' + +check_that_two_of_three_alternates_are_used() { + test_alternate_is_used .git/objects/info/alternates . && + # immediate submodule has alternate: + test_alternate_is_used .git/modules/subwithsub/objects/info/alternates subwithsub && + # but nested submodule has no alternate: + test_must_fail test_alternate_is_used .git/modules/subwithsub/modules/sub/objects/info/alternates subwithsub/sub +} + + +test_expect_success 'missing nested submodule alternate fails clone and submodule update' ' + test_when_finished "rm -rf supersuper-clone" && + test_must_fail git clone --recursive --reference supersuper2 supersuper2 supersuper-clone && + ( + cd supersuper-clone && + check_that_two_of_three_alternates_are_used && + # update of the submodule fails + test_must_fail git submodule update --init --recursive + ) +' + +test_expect_success 'missing nested submodule alternate in --reference-if-able mode' ' + test_when_finished "rm -rf supersuper-clone" && + git clone --recursive --reference-if-able supersuper2 supersuper2 supersuper-clone && + ( + cd supersuper-clone && + check_that_two_of_three_alternates_are_used && + # update of the submodule succeeds + git submodule update --init --recursive + ) +' test_done diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh index 47562ce465..eea36f1dbe 100755 --- a/t/t7411-submodule-config.sh +++ b/t/t7411-submodule-config.sh @@ -93,6 +93,20 @@ test_expect_success 'error message contains blob reference' ' ) ' +test_expect_success 'using different treeishs works' ' + ( + cd super && + git tag new_tag && + tree=$(git rev-parse HEAD^{tree}) && + commit=$(git rev-parse HEAD^{commit}) && + test-submodule-config $commit b >expect && + test-submodule-config $tree b >actual.1 && + test-submodule-config new_tag b >actual.2 && + test_cmp expect actual.1 && + test_cmp expect actual.2 + ) +' + cat >super/expect_url <<EOF Submodule url: 'git@somewhere.else.net:a.git' for path 'b' Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule' @@ -120,12 +134,33 @@ test_expect_success 'reading of local configuration' ' "" submodule \ >actual && test_cmp expect_local_path actual && - git config submodule.a.url $old_a && - git config submodule.submodule.url $old_submodule && + git config submodule.a.url "$old_a" && + git config submodule.submodule.url "$old_submodule" && git config --unset submodule.a.path c ) ' +cat >super/expect_url <<EOF +Submodule url: '../submodule' for path 'b' +Submodule url: 'git@somewhere.else.net:submodule.git' for path 'submodule' +EOF + +test_expect_success 'reading of local configuration for uninitialized submodules' ' + ( + cd super && + git submodule deinit -f b && + old_submodule=$(git config submodule.submodule.url) && + git config submodule.submodule.url git@somewhere.else.net:submodule.git && + test-submodule-config --url \ + "" b \ + "" submodule \ + >actual && + test_cmp expect_url actual && + git config submodule.submodule.url "$old_submodule" && + git submodule init b + ) +' + cat >super/expect_fetchrecurse_die.err <<EOF fatal: bad submodule.submodule.fetchrecursesubmodules argument: blabla EOF diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh new file mode 100755 index 0000000000..e2bbb449b6 --- /dev/null +++ b/t/t7412-submodule-absorbgitdirs.sh @@ -0,0 +1,128 @@ +#!/bin/sh + +test_description='Test submodule absorbgitdirs + +This test verifies that `git submodue absorbgitdirs` moves a submodules git +directory into the superproject. +' + +. ./test-lib.sh + +test_expect_success 'setup a real submodule' ' + git init sub1 && + test_commit -C sub1 first && + git submodule add ./sub1 && + test_tick && + git commit -m superproject +' + +test_expect_success 'absorb the git dir' ' + >expect.1 && + >expect.2 && + >actual.1 && + >actual.2 && + git status >expect.1 && + git -C sub1 rev-parse HEAD >expect.2 && + git submodule absorbgitdirs && + git fsck && + test -f sub1/.git && + test -d .git/modules/sub1 && + git status >actual.1 && + git -C sub1 rev-parse HEAD >actual.2 && + test_cmp expect.1 actual.1 && + test_cmp expect.2 actual.2 +' + +test_expect_success 'absorbing does not fail for deinitalized submodules' ' + test_when_finished "git submodule update --init" && + git submodule deinit --all && + git submodule absorbgitdirs && + test -d .git/modules/sub1 && + test -d sub1 && + ! test -e sub1/.git +' + +test_expect_success 'setup nested submodule' ' + git init sub1/nested && + test_commit -C sub1/nested first_nested && + git -C sub1 submodule add ./nested && + test_tick && + git -C sub1 commit -m "add nested" && + git add sub1 && + git commit -m "sub1 to include nested submodule" +' + +test_expect_success 'absorb the git dir in a nested submodule' ' + git status >expect.1 && + git -C sub1/nested rev-parse HEAD >expect.2 && + git submodule absorbgitdirs && + test -f sub1/nested/.git && + test -d .git/modules/sub1/modules/nested && + git status >actual.1 && + git -C sub1/nested rev-parse HEAD >actual.2 && + test_cmp expect.1 actual.1 && + test_cmp expect.2 actual.2 +' + +test_expect_success 're-setup nested submodule' ' + # un-absorb the direct submodule, to test if the nested submodule + # is still correct (needs a rewrite of the gitfile only) + rm -rf sub1/.git && + mv .git/modules/sub1 sub1/.git && + GIT_WORK_TREE=. git -C sub1 config --unset core.worktree && + # fixup the nested submodule + echo "gitdir: ../.git/modules/nested" >sub1/nested/.git && + GIT_WORK_TREE=../../../nested git -C sub1/.git/modules/nested config \ + core.worktree "../../../nested" && + # make sure this re-setup is correct + git status --ignore-submodules=none +' + +test_expect_success 'absorb the git dir in a nested submodule' ' + git status >expect.1 && + git -C sub1/nested rev-parse HEAD >expect.2 && + git submodule absorbgitdirs && + test -f sub1/.git && + test -f sub1/nested/.git && + test -d .git/modules/sub1/modules/nested && + git status >actual.1 && + git -C sub1/nested rev-parse HEAD >actual.2 && + test_cmp expect.1 actual.1 && + test_cmp expect.2 actual.2 +' + +test_expect_success 'setup a gitlink with missing .gitmodules entry' ' + git init sub2 && + test_commit -C sub2 first && + git add sub2 && + git commit -m superproject +' + +test_expect_success 'absorbing the git dir fails for incomplete submodules' ' + git status >expect.1 && + git -C sub2 rev-parse HEAD >expect.2 && + test_must_fail git submodule absorbgitdirs && + git -C sub2 fsck && + test -d sub2/.git && + git status >actual && + git -C sub2 rev-parse HEAD >actual.2 && + test_cmp expect.1 actual.1 && + test_cmp expect.2 actual.2 +' + +test_expect_success 'setup a submodule with multiple worktrees' ' + # first create another unembedded git dir in a new submodule + git init sub3 && + test_commit -C sub3 first && + git submodule add ./sub3 && + test_tick && + git commit -m "add another submodule" && + git -C sub3 worktree add ../sub3_second_work_tree +' + +test_expect_success 'absorbing fails for a submodule with multiple worktrees' ' + test_must_fail git submodule absorbgitdirs sub3 2>error && + test_i18ngrep "not supported" error +' + +test_done diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh index d84897a67a..0b6da7ae1f 100755 --- a/t/t7501-commit.sh +++ b/t/t7501-commit.sh @@ -155,6 +155,15 @@ test_expect_success 'amend --only ignores staged contents' ' git diff --exit-code ' +test_expect_success 'allow-empty --only ignores staged contents' ' + echo changed-again >file && + git add file && + git commit --allow-empty --only -m "empty" && + 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 @@ -460,6 +469,42 @@ $alt" && test_cmp expected actual ' +test_expect_success 'signoff respects trailer config' ' + + echo 5 >positive && + git add positive && + git commit -s -m "subject + +non-trailer line +Myfooter: x" && + git cat-file commit HEAD | sed -e "1,/^\$/d" > actual && + ( + echo subject + echo + echo non-trailer line + echo Myfooter: x + echo + echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" + ) >expected && + test_cmp expected actual && + + echo 6 >positive && + git add positive && + git -c "trailer.Myfooter.ifexists=add" commit -s -m "subject + +non-trailer line +Myfooter: x" && + git cat-file commit HEAD | sed -e "1,/^\$/d" > actual && + ( + echo subject + echo + echo non-trailer line + echo Myfooter: x + echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" + ) >expected && + test_cmp expected actual +' + test_expect_success 'multiple -m' ' >negative && diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh index 6e839f5489..762135adea 100755 --- a/t/t7510-signed-commit.sh +++ b/t/t7510-signed-commit.sh @@ -2,6 +2,7 @@ test_description='signed commit tests' . ./test-lib.sh +GNUPGHOME_NOT_USED=$GNUPGHOME . "$TEST_DIRECTORY/lib-gpg.sh" test_expect_success GPG 'create signed commits' ' @@ -190,7 +191,7 @@ test_expect_success GPG 'show bad signature with custom format' ' test_cmp expect actual ' -test_expect_success GPG 'show unknown signature with custom format' ' +test_expect_success GPG 'show untrusted signature with custom format' ' cat >expect <<-\EOF && U 61092E85B7227189 @@ -200,6 +201,16 @@ test_expect_success GPG 'show unknown signature with custom format' ' test_cmp expect actual ' +test_expect_success GPG 'show unknown signature with custom format' ' + cat >expect <<-\EOF && + E + 61092E85B7227189 + + EOF + GNUPGHOME="$GNUPGHOME_NOT_USED" git log -1 --format="%G?%n%GK%n%GS" eighth-signed-alt >actual && + test_cmp expect actual +' + test_expect_success GPG 'show lack of signature with custom format' ' cat >expect <<-\EOF && N diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh index 5c3db656df..458608cc1e 100755 --- a/t/t7512-status-help.sh +++ b/t/t7512-status-help.sh @@ -944,4 +944,23 @@ EOF test_i18ncmp expected actual ' +test_expect_success 'status: handle not-yet-started rebase -i gracefully' ' + ONTO=$(git rev-parse --short HEAD^) && + COMMIT=$(git rev-parse --short HEAD) && + EDITOR="git status --untracked-files=no >actual" git rebase -i HEAD^ && + cat >expected <<EOF && +On branch several_commits +No commands done. +Next command to do (1 remaining command): + pick $COMMIT four_commit + (use "git rebase --edit-todo" to view and edit) +You are currently editing a commit while rebasing branch '\''several_commits'\'' 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 + test_i18ncmp expected actual +' + test_done diff --git a/t/t7513-interpret-trailers.sh b/t/t7513-interpret-trailers.sh index aee785cffa..4dd1d7c520 100755 --- a/t/t7513-interpret-trailers.sh +++ b/t/t7513-interpret-trailers.sh @@ -126,6 +126,305 @@ test_expect_success 'with multiline title in the message' ' test_cmp expected actual ' +test_expect_success 'with non-trailer lines mixed with Signed-off-by' ' + cat >patch <<-\EOF && + + this is not a trailer + this is not a trailer + Signed-off-by: a <a@example.com> + this is not a trailer + EOF + cat >expected <<-\EOF && + + this is not a trailer + this is not a trailer + Signed-off-by: a <a@example.com> + this is not a trailer + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'with non-trailer lines mixed with cherry picked from' ' + cat >patch <<-\EOF && + + this is not a trailer + this is not a trailer + (cherry picked from commit x) + this is not a trailer + EOF + cat >expected <<-\EOF && + + this is not a trailer + this is not a trailer + (cherry picked from commit x) + this is not a trailer + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'with non-trailer lines mixed with a configured trailer' ' + cat >patch <<-\EOF && + + this is not a trailer + this is not a trailer + My-trailer: x + this is not a trailer + EOF + cat >expected <<-\EOF && + + this is not a trailer + this is not a trailer + My-trailer: x + this is not a trailer + token: value + EOF + test_config trailer.my.key "My-trailer: " && + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'with non-trailer lines mixed with a non-configured trailer' ' + cat >patch <<-\EOF && + + this is not a trailer + this is not a trailer + I-am-not-configured: x + this is not a trailer + EOF + cat >expected <<-\EOF && + + this is not a trailer + this is not a trailer + I-am-not-configured: x + this is not a trailer + + token: value + EOF + test_config trailer.my.key "My-trailer: " && + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'with all non-configured trailers' ' + cat >patch <<-\EOF && + + I-am-not-configured: x + I-am-also-not-configured: x + EOF + cat >expected <<-\EOF && + + I-am-not-configured: x + I-am-also-not-configured: x + token: value + EOF + test_config trailer.my.key "My-trailer: " && + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'with non-trailer lines only' ' + cat >patch <<-\EOF && + + this is not a trailer + EOF + cat >expected <<-\EOF && + + this is not a trailer + + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'line with leading whitespace is not trailer' ' + q_to_tab >patch <<-\EOF && + + Qtoken: value + EOF + q_to_tab >expected <<-\EOF && + + Qtoken: value + + token: value + EOF + git interpret-trailers --trailer "token: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'multiline field treated as one trailer for 25% check' ' + q_to_tab >patch <<-\EOF && + + Signed-off-by: a <a@example.com> + name: value on + Qmultiple lines + this is not a trailer + this is not a trailer + this is not a trailer + this is not a trailer + this is not a trailer + this is not a trailer + EOF + q_to_tab >expected <<-\EOF && + + Signed-off-by: a <a@example.com> + name: value on + Qmultiple lines + this is not a trailer + this is not a trailer + this is not a trailer + this is not a trailer + this is not a trailer + this is not a trailer + name: value + EOF + git interpret-trailers --trailer "name: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'multiline field treated as atomic for placement' ' + q_to_tab >patch <<-\EOF && + + another: trailer + name: value on + Qmultiple lines + another: trailer + EOF + q_to_tab >expected <<-\EOF && + + another: trailer + name: value on + Qmultiple lines + name: value + another: trailer + EOF + test_config trailer.name.where after && + git interpret-trailers --trailer "name: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'multiline field treated as atomic for replacement' ' + q_to_tab >patch <<-\EOF && + + another: trailer + name: value on + Qmultiple lines + another: trailer + EOF + q_to_tab >expected <<-\EOF && + + another: trailer + another: trailer + name: value + EOF + test_config trailer.name.ifexists replace && + git interpret-trailers --trailer "name: value" patch >actual && + test_cmp expected actual +' + +test_expect_success 'multiline field treated as atomic for difference check' ' + q_to_tab >patch <<-\EOF && + + another: trailer + name: first line + Qsecond line + another: trailer + EOF + test_config trailer.name.ifexists addIfDifferent && + + q_to_tab >trailer <<-\EOF && + name: first line + Qsecond line + EOF + q_to_tab >expected <<-\EOF && + + another: trailer + name: first line + Qsecond line + another: trailer + EOF + git interpret-trailers --trailer "$(cat trailer)" patch >actual && + test_cmp expected actual && + + q_to_tab >trailer <<-\EOF && + name: first line + QQQQQsecond line + EOF + q_to_tab >expected <<-\EOF && + + another: trailer + name: first line + Qsecond line + another: trailer + name: first line + QQQQQsecond line + EOF + git interpret-trailers --trailer "$(cat trailer)" patch >actual && + test_cmp expected actual && + + q_to_tab >trailer <<-\EOF && + name: first line *DIFFERENT* + Qsecond line + EOF + q_to_tab >expected <<-\EOF && + + another: trailer + name: first line + Qsecond line + another: trailer + name: first line *DIFFERENT* + Qsecond line + EOF + git interpret-trailers --trailer "$(cat trailer)" patch >actual && + test_cmp expected actual +' + +test_expect_success 'multiline field treated as atomic for neighbor check' ' + q_to_tab >patch <<-\EOF && + + another: trailer + name: first line + Qsecond line + another: trailer + EOF + test_config trailer.name.where after && + test_config trailer.name.ifexists addIfDifferentNeighbor && + + q_to_tab >trailer <<-\EOF && + name: first line + Qsecond line + EOF + q_to_tab >expected <<-\EOF && + + another: trailer + name: first line + Qsecond line + another: trailer + EOF + git interpret-trailers --trailer "$(cat trailer)" patch >actual && + test_cmp expected actual && + + q_to_tab >trailer <<-\EOF && + name: first line + QQQQQsecond line + EOF + q_to_tab >expected <<-\EOF && + + another: trailer + name: first line + Qsecond line + name: first line + QQQQQsecond line + another: trailer + EOF + git interpret-trailers --trailer "$(cat trailer)" patch >actual && + test_cmp expected actual +' + test_expect_success 'with config setup' ' git config trailer.ack.key "Acked-by: " && cat >expected <<-\EOF && diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index 85248a14b6..2ebda509ac 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -154,6 +154,10 @@ test_expect_success 'test option parsing' ' test_must_fail git merge -s foobar c1 && test_must_fail git merge -s=foobar c1 && test_must_fail git merge -m && + test_must_fail git merge --abort foobar && + test_must_fail git merge --abort --quiet && + test_must_fail git merge --continue foobar && + test_must_fail git merge --continue --quiet && test_must_fail git merge ' @@ -763,4 +767,11 @@ test_expect_success 'merge nothing into void' ' ) ' +test_expect_success 'merge can be completed with --continue' ' + git reset --hard c0 && + git merge --no-ff --no-commit c1 && + git merge --continue && + verify_parents $c0 $c1 +' + test_done diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh index f80bdb81e1..e90413204e 100755 --- a/t/t7609-merge-co-error-msgs.sh +++ b/t/t7609-merge-co-error-msgs.sh @@ -105,7 +105,7 @@ test_expect_success 'not uptodate file porcelain checkout error' ' ' cat >expect <<\EOF -error: Updating the following directories would lose untracked files in it: +error: Updating the following directories would lose untracked files in them: rep rep2 diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh index 7217f3968d..381b7df452 100755 --- a/t/t7610-mergetool.sh +++ b/t/t7610-mergetool.sh @@ -55,6 +55,22 @@ test_expect_success 'setup' ' git rm file12 && git commit -m "branch1 changes" && + git checkout -b delete-base branch1 && + mkdir -p a/a && + (echo one; echo two; echo 3; echo 4) >a/a/file.txt && + git add a/a/file.txt && + git commit -m"base file" && + git checkout -b move-to-b delete-base && + mkdir -p b/b && + git mv a/a/file.txt b/b/file.txt && + (echo one; echo two; echo 4) >b/b/file.txt && + git commit -a -m"move to b" && + git checkout -b move-to-c delete-base && + mkdir -p c/c && + git mv a/a/file.txt c/c/file.txt && + (echo one; echo two; echo 3) >c/c/file.txt && + git commit -a -m"move to c" && + git checkout -b stash1 master && echo stash1 change file11 >file11 && git add file11 && @@ -86,6 +102,23 @@ test_expect_success 'setup' ' git rm file11 && git commit -m "master updates" && + git clean -fdx && + git checkout -b order-file-start master && + echo start >a && + echo start >b && + git add a b && + git commit -m start && + git checkout -b order-file-side1 order-file-start && + echo side1 >a && + echo side1 >b && + git add a b && + git commit -m side1 && + git checkout -b order-file-side2 order-file-start && + echo side2 >a && + echo side2 >b && + git add a b && + git commit -m side2 && + git config merge.tool mytool && git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" && git config mergetool.mytool.trustExitCode true && @@ -94,7 +127,8 @@ test_expect_success 'setup' ' ' test_expect_success 'custom mergetool' ' - git checkout -b test1 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && git submodule update -N && test_must_fail git merge master >/dev/null 2>&1 && ( yes "" | git mergetool both >/dev/null 2>&1 ) && @@ -112,8 +146,13 @@ test_expect_success 'custom mergetool' ' ' test_expect_success 'mergetool crlf' ' + test_when_finished "git reset --hard" && + # This test_config line must go after the above reset line so that + # core.autocrlf is unconfigured before reset runs. (The + # test_config command uses test_when_finished internally and + # test_when_finished is LIFO.) test_config core.autocrlf true && - git checkout -b test2 branch1 && + git checkout -b test$test_count branch1 && test_must_fail git merge master >/dev/null 2>&1 && ( yes "" | git mergetool file1 >/dev/null 2>&1 ) && ( yes "" | git mergetool file2 >/dev/null 2>&1 ) && @@ -128,13 +167,12 @@ test_expect_success 'mergetool crlf' ' test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" && git submodule update -N && test "$(cat submod/bar)" = "master submodule" && - git commit -m "branch1 resolved with mergetool - autocrlf" && - test_config core.autocrlf false && - git reset --hard + git commit -m "branch1 resolved with mergetool - autocrlf" ' test_expect_success 'mergetool in subdir' ' - git checkout -b test3 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && git submodule update -N && ( cd subdir && @@ -145,8 +183,13 @@ test_expect_success 'mergetool in subdir' ' ' test_expect_success 'mergetool on file in parent dir' ' + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && + git submodule update -N && ( cd subdir && + test_must_fail git merge master >/dev/null 2>&1 && + ( yes "" | git mergetool file3 >/dev/null 2>&1 ) && ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) && ( yes "" | git mergetool ../file2 ../spaced\ name >/dev/null 2>&1 ) && ( yes "" | git mergetool ../both >/dev/null 2>&1 ) && @@ -161,7 +204,8 @@ test_expect_success 'mergetool on file in parent dir' ' ' test_expect_success 'mergetool skips autoresolved' ' - git checkout -b test4 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && git submodule update -N && test_must_fail git merge master && test -n "$(git ls-files -u)" && @@ -169,11 +213,12 @@ test_expect_success 'mergetool skips autoresolved' ' ( yes "d" | git mergetool file12 >/dev/null 2>&1 ) && ( yes "l" | git mergetool submod >/dev/null 2>&1 ) && output="$(git mergetool --no-prompt)" && - test "$output" = "No files need merging" && - git reset --hard + test "$output" = "No files need merging" ' -test_expect_success 'mergetool merges all from subdir' ' +test_expect_success 'mergetool merges all from subdir (rerere disabled)' ' + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && test_config rerere.enabled false && ( cd subdir && @@ -189,21 +234,41 @@ test_expect_success 'mergetool merges all from subdir' ' ) ' +test_expect_success 'mergetool merges all from subdir (rerere enabled)' ' + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && + test_config rerere.enabled true && + rm -rf .git/rr-cache && + ( + cd subdir && + test_must_fail git merge master && + ( yes "r" | git mergetool ../submod ) && + ( yes "d" "d" | git mergetool --no-prompt ) && + test "$(cat ../file1)" = "master updated" && + test "$(cat ../file2)" = "master new" && + test "$(cat file3)" = "master new sub" && + ( cd .. && git submodule update -N ) && + test "$(cat ../submod/bar)" = "master submodule" && + git commit -m "branch2 resolved by mergetool from subdir" + ) +' + test_expect_success 'mergetool skips resolved paths when rerere is active' ' + test_when_finished "git reset --hard" && test_config rerere.enabled true && rm -rf .git/rr-cache && - git checkout -b test5 branch1 && + git checkout -b test$test_count branch1 && git submodule update -N && test_must_fail git merge master >/dev/null 2>&1 && ( yes "l" | git mergetool --no-prompt submod >/dev/null 2>&1 ) && ( yes "d" "d" | git mergetool --no-prompt >/dev/null 2>&1 ) && git submodule update -N && output="$(yes "n" | git mergetool --no-prompt)" && - test "$output" = "No files need merging" && - git reset --hard + test "$output" = "No files need merging" ' test_expect_success 'conflicted stash sets up rerere' ' + test_when_finished "git reset --hard" && test_config rerere.enabled true && git checkout stash1 && echo "Conflicting stash content" >file11 && @@ -231,67 +296,57 @@ test_expect_success 'conflicted stash sets up rerere' ' ' test_expect_success 'mergetool takes partial path' ' - git reset --hard && + test_when_finished "git reset --hard" && test_config rerere.enabled false && - git checkout -b test12 branch1 && + git checkout -b test$test_count branch1 && git submodule update -N && test_must_fail git merge master && ( yes "" | git mergetool subdir ) && - test "$(cat subdir/file3)" = "master new sub" && - git reset --hard + test "$(cat subdir/file3)" = "master new sub" ' test_expect_success 'mergetool delete/delete conflict' ' - git checkout -b delete-base branch1 && - mkdir -p a/a && - (echo one; echo two; echo 3; echo 4) >a/a/file.txt && - git add a/a/file.txt && - git commit -m"base file" && - git checkout -b move-to-b delete-base && - mkdir -p b/b && - git mv a/a/file.txt b/b/file.txt && - (echo one; echo two; echo 4) >b/b/file.txt && - git commit -a -m"move to b" && - git checkout -b move-to-c delete-base && - mkdir -p c/c && - git mv a/a/file.txt c/c/file.txt && - (echo one; echo two; echo 3) >c/c/file.txt && - git commit -a -m"move to c" && + test_when_finished "git reset --hard" && + git checkout -b test$test_count move-to-c && test_must_fail git merge move-to-b && echo d | git mergetool a/a/file.txt && ! test -f a/a/file.txt && - git reset --hard HEAD && + git reset --hard && test_must_fail git merge move-to-b && echo m | git mergetool a/a/file.txt && test -f b/b/file.txt && - git reset --hard HEAD && + git reset --hard && test_must_fail git merge move-to-b && ! echo a | git mergetool a/a/file.txt && - ! test -f a/a/file.txt && - git reset --hard HEAD + ! test -f a/a/file.txt ' test_expect_success 'mergetool produces no errors when keepBackup is used' ' + test_when_finished "git reset --hard" && + git checkout -b test$test_count move-to-c && test_config mergetool.keepBackup true && test_must_fail git merge move-to-b && : >expect && echo d | git mergetool a/a/file.txt 2>actual && test_cmp expect actual && - ! test -d a && - git reset --hard HEAD + ! test -d a ' test_expect_success 'mergetool honors tempfile config for deleted files' ' + test_when_finished "git reset --hard" && + git checkout -b test$test_count move-to-c && test_config mergetool.keepTemporaries false && test_must_fail git merge move-to-b && echo d | git mergetool a/a/file.txt && - ! test -d a && - git reset --hard HEAD + ! test -d a ' test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' ' + test_when_finished "git reset --hard" && + test_when_finished "git clean -fdx" && + git checkout -b test$test_count move-to-c && test_config mergetool.keepTemporaries true && test_must_fail git merge move-to-b && ! (echo a; echo n) | git mergetool a/a/file.txt && @@ -302,18 +357,17 @@ test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' ' file_REMOTE_.txt EOF ls -1 a/a | sed -e "s/[0-9]*//g" >actual && - test_cmp expect actual && - git clean -fdx && - git reset --hard HEAD + test_cmp expect actual ' test_expect_success 'deleted vs modified submodule' ' - git checkout -b test6 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && git submodule update -N && mv submod submod-movedaside && git rm --cached submod && git commit -m "Submodule deleted from branch" && - git checkout -b test6.a test6 && + git checkout -b test$test_count.a test$test_count && test_must_fail git merge master && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && @@ -329,7 +383,7 @@ test_expect_success 'deleted vs modified submodule' ' git commit -m "Merge resolved by keeping module" && mv submod submod-movedaside && - git checkout -b test6.b test6 && + git checkout -b test$test_count.b test$test_count && git submodule update -N && test_must_fail git merge master && test -n "$(git ls-files -u)" && @@ -343,9 +397,9 @@ test_expect_success 'deleted vs modified submodule' ' git commit -m "Merge resolved by deleting module" && mv submod-movedaside submod && - git checkout -b test6.c master && + git checkout -b test$test_count.c master && git submodule update -N && - test_must_fail git merge test6 && + test_must_fail git merge test$test_count && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && ( yes "" | git mergetool both >/dev/null 2>&1 ) && @@ -359,9 +413,9 @@ test_expect_success 'deleted vs modified submodule' ' git commit -m "Merge resolved by deleting module" && mv submod.orig submod && - git checkout -b test6.d master && + git checkout -b test$test_count.d master && git submodule update -N && - test_must_fail git merge test6 && + test_must_fail git merge test$test_count && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && ( yes "" | git mergetool both >/dev/null 2>&1 ) && @@ -372,19 +426,19 @@ test_expect_success 'deleted vs modified submodule' ' test "$(cat submod/bar)" = "master submodule" && output="$(git mergetool --no-prompt)" && test "$output" = "No files need merging" && - git commit -m "Merge resolved by keeping module" && - git reset --hard HEAD + git commit -m "Merge resolved by keeping module" ' test_expect_success 'file vs modified submodule' ' - git checkout -b test7 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && git submodule update -N && mv submod submod-movedaside && git rm --cached submod && echo not a submodule >submod && git add submod && git commit -m "Submodule path becomes file" && - git checkout -b test7.a branch1 && + git checkout -b test$test_count.a branch1 && test_must_fail git merge master && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && @@ -400,7 +454,7 @@ test_expect_success 'file vs modified submodule' ' git commit -m "Merge resolved by keeping module" && mv submod submod-movedaside && - git checkout -b test7.b test7 && + git checkout -b test$test_count.b test$test_count && test_must_fail git merge master && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && @@ -413,11 +467,11 @@ test_expect_success 'file vs modified submodule' ' test "$output" = "No files need merging" && git commit -m "Merge resolved by keeping file" && - git checkout -b test7.c master && + git checkout -b test$test_count.c master && rmdir submod && mv submod-movedaside submod && test ! -e submod.orig && git submodule update -N && - test_must_fail git merge test7 && + test_must_fail git merge test$test_count && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && ( yes "" | git mergetool both >/dev/null 2>&1 ) && @@ -430,10 +484,10 @@ test_expect_success 'file vs modified submodule' ' test "$output" = "No files need merging" && git commit -m "Merge resolved by keeping file" && - git checkout -b test7.d master && + git checkout -b test$test_count.d master && rmdir submod && mv submod.orig submod && git submodule update -N && - test_must_fail git merge test7 && + test_must_fail git merge test$test_count && test -n "$(git ls-files -u)" && ( yes "" | git mergetool file1 file2 spaced\ name subdir/file3 >/dev/null 2>&1 ) && ( yes "" | git mergetool both>/dev/null 2>&1 ) && @@ -448,7 +502,8 @@ test_expect_success 'file vs modified submodule' ' ' test_expect_success 'submodule in subdirectory' ' - git checkout -b test10 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && git submodule update -N && ( cd subdir && @@ -460,56 +515,57 @@ test_expect_success 'submodule in subdirectory' ' git commit -m "add initial versions" ) ) && + test_when_finished "rm -rf subdir/subdir_module" && git submodule add git://example.com/subsubmodule subdir/subdir_module && git add subdir/subdir_module && git commit -m "add submodule in subdirectory" && - git checkout -b test10.a test10 && + git checkout -b test$test_count.a test$test_count && git submodule update -N && ( cd subdir/subdir_module && git checkout -b super10.a && - echo test10.a >file15 && + echo test$test_count.a >file15 && git add file15 && git commit -m "on branch 10.a" ) && git add subdir/subdir_module && - git commit -m "change submodule in subdirectory on test10.a" && + git commit -m "change submodule in subdirectory on test$test_count.a" && - git checkout -b test10.b test10 && + git checkout -b test$test_count.b test$test_count && git submodule update -N && ( cd subdir/subdir_module && git checkout -b super10.b && - echo test10.b >file15 && + echo test$test_count.b >file15 && git add file15 && git commit -m "on branch 10.b" ) && git add subdir/subdir_module && - git commit -m "change submodule in subdirectory on test10.b" && + git commit -m "change submodule in subdirectory on test$test_count.b" && - test_must_fail git merge test10.a >/dev/null 2>&1 && + test_must_fail git merge test$test_count.a >/dev/null 2>&1 && ( cd subdir && ( yes "l" | git mergetool subdir_module ) ) && - test "$(cat subdir/subdir_module/file15)" = "test10.b" && + test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" && git submodule update -N && - test "$(cat subdir/subdir_module/file15)" = "test10.b" && + test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" && git reset --hard && git submodule update -N && - test_must_fail git merge test10.a >/dev/null 2>&1 && + test_must_fail git merge test$test_count.a >/dev/null 2>&1 && ( yes "r" | git mergetool subdir/subdir_module ) && - test "$(cat subdir/subdir_module/file15)" = "test10.b" && + test "$(cat subdir/subdir_module/file15)" = "test$test_count.b" && git submodule update -N && - test "$(cat subdir/subdir_module/file15)" = "test10.a" && - git commit -m "branch1 resolved with mergetool" && - rm -rf subdir/subdir_module + test "$(cat subdir/subdir_module/file15)" = "test$test_count.a" && + git commit -m "branch1 resolved with mergetool" ' test_expect_success 'directory vs modified submodule' ' - git checkout -b test11 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && mv submod submod-movedaside && git rm --cached submod && mkdir submod && @@ -523,7 +579,7 @@ test_expect_success 'directory vs modified submodule' ' test "$(cat submod/file16)" = "not a submodule" && rm -rf submod.orig && - git reset --hard >/dev/null 2>&1 && + git reset --hard && test_must_fail git merge master && test -n "$(git ls-files -u)" && test ! -e submod.orig && @@ -535,75 +591,131 @@ test_expect_success 'directory vs modified submodule' ' ( cd submod && git clean -f && git reset --hard ) && git submodule update -N && test "$(cat submod/bar)" = "master submodule" && - git reset --hard >/dev/null 2>&1 && rm -rf submod-movedaside && + git reset --hard && + rm -rf submod-movedaside && - git checkout -b test11.c master && + git checkout -b test$test_count.c master && git submodule update -N && - test_must_fail git merge test11 && + test_must_fail git merge test$test_count && test -n "$(git ls-files -u)" && ( yes "l" | git mergetool submod ) && git submodule update -N && test "$(cat submod/bar)" = "master submodule" && - git reset --hard >/dev/null 2>&1 && + git reset --hard && git submodule update -N && - test_must_fail git merge test11 && + test_must_fail git merge test$test_count && test -n "$(git ls-files -u)" && test ! -e submod.orig && ( yes "r" | git mergetool submod ) && test "$(cat submod/file16)" = "not a submodule" && - git reset --hard master >/dev/null 2>&1 && + git reset --hard master && ( cd submod && git clean -f && git reset --hard ) && git submodule update -N ' test_expect_success 'file with no base' ' - git checkout -b test13 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && test_must_fail git merge master && git mergetool --no-prompt --tool mybase -- both && >expected && - test_cmp both expected && - git reset --hard master >/dev/null 2>&1 + test_cmp both expected ' test_expect_success 'custom commands override built-ins' ' - git checkout -b test14 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && test_config mergetool.defaults.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" && test_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 reset --hard master >/dev/null 2>&1 + test_cmp both expected ' test_expect_success 'filenames seen by tools start with ./' ' - git checkout -b test15 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && test_config mergetool.writeToTemp false && test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" && test_config mergetool.myecho.trustExitCode true && test_must_fail git merge master && git mergetool --no-prompt --tool myecho -- both >actual && - grep ^\./both_LOCAL_ actual >/dev/null && - git reset --hard master >/dev/null 2>&1 + grep ^\./both_LOCAL_ actual >/dev/null ' test_lazy_prereq MKTEMP ' tempdir=$(mktemp -d -t foo.XXXXXX) && - test -d "$tempdir" + test -d "$tempdir" && + rmdir "$tempdir" ' test_expect_success MKTEMP 'temporary filenames are used with mergetool.writeToTemp' ' - git checkout -b test16 branch1 && + test_when_finished "git reset --hard" && + git checkout -b test$test_count branch1 && test_config mergetool.writeToTemp true && test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" && test_config mergetool.myecho.trustExitCode true && test_must_fail git merge master && git mergetool --no-prompt --tool myecho -- both >actual && - test_must_fail grep ^\./both_LOCAL_ actual >/dev/null && - grep /both_LOCAL_ actual >/dev/null && - git reset --hard master >/dev/null 2>&1 + ! grep ^\./both_LOCAL_ actual >/dev/null && + grep /both_LOCAL_ actual >/dev/null +' + +test_expect_success 'diff.orderFile configuration is honored' ' + test_when_finished "git reset --hard" && + git checkout -b test$test_count order-file-side2 && + test_config diff.orderFile order-file && + test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" && + test_config mergetool.myecho.trustExitCode true && + echo b >order-file && + echo a >>order-file && + test_must_fail git merge order-file-side1 && + cat >expect <<-\EOF && + Merging: + b + a + EOF + + # make sure "order-file" that is ambiguous between + # rev and path is understood correctly. + git branch order-file HEAD && + + git mergetool --no-prompt --tool myecho >output && + git grep --no-index -h -A2 Merging: output >actual && + test_cmp expect actual +' +test_expect_success 'mergetool -Oorder-file is honored' ' + test_when_finished "git reset --hard" && + git checkout -b test$test_count order-file-side2 && + test_config diff.orderFile order-file && + test_config mergetool.myecho.cmd "echo \"\$LOCAL\"" && + test_config mergetool.myecho.trustExitCode true && + echo b >order-file && + echo a >>order-file && + test_must_fail git merge order-file-side1 && + cat >expect <<-\EOF && + Merging: + a + b + EOF + git mergetool -O/dev/null --no-prompt --tool myecho >output && + git grep --no-index -h -A2 Merging: output >actual && + test_cmp expect actual && + git reset --hard && + + git config --unset diff.orderFile && + test_must_fail git merge order-file-side1 && + cat >expect <<-\EOF && + Merging: + b + a + EOF + git mergetool -Oorder-file --no-prompt --tool myecho >output && + git grep --no-index -h -A2 Merging: output >actual && + test_cmp expect actual ' test_done diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh index 70a2de461a..25241f4096 100755 --- a/t/t7800-difftool.sh +++ b/t/t7800-difftool.sh @@ -23,8 +23,20 @@ prompt_given () test "$prompt" = "Launch 'test-tool' [Y/n]? branch" } +test_expect_success 'basic usage requires no repo' ' + test_expect_code 129 git difftool -h >output && + grep ^usage: output && + # create a ceiling directory to prevent Git from finding a repo + mkdir -p not/repo && + test_when_finished rm -r not && + test_expect_code 129 \ + env GIT_CEILING_DIRECTORIES="$(pwd)/not" \ + git -C not/repo difftool -h >output && + grep ^usage: output +' + # Create a file on master and change it on branch -test_expect_success PERL 'setup' ' +test_expect_success 'setup' ' echo master >file && git add file && git commit -m "added file" && @@ -36,7 +48,7 @@ test_expect_success PERL 'setup' ' ' # Configure a custom difftool.<tool>.cmd and use it -test_expect_success PERL 'custom commands' ' +test_expect_success 'custom commands' ' difftool_test_setup && test_config difftool.test-tool.cmd "cat \"\$REMOTE\"" && echo master >expect && @@ -49,21 +61,21 @@ test_expect_success PERL 'custom commands' ' test_cmp expect actual ' -test_expect_success PERL 'custom tool commands override built-ins' ' +test_expect_success '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 ' -test_expect_success PERL 'difftool ignores bad --tool values' ' +test_expect_success 'difftool ignores bad --tool values' ' : >expect && test_must_fail \ git difftool --no-prompt --tool=bad-tool branch >actual && test_cmp expect actual ' -test_expect_success PERL 'difftool forwards arguments to diff' ' +test_expect_success 'difftool forwards arguments to diff' ' difftool_test_setup && >for-diff && git add for-diff && @@ -76,40 +88,40 @@ test_expect_success PERL 'difftool forwards arguments to diff' ' rm for-diff ' -test_expect_success PERL 'difftool ignores exit code' ' +test_expect_success 'difftool ignores exit code' ' test_config difftool.error.cmd false && git difftool -y -t error branch ' -test_expect_success PERL 'difftool forwards exit code with --trust-exit-code' ' +test_expect_success 'difftool forwards exit code with --trust-exit-code' ' test_config difftool.error.cmd false && test_must_fail git difftool -y --trust-exit-code -t error branch ' -test_expect_success PERL 'difftool forwards exit code with --trust-exit-code for built-ins' ' +test_expect_success 'difftool forwards exit code with --trust-exit-code for built-ins' ' test_config difftool.vimdiff.path false && test_must_fail git difftool -y --trust-exit-code -t vimdiff branch ' -test_expect_success PERL 'difftool honors difftool.trustExitCode = true' ' +test_expect_success 'difftool honors difftool.trustExitCode = true' ' test_config difftool.error.cmd false && test_config difftool.trustExitCode true && test_must_fail git difftool -y -t error branch ' -test_expect_success PERL 'difftool honors difftool.trustExitCode = false' ' +test_expect_success 'difftool honors difftool.trustExitCode = false' ' test_config difftool.error.cmd false && test_config difftool.trustExitCode false && git difftool -y -t error branch ' -test_expect_success PERL 'difftool ignores exit code with --no-trust-exit-code' ' +test_expect_success 'difftool ignores exit code with --no-trust-exit-code' ' test_config difftool.error.cmd false && test_config difftool.trustExitCode true && git difftool -y --no-trust-exit-code -t error branch ' -test_expect_success PERL 'difftool stops on error with --trust-exit-code' ' +test_expect_success 'difftool stops on error with --trust-exit-code' ' test_when_finished "rm -f for-diff .git/fail-right-file" && test_when_finished "git reset -- for-diff" && write_script .git/fail-right-file <<-\EOF && @@ -124,13 +136,13 @@ test_expect_success PERL 'difftool stops on error with --trust-exit-code' ' test_cmp expect actual ' -test_expect_success PERL 'difftool honors exit status if command not found' ' +test_expect_success 'difftool honors exit status if command not found' ' test_config difftool.nonexistent.cmd i-dont-exist && test_config difftool.trustExitCode false && test_must_fail git difftool -y -t nonexistent branch ' -test_expect_success PERL 'difftool honors --gui' ' +test_expect_success 'difftool honors --gui' ' difftool_test_setup && test_config merge.tool bogus-tool && test_config diff.tool bogus-tool && @@ -141,7 +153,7 @@ test_expect_success PERL 'difftool honors --gui' ' test_cmp expect actual ' -test_expect_success PERL 'difftool --gui last setting wins' ' +test_expect_success 'difftool --gui last setting wins' ' difftool_test_setup && : >expect && git difftool --no-prompt --gui --no-gui >actual && @@ -155,7 +167,7 @@ test_expect_success PERL 'difftool --gui last setting wins' ' test_cmp expect actual ' -test_expect_success PERL 'difftool --gui works without configured diff.guitool' ' +test_expect_success 'difftool --gui works without configured diff.guitool' ' difftool_test_setup && echo branch >expect && git difftool --no-prompt --gui branch >actual && @@ -163,7 +175,7 @@ test_expect_success PERL 'difftool --gui works without configured diff.guitool' ' # Specify the diff tool using $GIT_DIFF_TOOL -test_expect_success PERL 'GIT_DIFF_TOOL variable' ' +test_expect_success 'GIT_DIFF_TOOL variable' ' difftool_test_setup && git config --unset diff.tool && echo branch >expect && @@ -173,7 +185,7 @@ test_expect_success PERL 'GIT_DIFF_TOOL variable' ' # 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' ' +test_expect_success 'GIT_DIFF_TOOL overrides' ' difftool_test_setup && test_config diff.tool bogus-tool && test_config merge.tool bogus-tool && @@ -191,7 +203,7 @@ test_expect_success PERL 'GIT_DIFF_TOOL overrides' ' # 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' ' +test_expect_success 'GIT_DIFFTOOL_NO_PROMPT variable' ' difftool_test_setup && echo branch >expect && GIT_DIFFTOOL_NO_PROMPT=true git difftool branch >actual && @@ -200,7 +212,7 @@ test_expect_success PERL 'GIT_DIFFTOOL_NO_PROMPT variable' ' # 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' ' +test_expect_success 'GIT_DIFFTOOL_PROMPT variable' ' difftool_test_setup && test_config difftool.prompt false && echo >input && @@ -210,7 +222,7 @@ test_expect_success PERL 'GIT_DIFFTOOL_PROMPT variable' ' ' # 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' ' +test_expect_success 'difftool.prompt config variable is false' ' difftool_test_setup && test_config difftool.prompt false && echo branch >expect && @@ -219,7 +231,7 @@ test_expect_success PERL 'difftool.prompt config variable is false' ' ' # Test that we don't have to pass --no-prompt when mergetool.prompt is false -test_expect_success PERL 'difftool merge.prompt = false' ' +test_expect_success 'difftool merge.prompt = false' ' difftool_test_setup && test_might_fail git config --unset difftool.prompt && test_config mergetool.prompt false && @@ -229,7 +241,7 @@ test_expect_success PERL 'difftool merge.prompt = false' ' ' # Test that the -y flag can override difftool.prompt = true -test_expect_success PERL 'difftool.prompt can overridden with -y' ' +test_expect_success 'difftool.prompt can overridden with -y' ' difftool_test_setup && test_config difftool.prompt true && echo branch >expect && @@ -238,7 +250,7 @@ test_expect_success PERL 'difftool.prompt can overridden with -y' ' ' # Test that the --prompt flag can override difftool.prompt = false -test_expect_success PERL 'difftool.prompt can overridden with --prompt' ' +test_expect_success 'difftool.prompt can overridden with --prompt' ' difftool_test_setup && test_config difftool.prompt false && echo >input && @@ -248,7 +260,7 @@ test_expect_success PERL 'difftool.prompt can overridden with --prompt' ' ' # Test that the last flag passed on the command-line wins -test_expect_success PERL 'difftool last flag wins' ' +test_expect_success 'difftool last flag wins' ' difftool_test_setup && echo branch >expect && git difftool --prompt --no-prompt branch >actual && @@ -261,7 +273,7 @@ test_expect_success PERL 'difftool last flag wins' ' # git-difftool falls back to git-mergetool config variables # so test that behavior here -test_expect_success PERL 'difftool + mergetool config variables' ' +test_expect_success 'difftool + mergetool config variables' ' test_config merge.tool test-tool && test_config mergetool.test-tool.cmd "cat \$LOCAL" && echo branch >expect && @@ -275,49 +287,49 @@ test_expect_success PERL 'difftool + mergetool config variables' ' test_cmp expect actual ' -test_expect_success PERL 'difftool.<tool>.path' ' +test_expect_success 'difftool.<tool>.path' ' test_config difftool.tkdiff.path echo && git difftool --tool=tkdiff --no-prompt branch >output && - lines=$(grep file output | wc -l) && - test "$lines" -eq 1 + grep file output >grep-output && + test_line_count = 1 grep-output ' -test_expect_success PERL 'difftool --extcmd=cat' ' +test_expect_success 'difftool --extcmd=cat' ' 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' ' +test_expect_success 'difftool --extcmd cat' ' 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' ' +test_expect_success 'difftool -x cat' ' 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' ' +test_expect_success 'difftool --extcmd echo arg1' ' 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' ' +test_expect_success 'difftool --extcmd cat arg1' ' 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' ' +test_expect_success 'difftool --extcmd cat arg2' ' echo branch >expect && git difftool --no-prompt \ --extcmd sh\ -c\ \"cat\ \$2\" branch >actual && @@ -325,7 +337,7 @@ test_expect_success PERL 'difftool --extcmd cat arg2' ' ' # Create a second file on master and a different version on branch -test_expect_success PERL 'setup with 2 files different' ' +test_expect_success 'setup with 2 files different' ' echo m2 >file2 && git add file2 && git commit -m "added file2" && @@ -337,7 +349,7 @@ test_expect_success PERL 'setup with 2 files different' ' git checkout master ' -test_expect_success PERL 'say no to the first file' ' +test_expect_success 'say no to the first file' ' (echo n && echo) >input && git difftool -x cat branch <input >output && grep m2 output && @@ -346,7 +358,7 @@ test_expect_success PERL 'say no to the first file' ' ! grep branch output ' -test_expect_success PERL 'say no to the second file' ' +test_expect_success 'say no to the second file' ' (echo && echo n) >input && git difftool -x cat branch <input >output && grep master output && @@ -355,7 +367,7 @@ test_expect_success PERL 'say no to the second file' ' ! grep br2 output ' -test_expect_success PERL 'ending prompt input with EOF' ' +test_expect_success 'ending prompt input with EOF' ' git difftool -x cat branch </dev/null >output && ! grep master output && ! grep branch output && @@ -363,17 +375,18 @@ test_expect_success PERL 'ending prompt input with EOF' ' ! grep br2 output ' -test_expect_success PERL 'difftool --tool-help' ' +test_expect_success 'difftool --tool-help' ' git difftool --tool-help >output && grep tool output ' -test_expect_success PERL 'setup change in subdirectory' ' +test_expect_success 'setup change in subdirectory' ' git checkout master && mkdir sub && echo master >sub/sub && git add sub/sub && git commit -m "added sub/sub" && + git tag v1 && echo test >>file && echo test >>sub/sub && git add file sub/sub && @@ -381,11 +394,11 @@ test_expect_success PERL 'setup change in subdirectory' ' ' run_dir_diff_test () { - test_expect_success PERL "$1 --no-symlinks" " + test_expect_success "$1 --no-symlinks" " symlinks=--no-symlinks && $2 " - test_expect_success PERL,SYMLINKS "$1 --symlinks" " + test_expect_success SYMLINKS "$1 --symlinks" " symlinks=--symlinks && $2 " @@ -409,12 +422,56 @@ run_dir_diff_test 'difftool --dir-diff ignores --prompt' ' grep file output ' -run_dir_diff_test 'difftool --dir-diff from subdirectory' ' +run_dir_diff_test 'difftool --dir-diff branch from subdirectory' ' ( cd sub && git difftool --dir-diff $symlinks --extcmd ls branch >output && - grep sub output && - grep file output + # "sub" must only exist in "right" + # "file" and "file2" must be listed in both "left" and "right" + grep sub output > sub-output && + test_line_count = 1 sub-output && + grep file"$" output >file-output && + test_line_count = 2 file-output && + grep file2 output >file2-output && + test_line_count = 2 file2-output + ) +' + +run_dir_diff_test 'difftool --dir-diff v1 from subdirectory' ' + ( + cd sub && + git difftool --dir-diff $symlinks --extcmd ls v1 >output && + # "sub" and "file" exist in both v1 and HEAD. + # "file2" is unchanged. + grep sub output >sub-output && + test_line_count = 2 sub-output && + grep file output >file-output && + test_line_count = 2 file-output && + ! grep file2 output + ) +' + +run_dir_diff_test 'difftool --dir-diff branch from subdirectory w/ pathspec' ' + ( + cd sub && + git difftool --dir-diff $symlinks --extcmd ls branch -- .>output && + # "sub" only exists in "right" + # "file" and "file2" must not be listed + grep sub output >sub-output && + test_line_count = 1 sub-output && + ! grep file output + ) +' + +run_dir_diff_test 'difftool --dir-diff v1 from subdirectory w/ pathspec' ' + ( + cd sub && + git difftool --dir-diff $symlinks --extcmd ls v1 -- .>output && + # "sub" exists in v1 and HEAD + # "file" is filtered out by the pathspec + grep sub output >sub-output && + test_line_count = 2 sub-output && + ! grep file output ) ' @@ -470,7 +527,7 @@ do done >actual EOF -test_expect_success PERL,SYMLINKS 'difftool --dir-diff --symlink without unstaged changes' ' +test_expect_success SYMLINKS 'difftool --dir-diff --symlink without unstaged changes' ' cat >expect <<-EOF && file $PWD/file @@ -507,7 +564,7 @@ write_script modify-file <<\EOF echo "new content" >file EOF -test_expect_success PERL 'difftool --no-symlinks does not overwrite working tree file ' ' +test_expect_success '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 && @@ -520,7 +577,7 @@ echo "tmp content" >"$2/file" && echo "$2" >tmpdir EOF -test_expect_success PERL 'difftool --no-symlinks detects conflict ' ' +test_expect_success 'difftool --no-symlinks detects conflict ' ' ( TMPDIR=$TRASH_DIRECTORY && export TMPDIR && @@ -533,7 +590,7 @@ test_expect_success PERL 'difftool --no-symlinks detects conflict ' ' ) ' -test_expect_success PERL 'difftool properly honors gitlink and core.worktree' ' +test_expect_success 'difftool properly honors gitlink and core.worktree' ' git submodule add ./. submod/ule && test_config -C submod/ule diff.tool checktrees && test_config -C submod/ule difftool.checktrees.cmd '\'' @@ -547,7 +604,7 @@ test_expect_success PERL 'difftool properly honors gitlink and core.worktree' ' ) ' -test_expect_success PERL,SYMLINKS 'difftool --dir-diff symlinked directories' ' +test_expect_success SYMLINKS 'difftool --dir-diff symlinked directories' ' git init dirlinks && ( cd dirlinks && diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh index de2405ccba..cee42097b0 100755 --- a/t/t7810-grep.sh +++ b/t/t7810-grep.sh @@ -39,6 +39,10 @@ test_expect_success setup ' echo "a+bc" echo "abc" } >ab && + { + echo d && + echo 0 + } >d0 && echo vvv >v && echo ww w >w && echo x x xx x >x && @@ -978,6 +982,72 @@ test_expect_success 'grep -e -- -- path' ' test_cmp expected actual ' +test_expect_success 'dashdash disambiguates rev as rev' ' + test_when_finished "rm -f master" && + echo content >master && + echo master:hello.c >expect && + git grep -l o master -- hello.c >actual && + test_cmp expect actual +' + +test_expect_success 'dashdash disambiguates pathspec as pathspec' ' + test_when_finished "git rm -f master" && + echo content >master && + git add master && + echo master:content >expect && + git grep o -- master >actual && + test_cmp expect actual +' + +test_expect_success 'report bogus arg without dashdash' ' + test_must_fail git grep o does-not-exist +' + +test_expect_success 'report bogus rev with dashdash' ' + test_must_fail git grep o hello.c -- +' + +test_expect_success 'allow non-existent path with dashdash' ' + # We need a real match so grep exits with success. + tree=$(git ls-tree HEAD | + sed s/hello.c/not-in-working-tree/ | + git mktree) && + git grep o "$tree" -- not-in-working-tree +' + +test_expect_success 'grep --no-index pattern -- path' ' + rm -fr non && + mkdir -p non/git && + ( + GIT_CEILING_DIRECTORIES="$(pwd)/non" && + export GIT_CEILING_DIRECTORIES && + cd non/git && + echo hello >hello && + echo goodbye >goodbye && + echo hello:hello >expect && + git grep --no-index o -- hello >actual && + test_cmp expect actual + ) +' + +test_expect_success 'grep --no-index complains of revs' ' + test_must_fail git grep --no-index o master -- 2>err && + test_i18ngrep "cannot be used with revs" err +' + +test_expect_success 'grep --no-index prefers paths to revs' ' + test_when_finished "rm -f master" && + echo content >master && + echo master:content >expect && + git grep --no-index o master >actual && + test_cmp expect actual +' + +test_expect_success 'grep --no-index does not "diagnose" revs' ' + test_must_fail git grep --no-index o :1:hello.c 2>err && + test_i18ngrep ! -i "did you mean" err +' + cat >expected <<EOF hello.c:int main(int argc, const char **argv) hello.c: printf("Hello world.\n"); @@ -1105,36 +1175,36 @@ test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =extended ' 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 + echo "d0:d" >expected && + git grep -G -F -P -E "[\d]" d0 >actual && + test_cmp expected actual ' test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =perl, =extended' ' - >empty && - test_must_fail git \ + echo "d0:d" >expected && + 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 + grep "[\d]" d0 >actual && + test_cmp expected 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 && + echo "d0:0" >expected && + git grep -G -F -E -P "[\d]" d0 >actual && test_cmp expected actual ' test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' ' - echo "ab:a+b*c" >expected && + echo "d0:0" >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 && + grep "[\d]" d0 >actual && test_cmp expected actual ' diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh new file mode 100755 index 0000000000..67247a01d6 --- /dev/null +++ b/t/t7814-grep-recurse-submodules.sh @@ -0,0 +1,241 @@ +#!/bin/sh + +test_description='Test grep recurse-submodules feature + +This test verifies the recurse-submodules feature correctly greps across +submodules. +' + +. ./test-lib.sh + +test_expect_success 'setup directory structure and submodule' ' + echo "foobar" >a && + mkdir b && + echo "bar" >b/b && + git add a b && + git commit -m "add a and b" && + git init submodule && + echo "foobar" >submodule/a && + git -C submodule add a && + git -C submodule commit -m "add a" && + git submodule add ./submodule && + git commit -m "added submodule" +' + +test_expect_success 'grep correctly finds patterns in a submodule' ' + cat >expect <<-\EOF && + a:foobar + b/b:bar + submodule/a:foobar + EOF + + git grep -e "bar" --recurse-submodules >actual && + test_cmp expect actual +' + +test_expect_success 'grep and basic pathspecs' ' + cat >expect <<-\EOF && + submodule/a:foobar + EOF + + git grep -e. --recurse-submodules -- submodule >actual && + test_cmp expect actual +' + +test_expect_success 'grep and nested submodules' ' + git init submodule/sub && + echo "foobar" >submodule/sub/a && + git -C submodule/sub add a && + git -C submodule/sub commit -m "add a" && + git -C submodule submodule add ./sub && + git -C submodule add sub && + git -C submodule commit -m "added sub" && + git add submodule && + git commit -m "updated submodule" && + + cat >expect <<-\EOF && + a:foobar + b/b:bar + submodule/a:foobar + submodule/sub/a:foobar + EOF + + git grep -e "bar" --recurse-submodules >actual && + test_cmp expect actual +' + +test_expect_success 'grep and multiple patterns' ' + cat >expect <<-\EOF && + a:foobar + submodule/a:foobar + submodule/sub/a:foobar + EOF + + git grep -e "bar" --and -e "foo" --recurse-submodules >actual && + test_cmp expect actual +' + +test_expect_success 'grep and multiple patterns' ' + cat >expect <<-\EOF && + b/b:bar + EOF + + git grep -e "bar" --and --not -e "foo" --recurse-submodules >actual && + test_cmp expect actual +' + +test_expect_success 'basic grep tree' ' + cat >expect <<-\EOF && + HEAD:a:foobar + HEAD:b/b:bar + HEAD:submodule/a:foobar + HEAD:submodule/sub/a:foobar + EOF + + git grep -e "bar" --recurse-submodules HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'grep tree HEAD^' ' + cat >expect <<-\EOF && + HEAD^:a:foobar + HEAD^:b/b:bar + HEAD^:submodule/a:foobar + EOF + + git grep -e "bar" --recurse-submodules HEAD^ >actual && + test_cmp expect actual +' + +test_expect_success 'grep tree HEAD^^' ' + cat >expect <<-\EOF && + HEAD^^:a:foobar + HEAD^^:b/b:bar + EOF + + git grep -e "bar" --recurse-submodules HEAD^^ >actual && + test_cmp expect actual +' + +test_expect_success 'grep tree and pathspecs' ' + cat >expect <<-\EOF && + HEAD:submodule/a:foobar + HEAD:submodule/sub/a:foobar + EOF + + git grep -e "bar" --recurse-submodules HEAD -- submodule >actual && + test_cmp expect actual +' + +test_expect_success 'grep tree and pathspecs' ' + cat >expect <<-\EOF && + HEAD:submodule/a:foobar + HEAD:submodule/sub/a:foobar + EOF + + git grep -e "bar" --recurse-submodules HEAD -- "submodule*a" >actual && + test_cmp expect actual +' + +test_expect_success 'grep tree and more pathspecs' ' + cat >expect <<-\EOF && + HEAD:submodule/a:foobar + EOF + + git grep -e "bar" --recurse-submodules HEAD -- "submodul?/a" >actual && + test_cmp expect actual +' + +test_expect_success 'grep tree and more pathspecs' ' + cat >expect <<-\EOF && + HEAD:submodule/sub/a:foobar + EOF + + git grep -e "bar" --recurse-submodules HEAD -- "submodul*/sub/a" >actual && + test_cmp expect actual +' + +test_expect_success !MINGW 'grep recurse submodule colon in name' ' + git init parent && + test_when_finished "rm -rf parent" && + echo "foobar" >"parent/fi:le" && + git -C parent add "fi:le" && + git -C parent commit -m "add fi:le" && + + git init "su:b" && + test_when_finished "rm -rf su:b" && + echo "foobar" >"su:b/fi:le" && + git -C "su:b" add "fi:le" && + git -C "su:b" commit -m "add fi:le" && + + git -C parent submodule add "../su:b" "su:b" && + git -C parent commit -m "add submodule" && + + cat >expect <<-\EOF && + fi:le:foobar + su:b/fi:le:foobar + EOF + git -C parent grep -e "foobar" --recurse-submodules >actual && + test_cmp expect actual && + + cat >expect <<-\EOF && + HEAD:fi:le:foobar + HEAD:su:b/fi:le:foobar + EOF + git -C parent grep -e "foobar" --recurse-submodules HEAD >actual && + test_cmp expect actual +' + +test_expect_success 'grep history with moved submoules' ' + git init parent && + test_when_finished "rm -rf parent" && + echo "foobar" >parent/file && + git -C parent add file && + git -C parent commit -m "add file" && + + git init sub && + test_when_finished "rm -rf sub" && + echo "foobar" >sub/file && + git -C sub add file && + git -C sub commit -m "add file" && + + git -C parent submodule add ../sub dir/sub && + git -C parent commit -m "add submodule" && + + cat >expect <<-\EOF && + dir/sub/file:foobar + file:foobar + EOF + git -C parent grep -e "foobar" --recurse-submodules >actual && + test_cmp expect actual && + + git -C parent mv dir/sub sub-moved && + git -C parent commit -m "moved submodule" && + + cat >expect <<-\EOF && + file:foobar + sub-moved/file:foobar + EOF + git -C parent grep -e "foobar" --recurse-submodules >actual && + test_cmp expect actual && + + cat >expect <<-\EOF && + HEAD^:dir/sub/file:foobar + HEAD^:file:foobar + EOF + git -C parent grep -e "foobar" --recurse-submodules HEAD^ >actual && + test_cmp expect actual +' + +test_incompatible_with_recurse_submodules () +{ + test_expect_success "--recurse-submodules and $1 are incompatible" " + test_must_fail git grep -e. --recurse-submodules $1 2>actual && + test_i18ngrep 'not supported with --recurse-submodules' actual + " +} + +test_incompatible_with_recurse_submodules --untracked +test_incompatible_with_recurse_submodules --no-index + +test_done diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh index ab79de9544..380e1c1054 100755 --- a/t/t8002-blame.sh +++ b/t/t8002-blame.sh @@ -86,4 +86,36 @@ test_expect_success 'blame with showEmail config true' ' test_cmp expected_n result ' +test_expect_success 'set up abbrev tests' ' + test_commit abbrev && + sha1=$(git rev-parse --verify HEAD) && + check_abbrev () { + expect=$1; shift + echo $sha1 | cut -c 1-$expect >expect && + git blame "$@" abbrev.t >actual && + perl -lne "/[0-9a-f]+/ and print \$&" <actual >actual.sha && + test_cmp expect actual.sha + } +' + +test_expect_success 'blame --abbrev=<n> works' ' + # non-boundary commits get +1 for alignment + check_abbrev 31 --abbrev=30 HEAD && + check_abbrev 30 --abbrev=30 ^HEAD +' + +test_expect_success 'blame -l aligns regular and boundary commits' ' + check_abbrev 40 -l HEAD && + check_abbrev 39 -l ^HEAD +' + +test_expect_success 'blame --abbrev=40 behaves like -l' ' + check_abbrev 40 --abbrev=40 HEAD && + check_abbrev 39 --abbrev=40 ^HEAD +' + +test_expect_success '--no-abbrev works like --abbrev=40' ' + check_abbrev 40 --no-abbrev +' + test_done diff --git a/t/t8003-blame-corner-cases.sh b/t/t8003-blame-corner-cases.sh index e48370dfa0..661f9d430d 100755 --- a/t/t8003-blame-corner-cases.sh +++ b/t/t8003-blame-corner-cases.sh @@ -212,12 +212,12 @@ EOF test_expect_success 'blame -L with invalid start' ' test_must_fail git blame -L5 tres 2>errors && - grep "has only 2 lines" errors + test_i18ngrep "has only 2 lines" errors ' test_expect_success 'blame -L with invalid end' ' test_must_fail git blame -L1,5 tres 2>errors && - grep "has only 2 lines" errors + test_i18ngrep "has only 2 lines" errors ' test_expect_success 'blame parses <end> part of -L' ' diff --git a/t/t8010-cat-file-filters.sh b/t/t8010-cat-file-filters.sh new file mode 100755 index 0000000000..d8242e467e --- /dev/null +++ b/t/t8010-cat-file-filters.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +test_description='git cat-file filters support' +. ./test-lib.sh + +test_expect_success 'setup ' ' + echo "*.txt eol=crlf diff=txt" >.gitattributes && + echo "hello" | append_cr >world.txt && + git add .gitattributes world.txt && + test_tick && + git commit -m "Initial commit" +' + +has_cr () { + tr '\015' Q <"$1" | grep Q >/dev/null +} + +test_expect_success 'no filters with `git show`' ' + git show HEAD:world.txt >actual && + ! has_cr actual + +' + +test_expect_success 'no filters with cat-file' ' + git cat-file blob HEAD:world.txt >actual && + ! has_cr actual +' + +test_expect_success 'cat-file --filters converts to worktree version' ' + git cat-file --filters HEAD:world.txt >actual && + has_cr actual +' + +test_expect_success 'cat-file --filters --path=<path> works' ' + sha1=$(git rev-parse -q --verify HEAD:world.txt) && + git cat-file --filters --path=world.txt $sha1 >actual && + has_cr actual +' + +test_expect_success 'cat-file --textconv --path=<path> works' ' + sha1=$(git rev-parse -q --verify HEAD:world.txt) && + test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" && + git cat-file --textconv --path=hello.txt $sha1 >rot13 && + test uryyb = "$(cat rot13 | remove_cr)" +' + +test_expect_success '--path=<path> complains without --textconv/--filters' ' + sha1=$(git rev-parse -q --verify HEAD:world.txt) && + test_must_fail git cat-file --path=hello.txt blob $sha1 >actual 2>err && + test ! -s actual && + grep "path.*needs.*filters" err +' + +test_expect_success 'cat-file --textconv --batch works' ' + sha1=$(git rev-parse -q --verify HEAD:world.txt) && + test_config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <" && + printf "%s hello.txt\n%s hello\n" $sha1 $sha1 | + git cat-file --textconv --batch >actual && + printf "%s blob 6\nuryyb\r\n\n%s blob 6\nhello\n\n" \ + $sha1 $sha1 >expect && + test_cmp expect actual +' + +test_done diff --git a/t/t8011-blame-split-file.sh b/t/t8011-blame-split-file.sh new file mode 100755 index 0000000000..831125047b --- /dev/null +++ b/t/t8011-blame-split-file.sh @@ -0,0 +1,117 @@ +#!/bin/sh + +test_description=' +The general idea is that we have a single file whose lines come from +multiple other files, and those individual files were modified in the same +commits. That means that we will see the same commit in multiple contexts, +and each one should be attributed to the correct file. + +Note that we need to use "blame -C" to find the commit for all lines. We will +not bother testing that the non-C case fails to find it. That is how blame +behaves now, but it is not a property we want to make sure is retained. +' +. ./test-lib.sh + +# help avoid typing and reading long strings of similar lines +# in the tests below +generate_expect () { + while read nr data + do + i=0 + while test $i -lt $nr + do + echo $data + i=$((i + 1)) + done + done +} + +test_expect_success 'setup split file case' ' + # use lines long enough to trigger content detection + test_seq 1000 1010 >one && + test_seq 2000 2010 >two && + git add one two && + test_commit base && + + sed "6s/^/modified /" <one >one.tmp && + mv one.tmp one && + sed "6s/^/modified /" <two >two.tmp && + mv two.tmp two && + git add -u && + test_commit modified && + + cat one two >combined && + git add combined && + git rm one two && + test_commit combined +' + +test_expect_success 'setup simulated porcelain' ' + # This just reads porcelain-ish output and tries + # to output the value of a given field for each line (either by + # reading the field that accompanies this line, or referencing + # the information found last time the commit was mentioned). + cat >read-porcelain.pl <<-\EOF + my $field = shift; + while (<>) { + if (/^[0-9a-f]{40} /) { + flush(); + $hash = $&; + } elsif (/^$field (.*)/) { + $cache{$hash} = $1; + } + } + flush(); + + sub flush { + return unless defined $hash; + if (defined $cache{$hash}) { + print "$cache{$hash}\n"; + } else { + print "NONE\n"; + } + } + EOF +' + +for output in porcelain line-porcelain +do + test_expect_success "generate --$output output" ' + git blame --root -C --$output combined >output + ' + + test_expect_success "$output output finds correct commits" ' + generate_expect >expect <<-\EOF && + 5 base + 1 modified + 10 base + 1 modified + 5 base + EOF + perl read-porcelain.pl summary <output >actual && + test_cmp expect actual + ' + + test_expect_success "$output output shows correct filenames" ' + generate_expect >expect <<-\EOF && + 11 one + 11 two + EOF + perl read-porcelain.pl filename <output >actual && + test_cmp expect actual + ' + + test_expect_success "$output output shows correct previous pointer" ' + generate_expect >expect <<-EOF && + 5 NONE + 1 $(git rev-parse modified^) one + 10 NONE + 1 $(git rev-parse modified^) two + 5 NONE + EOF + perl read-porcelain.pl previous <output >actual && + test_cmp expect actual + ' +done + +test_done diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index 3dc4a3454d..0f398dd160 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -50,7 +50,7 @@ test_no_confirm () { --smtp-server="$(pwd)/fake.sendmail" \ $@ \ $patches >stdout && - test_must_fail grep "Send this email" stdout && + ! grep "Send this email" stdout && >no_confirm_okay } diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh index 92a3aa8063..8a8ba65a2a 100755 --- a/t/t9100-git-svn-basic.sh +++ b/t/t9100-git-svn-basic.sh @@ -17,25 +17,12 @@ case "$GIT_SVN_LC_ALL" in ;; esac -deepdir=nothing-above -ceiling=$PWD - test_expect_success 'git svn --version works anywhere' ' - mkdir -p "$deepdir" && ( - GIT_CEILING_DIRECTORIES="$ceiling" && - export GIT_CEILING_DIRECTORIES && - cd "$deepdir" && - git svn --version - ) + nongit git svn --version ' test_expect_success 'git svn help works anywhere' ' - mkdir -p "$deepdir" && ( - GIT_CEILING_DIRECTORIES="$ceiling" && - export GIT_CEILING_DIRECTORIES && - cd "$deepdir" && - git svn help - ) + nongit git svn help ' test_expect_success \ diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh index 69a675052e..044f65e916 100755 --- a/t/t9117-git-svn-init-clone.sh +++ b/t/t9117-git-svn-init-clone.sh @@ -55,7 +55,7 @@ test_expect_success 'clone to target directory with --stdlayout' ' 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 && + ! grep -q prefix warning && rm -rf trunk && rm -f warning ' @@ -63,7 +63,7 @@ test_expect_success 'init without -s/-T/-b/-t does not warn' ' 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 && + ! grep -q prefix warning && rm -rf trunk && rm -f warning ' @@ -86,7 +86,7 @@ EOF test_expect_success 'init with -s/-T/-b/-t assumes --prefix=origin/' ' test ! -d project && git svn init -s "$svnrepo"/project project 2>warning && - test_must_fail grep -q prefix warning && + ! grep -q prefix warning && test_svn_configured_prefix "origin/" && rm -rf project && rm -f warning @@ -95,7 +95,7 @@ test_expect_success 'init with -s/-T/-b/-t assumes --prefix=origin/' ' test_expect_success 'clone with -s/-T/-b/-t assumes --prefix=origin/' ' test ! -d project && git svn clone -s "$svnrepo"/project 2>warning && - test_must_fail grep -q prefix warning && + ! grep -q prefix warning && test_svn_configured_prefix "origin/" && rm -rf project && rm -f warning @@ -104,7 +104,7 @@ test_expect_success 'clone with -s/-T/-b/-t assumes --prefix=origin/' ' test_expect_success 'init with -s/-T/-b/-t and --prefix "" still works' ' test ! -d project && git svn init -s "$svnrepo"/project project --prefix "" 2>warning && - test_must_fail grep -q prefix warning && + ! grep -q prefix warning && test_svn_configured_prefix "" && rm -rf project && rm -f warning @@ -113,7 +113,7 @@ test_expect_success 'init with -s/-T/-b/-t and --prefix "" still works' ' test_expect_success 'clone with -s/-T/-b/-t and --prefix "" still works' ' test ! -d project && git svn clone -s "$svnrepo"/project --prefix "" 2>warning && - test_must_fail grep -q prefix warning && + ! grep -q prefix warning && test_svn_configured_prefix "" && rm -rf project && rm -f warning diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh index 83acf68bc3..dadc70b7d5 100755 --- a/t/t9301-fast-import-notes.sh +++ b/t/t9301-fast-import-notes.sh @@ -483,6 +483,48 @@ test_expect_success 'verify that lots of notes trigger a fanout scheme' ' ' +# Create another notes tree from the one above +SP=" " +cat >>input <<INPUT_END +commit refs/heads/other_commits +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +commit #$(($num_commit + 1)) +COMMIT + +from refs/heads/many_commits +M 644 inline file +data <<EOF +file contents in commit #$(($num_commit + 1)) +EOF + +commit refs/notes/other_notes +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE +data <<COMMIT +committing one more note on a tree imported from a previous notes tree +COMMIT + +M 040000 $(git log --no-walk --format=%T refs/notes/many_notes)$SP +N inline :$(($num_commit + 1)) +data <<EOF +note for commit #$(($num_commit + 1)) +EOF +INPUT_END + +test_expect_success 'verify that importing a notes tree respects the fanout scheme' ' + git fast-import <input && + + # None of the entries in the top-level notes tree should be a full SHA1 + git ls-tree --name-only refs/notes/other_notes | + while read path + do + if test $(expr length "$path") -ge 40 + then + return 1 + fi + done +' + cat >>expect_non-note1 << EOF This is not a note, but rather a regular file residing in a notes tree EOF diff --git a/t/t9303-fast-import-compression.sh b/t/t9303-fast-import-compression.sh new file mode 100755 index 0000000000..856219f46a --- /dev/null +++ b/t/t9303-fast-import-compression.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +test_description='compression setting of fast-import utility' +. ./test-lib.sh + +# This should be moved to test-lib.sh together with the +# copy in t0021 after both topics have graduated to 'master'. +file_size () { + perl -e 'print -s $ARGV[0]' "$1" +} + +import_large () { + ( + echo blob + echo "data <<EOD" + printf "%2000000s\n" "$*" + echo EOD + ) | git "$@" fast-import +} + +while read expect config +do + test_expect_success "fast-import (packed) with $config" ' + test_when_finished "rm -f .git/objects/pack/pack-*.*" && + test_when_finished "rm -rf .git/objects/??" && + import_large -c fastimport.unpacklimit=0 $config && + sz=$(file_size .git/objects/pack/pack-*.pack) && + case "$expect" in + small) test "$sz" -le 100000 ;; + large) test "$sz" -ge 100000 ;; + esac + ' +done <<\EOF +large -c core.compression=0 +small -c core.compression=9 +large -c core.compression=0 -c pack.compression=0 +large -c core.compression=9 -c pack.compression=0 +small -c core.compression=0 -c pack.compression=9 +small -c core.compression=9 -c pack.compression=9 +large -c pack.compression=0 +small -c pack.compression=9 +EOF + +while read expect config +do + test_expect_success "fast-import (loose) with $config" ' + test_when_finished "rm -f .git/objects/pack/pack-*.*" && + test_when_finished "rm -rf .git/objects/??" && + import_large -c fastimport.unpacklimit=9 $config && + sz=$(file_size .git/objects/??/????*) && + case "$expect" in + small) test "$sz" -le 100000 ;; + large) test "$sz" -ge 100000 ;; + esac + ' +done <<\EOF +large -c core.compression=0 +small -c core.compression=9 +large -c core.compression=0 -c core.loosecompression=0 +large -c core.compression=9 -c core.loosecompression=0 +small -c core.compression=0 -c core.loosecompression=9 +small -c core.compression=9 -c core.loosecompression=9 +large -c core.loosecompression=0 +small -c core.loosecompression=9 +EOF + +test_done diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh index e94b2f147a..6d06ed96cb 100755 --- a/t/t9500-gitweb-standalone-no-errors.sh +++ b/t/t9500-gitweb-standalone-no-errors.sh @@ -709,6 +709,14 @@ test_expect_success HIGHLIGHT \ git commit -m "Add test.sh" && gitweb_run "p=.git;a=blob;f=test.sh"' +test_expect_success HIGHLIGHT \ + 'syntax highlighting (highlighter language autodetection)' \ + 'git config gitweb.highlight yes && + echo "#!/usr/bin/perl" > test && + git add test && + git commit -m "Add test" && + gitweb_run "p=.git;a=blob;f=test"' + # ---------------------------------------------------------------------- # forks of projects diff --git a/t/t9800-git-p4-basic.sh b/t/t9800-git-p4-basic.sh index 0730f18d0f..4849edc4ef 100755 --- a/t/t9800-git-p4-basic.sh +++ b/t/t9800-git-p4-basic.sh @@ -131,6 +131,26 @@ test_expect_success 'clone two dirs, @all, conflicting files' ' ) ' +test_expect_success 'clone two dirs, each edited by submit, single git commit' ' + ( + cd "$cli" && + echo sub1/f4 >sub1/f4 && + p4 add sub1/f4 && + echo sub2/f4 >sub2/f4 && + p4 add sub2/f4 && + p4 submit -d "sub1/f4 and sub2/f4" + ) && + git p4 clone --dest="$git" //depot/sub1@all //depot/sub2@all && + test_when_finished cleanup_git && + ( + cd "$git" && + git ls-files >lines && + test_line_count = 4 lines && + git log --oneline p4/master >lines && + test_line_count = 5 lines + ) +' + revision_ranges="2000/01/01,#head \ 1,2080/01/01 \ 2000/01/01,2080/01/01 \ @@ -147,7 +167,7 @@ test_expect_success 'clone using non-numeric revision ranges' ' ( cd "$git" && git ls-files >lines && - test_line_count = 6 lines + test_line_count = 8 lines ) done ' @@ -257,6 +277,26 @@ test_expect_success 'submit from detached head' ' ) ' +test_expect_success 'submit from worktree' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$git" && + git worktree add ../worktree-test + ) && + ( + cd "$git/../worktree-test" && + test_commit "worktree-commit" && + git config git-p4.skipSubmitEdit true && + git p4 submit + ) && + ( + cd "$cli" && + p4 sync && + test_path_is_file worktree-commit.t + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9806-git-p4-options.sh b/t/t9806-git-p4-options.sh index 254d428b73..1ab76c4246 100755 --- a/t/t9806-git-p4-options.sh +++ b/t/t9806-git-p4-options.sh @@ -269,6 +269,38 @@ test_expect_success 'submit works with two branches' ' ) ' +test_expect_success 'use --git-dir option and GIT_DIR' ' + test_when_finished cleanup_git && + git p4 clone //depot --destination="$git" && + ( + cd "$git" && + git config git-p4.skipSubmitEdit true && + test_commit first-change && + git p4 submit --git-dir "$git" + ) && + ( + cd "$cli" && + p4 sync && + test_path_is_file first-change.t && + echo "cli_file" >cli_file.t && + p4 add cli_file.t && + p4 submit -d "cli change" + ) && + (git --git-dir "$git" p4 sync) && + (cd "$git" && git checkout -q p4/master) && + test_path_is_file "$git"/cli_file.t && + ( + cd "$cli" && + echo "cli_file2" >cli_file2.t && + p4 add cli_file2.t && + p4 submit -d "cli change2" + ) && + (GIT_DIR="$git" git p4 sync) && + (cd "$git" && git checkout -q p4/master) && + test_path_is_file "$git"/cli_file2.t +' + + test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9807-git-p4-submit.sh b/t/t9807-git-p4-submit.sh index 593152817d..e37239e657 100755 --- a/t/t9807-git-p4-submit.sh +++ b/t/t9807-git-p4-submit.sh @@ -413,6 +413,75 @@ test_expect_success 'submit --prepare-p4-only' ' ) ' +test_expect_success 'submit --shelve' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 revert ... && + cd "$git" && + git config git-p4.skipSubmitEdit true && + test_commit "shelveme1" && + git p4 submit --origin=HEAD^ && + + echo 654321 >shelveme2.t && + echo 123456 >>shelveme1.t && + git add shelveme* && + git commit -m"shelvetest" && + git p4 submit --shelve --origin=HEAD^ && + + test_path_is_file shelveme1.t && + test_path_is_file shelveme2.t + ) && + ( + cd "$cli" && + change=$(p4 -G changes -s shelved -m 1 //depot/... | \ + marshal_dump change) && + p4 describe -S $change | grep shelveme2 && + p4 describe -S $change | grep 123456 && + test_path_is_file shelveme1.t && + test_path_is_missing shelveme2.t + ) +' + +# Update an existing shelved changelist + +test_expect_success 'submit --update-shelve' ' + test_when_finished cleanup_git && + git p4 clone --dest="$git" //depot && + ( + cd "$cli" && + p4 revert ... && + cd "$git" && + git config git-p4.skipSubmitEdit true && + test_commit "test-update-shelved-change" && + git p4 submit --origin=HEAD^ --shelve && + + shelf_cl=$(p4 -G changes -s shelved -m 1 |\ + marshal_dump change) && + test -n $shelf_cl && + echo "updating shelved change list $shelf_cl" && + + echo "updated-line" >>shelf.t && + echo added-file.t >added-file.t && + git add shelf.t added-file.t && + git rm -f test-update-shelved-change.t && + git commit --amend -C HEAD && + git show --stat HEAD && + git p4 submit -v --origin HEAD^ --update-shelve $shelf_cl && + echo "done git p4 submit" + ) && + ( + cd "$cli" && + change=$(p4 -G changes -s shelved -m 1 //depot/... | \ + marshal_dump change) && + p4 unshelve -c $change -s $change && + grep -q updated-line shelf.t && + p4 describe -S $change | grep added-file.t && + test_path_is_missing test-update-shelved-change.t + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9813-git-p4-preserve-users.sh b/t/t9813-git-p4-preserve-users.sh index 0fe2312807..bda222aa02 100755 --- a/t/t9813-git-p4-preserve-users.sh +++ b/t/t9813-git-p4-preserve-users.sh @@ -118,21 +118,21 @@ test_expect_success 'not preserving user with mixed authorship' ' make_change_by_user usernamefile3 Derek derek@example.com && P4EDITOR=cat P4USER=alice P4PASSWD=secret && export P4EDITOR P4USER P4PASSWD && - git p4 commit |\ - grep "git author derek@example.com does not match" && + git p4 commit >actual && + grep "git author derek@example.com does not match" actual && make_change_by_user usernamefile3 Charlie charlie@example.com && - git p4 commit |\ - grep "git author charlie@example.com does not match" && + git p4 commit >actual && + grep "git author charlie@example.com does not match" actual && make_change_by_user usernamefile3 alice alice@example.com && - git p4 commit |\ - test_must_fail grep "git author.*does not match" && + git p4 commit >actual && + ! grep "git author.*does not match" actual && git config git-p4.skipUserNameCheck true && make_change_by_user usernamefile3 Charlie charlie@example.com && - git p4 commit |\ - test_must_fail grep "git author.*does not match" && + git p4 commit >actual && + ! grep "git author.*does not match" actual && p4_check_commit_author usernamefile3 alice ) diff --git a/t/t9814-git-p4-rename.sh b/t/t9814-git-p4-rename.sh index c89992cf95..e7e0268e98 100755 --- a/t/t9814-git-p4-rename.sh +++ b/t/t9814-git-p4-rename.sh @@ -141,7 +141,7 @@ test_expect_success 'detect copies' ' git diff-tree -r -C HEAD && git p4 submit && p4 filelog //depot/file8 && - p4 filelog //depot/file8 | test_must_fail grep -q "branch from" && + ! p4 filelog //depot/file8 | grep -q "branch from" && echo "file9" >>file2 && git commit -a -m "Differentiate file2" && @@ -154,7 +154,7 @@ test_expect_success 'detect copies' ' git config git-p4.detectCopies true && git p4 submit && p4 filelog //depot/file9 && - p4 filelog //depot/file9 | test_must_fail grep -q "branch from" && + ! p4 filelog //depot/file9 | grep -q "branch from" && echo "file10" >>file2 && git commit -a -m "Differentiate file2" && @@ -202,7 +202,7 @@ test_expect_success 'detect copies' ' 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" && + ! p4 filelog //depot/file12 | grep -q "branch from" && echo "file13" >>file2 && git commit -a -m "Differentiate file2" && diff --git a/t/t9822-git-p4-path-encoding.sh b/t/t9822-git-p4-path-encoding.sh index 7b83e696a9..c78477c19b 100755 --- a/t/t9822-git-p4-path-encoding.sh +++ b/t/t9822-git-p4-path-encoding.sh @@ -51,6 +51,22 @@ test_expect_success 'Clone repo containing iso8859-1 encoded paths with git-p4.p ) ' +test_expect_success 'Delete iso8859-1 encoded paths and clone' ' + ( + cd "$cli" && + ISO8859="$(printf "$ISO8859_ESCAPED")" && + p4 delete "$ISO8859" && + p4 submit -d "remove file" + ) && + git p4 clone --destination="$git" //depot@all && + test_when_finished cleanup_git && + ( + cd "$git" && + git -c core.quotepath=false ls-files >actual && + test_must_be_empty actual + ) +' + test_expect_success 'kill p4d' ' kill_p4d ' diff --git a/t/t9824-git-p4-git-lfs.sh b/t/t9824-git-p4-git-lfs.sh index 110a7e7924..ed80ca858c 100755 --- a/t/t9824-git-p4-git-lfs.sh +++ b/t/t9824-git-p4-git-lfs.sh @@ -42,6 +42,8 @@ test_expect_success 'Create repo with binary files' ' ( cd "$cli" && + >file0.dat && + p4 add file0.dat && echo "content 1 txt 23 bytes" >file1.txt && p4 add file1.txt && echo "content 2-3 bin 25 bytes" >file2.dat && @@ -81,9 +83,9 @@ test_expect_success 'Store files in LFS based on size (>24 bytes)' ' # # Git LFS (see https://git-lfs.github.com/) # - /file2.dat filter=lfs -text - /file4.bin filter=lfs -text - /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs -text + /file2.dat filter=lfs diff=lfs merge=lfs -text + /file4.bin filter=lfs diff=lfs merge=lfs -text + /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs diff=lfs merge=lfs -text EOF test_path_is_file .gitattributes && test_cmp expect .gitattributes @@ -109,7 +111,7 @@ test_expect_success 'Store files in LFS based on size (>25 bytes)' ' # # Git LFS (see https://git-lfs.github.com/) # - /file4.bin filter=lfs -text + /file4.bin filter=lfs diff=lfs merge=lfs -text EOF test_path_is_file .gitattributes && test_cmp expect .gitattributes @@ -135,7 +137,7 @@ test_expect_success 'Store files in LFS based on extension (dat)' ' # # Git LFS (see https://git-lfs.github.com/) # - *.dat filter=lfs -text + *.dat filter=lfs diff=lfs merge=lfs -text EOF test_path_is_file .gitattributes && test_cmp expect .gitattributes @@ -163,8 +165,8 @@ test_expect_success 'Store files in LFS based on size (>25 bytes) and extension # # Git LFS (see https://git-lfs.github.com/) # - *.dat filter=lfs -text - /file4.bin filter=lfs -text + *.dat filter=lfs diff=lfs merge=lfs -text + /file4.bin filter=lfs diff=lfs merge=lfs -text EOF test_path_is_file .gitattributes && test_cmp expect .gitattributes @@ -199,8 +201,8 @@ test_expect_success 'Remove file from repo and store files in LFS based on size # # Git LFS (see https://git-lfs.github.com/) # - /file2.dat filter=lfs -text - /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs -text + /file2.dat filter=lfs diff=lfs merge=lfs -text + /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs diff=lfs merge=lfs -text EOF test_path_is_file .gitattributes && test_cmp expect .gitattributes @@ -237,8 +239,8 @@ test_expect_success 'Add .gitattributes and store files in LFS based on size (>2 # # Git LFS (see https://git-lfs.github.com/) # - /file2.dat filter=lfs -text - /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs -text + /file2.dat filter=lfs diff=lfs merge=lfs -text + /path[[:space:]]with[[:space:]]spaces/file3.bin filter=lfs diff=lfs merge=lfs -text EOF test_path_is_file .gitattributes && test_cmp expect .gitattributes @@ -278,7 +280,7 @@ test_expect_success 'Add big files to repo and store files in LFS based on compr # # Git LFS (see https://git-lfs.github.com/) # - /file6.bin filter=lfs -text + /file6.bin filter=lfs diff=lfs merge=lfs -text EOF test_path_is_file .gitattributes && test_cmp expect .gitattributes diff --git a/t/t9830-git-p4-symlink-dir.sh b/t/t9830-git-p4-symlink-dir.sh new file mode 100755 index 0000000000..3dc528bb1e --- /dev/null +++ b/t/t9830-git-p4-symlink-dir.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +test_description='git p4 symlinked directories' + +. ./lib-git-p4.sh + +test_expect_success 'start p4d' ' + start_p4d +' + +test_expect_success 'symlinked directory' ' + ( + cd "$cli" && + : >first_file.t && + p4 add first_file.t && + p4 submit -d "first change" + ) && + git p4 clone --dest "$git" //depot && + ( + cd "$git" && + mkdir -p some/sub/directory && + mkdir -p other/subdir2 && + : > other/subdir2/file.t && + (cd some/sub/directory && ln -s ../../../other/subdir2 .) && + git add some other && + git commit -m "symlinks" && + git config git-p4.skipSubmitEdit true && + git p4 submit -v + ) && + ( + cd "$cli" && + p4 sync && + test -L some/sub/directory/subdir2 + test_path_is_file some/sub/directory/subdir2/file.t + ) + +' + +test_expect_success 'kill p4d' ' + kill_p4d +' + +test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 2ba62fbc17..d711bef240 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -98,7 +98,7 @@ test_gitcomp () { local -a COMPREPLY && sed -e 's/Z$//' >expected && - cur="$1" && + local cur="$1" && shift && __gitcomp "$@" && print_comp && @@ -113,7 +113,7 @@ test_gitcomp_nl () { local -a COMPREPLY && sed -e 's/Z$//' >expected && - cur="$1" && + local cur="$1" && shift && __gitcomp_nl "$@" && print_comp && @@ -124,145 +124,280 @@ invalid_variable_name='${foo.bar}' actual="$TRASH_DIRECTORY/actual" -test_expect_success 'setup for __gitdir tests' ' +if test_have_prereq MINGW +then + ROOT="$(pwd -W)" +else + ROOT="$(pwd)" +fi + +test_expect_success 'setup for __git_find_repo_path/__gitdir tests' ' mkdir -p subdir/subsubdir && + mkdir -p non-repo && git init otherrepo ' -test_expect_success '__gitdir - from command line (through $__git_dir)' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && +test_expect_success '__git_find_repo_path - from command line (through $__git_dir)' ' + echo "$ROOT/otherrepo/.git" >expected && ( - __git_dir="$TRASH_DIRECTORY/otherrepo/.git" && - __gitdir >"$actual" + __git_dir="$ROOT/otherrepo/.git" && + __git_find_repo_path && + echo "$__git_repo_path" >"$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' ' +test_expect_success '__git_find_repo_path - .git directory in cwd' ' echo ".git" >expected && - __gitdir >"$actual" && + ( + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - .git directory in parent' ' - echo "$(pwd -P)/.git" >expected && +test_expect_success '__git_find_repo_path - .git directory in parent' ' + echo "$ROOT/.git" >expected && ( cd subdir/subsubdir && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - cwd is a .git directory' ' +test_expect_success '__git_find_repo_path - cwd is a .git directory' ' echo "." >expected && ( cd .git && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - parent is a .git directory' ' - echo "$(pwd -P)/.git" >expected && +test_expect_success '__git_find_repo_path - parent is a .git directory' ' + echo "$ROOT/.git" >expected && ( cd .git/refs/heads && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && +test_expect_success '__git_find_repo_path - $GIT_DIR set while .git directory in cwd' ' + echo "$ROOT/otherrepo/.git" >expected && ( - GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && + GIT_DIR="$ROOT/otherrepo/.git" && export GIT_DIR && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' ' - echo "$TRASH_DIRECTORY/otherrepo/.git" >expected && +test_expect_success '__git_find_repo_path - $GIT_DIR set while .git directory in parent' ' + echo "$ROOT/otherrepo/.git" >expected && ( - GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" && + GIT_DIR="$ROOT/otherrepo/.git" && export GIT_DIR && cd subdir && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - from command line while "git -C"' ' + echo "$ROOT/.git" >expected && + ( + __git_dir="$ROOT/.git" && + __git_C_args=(-C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - relative dir from command line and "git -C"' ' + echo "$ROOT/otherrepo/.git" >expected && + ( + cd subdir && + __git_dir="otherrepo/.git" && + __git_C_args=(-C ..) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - non-existing $GIT_DIR' ' +test_expect_success '__git_find_repo_path - $GIT_DIR set while "git -C"' ' + echo "$ROOT/.git" >expected && ( - GIT_DIR="$TRASH_DIRECTORY/non-existing" && + GIT_DIR="$ROOT/.git" && export GIT_DIR && - test_must_fail __gitdir - ) + __git_C_args=(-C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" ' -function pwd_P_W () { - if test_have_prereq MINGW - then - pwd -W - else - pwd -P - fi -} +test_expect_success '__git_find_repo_path - relative dir in $GIT_DIR and "git -C"' ' + echo "$ROOT/otherrepo/.git" >expected && + ( + cd subdir && + GIT_DIR="otherrepo/.git" && + export GIT_DIR && + __git_C_args=(-C ..) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' -test_expect_success '__gitdir - gitfile in cwd' ' - echo "$(pwd_P_W)/otherrepo/.git" >expected && - echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git && +test_expect_success '__git_find_repo_path - "git -C" while .git directory in cwd' ' + echo "$ROOT/otherrepo/.git" >expected && + ( + __git_C_args=(-C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - "git -C" while cwd is a .git directory' ' + echo "$ROOT/otherrepo/.git" >expected && + ( + cd .git && + __git_C_args=(-C .. -C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - "git -C" while .git directory in parent' ' + echo "$ROOT/otherrepo/.git" >expected && + ( + cd subdir && + __git_C_args=(-C .. -C otherrepo) && + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_find_repo_path - non-existing path in "git -C"' ' + ( + __git_C_args=(-C non-existing) && + test_must_fail __git_find_repo_path && + printf "$__git_repo_path" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_find_repo_path - non-existing path in $__git_dir' ' + ( + __git_dir="non-existing" && + test_must_fail __git_find_repo_path && + printf "$__git_repo_path" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_find_repo_path - non-existing $GIT_DIR' ' + ( + GIT_DIR="$ROOT/non-existing" && + export GIT_DIR && + test_must_fail __git_find_repo_path && + printf "$__git_repo_path" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_find_repo_path - gitfile in cwd' ' + echo "$ROOT/otherrepo/.git" >expected && + echo "gitdir: $ROOT/otherrepo/.git" >subdir/.git && test_when_finished "rm -f subdir/.git" && ( cd subdir && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - gitfile in parent' ' - echo "$(pwd_P_W)/otherrepo/.git" >expected && - echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git && +test_expect_success '__git_find_repo_path - gitfile in parent' ' + echo "$ROOT/otherrepo/.git" >expected && + echo "gitdir: $ROOT/otherrepo/.git" >subdir/.git && test_when_finished "rm -f subdir/.git" && ( cd subdir/subsubdir && - __gitdir >"$actual" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' ' - echo "$(pwd -P)/otherrepo/.git" >expected && +test_expect_success SYMLINKS '__git_find_repo_path - resulting path avoids symlinks' ' + echo "$ROOT/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" + __git_find_repo_path && + echo "$__git_repo_path" >"$actual" ) && test_cmp expected "$actual" ' -test_expect_success '__gitdir - not a git repository' ' +test_expect_success '__git_find_repo_path - not a git repository' ' ( - cd subdir/subsubdir && - GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" && + cd non-repo && + GIT_CEILING_DIRECTORIES="$ROOT" && export GIT_CEILING_DIRECTORIES && - test_must_fail __gitdir - ) + test_must_fail __git_find_repo_path && + printf "$__git_repo_path" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__gitdir - finds repo' ' + echo "$ROOT/.git" >expected && + ( + cd subdir/subsubdir && + __gitdir >"$actual" + ) && + test_cmp expected "$actual" +' + + +test_expect_success '__gitdir - returns error when cant find repo' ' + ( + __git_dir="non-existing" && + test_must_fail __gitdir >"$actual" + ) && + test_must_be_empty "$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 '__gitcomp - trailing space - options' ' @@ -366,10 +501,286 @@ test_expect_success '__git_remotes - list remotes from $GIT_DIR/remotes and from git remote add remote_in_config_1 git://remote_1 && test_when_finished "git remote remove remote_in_config_2" && git remote add remote_in_config_2 git://remote_2 && - __git_remotes >actual && + ( + __git_remotes >actual + ) && test_cmp expect actual ' +test_expect_success '__git_is_configured_remote' ' + test_when_finished "git remote remove remote_1" && + git remote add remote_1 git://remote_1 && + test_when_finished "git remote remove remote_2" && + git remote add remote_2 git://remote_2 && + ( + verbose __git_is_configured_remote remote_2 && + test_must_fail __git_is_configured_remote non-existent + ) +' + +test_expect_success 'setup for ref completion' ' + git commit --allow-empty -m initial && + git branch matching-branch && + git tag matching-tag && + ( + cd otherrepo && + git commit --allow-empty -m initial && + git branch -m master master-in-other && + git branch branch-in-other && + git tag tag-in-other + ) && + git remote add other "$ROOT/otherrepo/.git" && + git fetch --no-tags other && + rm -f .git/FETCH_HEAD && + git init thirdrepo +' + +test_expect_success '__git_refs - simple' ' + cat >expected <<-EOF && + HEAD + master + matching-branch + other/branch-in-other + other/master-in-other + matching-tag + EOF + ( + cur= && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - full refs' ' + cat >expected <<-EOF && + refs/heads/master + refs/heads/matching-branch + EOF + ( + cur=refs/heads/ && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - repo given on the command line' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + tag-in-other + EOF + ( + __git_dir="$ROOT/otherrepo/.git" && + cur= && + __git_refs >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - remote on local file system' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + tag-in-other + EOF + ( + cur= && + __git_refs otherrepo >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - remote on local file system - full refs' ' + cat >expected <<-EOF && + refs/heads/branch-in-other + refs/heads/master-in-other + refs/tags/tag-in-other + EOF + ( + cur=refs/ && + __git_refs otherrepo >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + EOF + ( + cur= && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote - full refs' ' + cat >expected <<-EOF && + refs/heads/branch-in-other + refs/heads/master-in-other + refs/tags/tag-in-other + EOF + ( + cur=refs/ && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote - repo given on the command line' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + EOF + ( + cd thirdrepo && + __git_dir="$ROOT/.git" && + cur= && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote - full refs - repo given on the command line' ' + cat >expected <<-EOF && + refs/heads/branch-in-other + refs/heads/master-in-other + refs/tags/tag-in-other + EOF + ( + cd thirdrepo && + __git_dir="$ROOT/.git" && + cur=refs/ && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - configured remote - remote name matches a directory' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + EOF + mkdir other && + test_when_finished "rm -rf other" && + ( + cur= && + __git_refs other >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - URL remote' ' + cat >expected <<-EOF && + HEAD + branch-in-other + master-in-other + tag-in-other + EOF + ( + cur= && + __git_refs "file://$ROOT/otherrepo/.git" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - URL remote - full refs' ' + cat >expected <<-EOF && + refs/heads/branch-in-other + refs/heads/master-in-other + refs/tags/tag-in-other + EOF + ( + cur=refs/ && + __git_refs "file://$ROOT/otherrepo/.git" >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success '__git_refs - non-existing remote' ' + ( + cur= && + __git_refs non-existing >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - non-existing remote - full refs' ' + ( + cur=refs/ && + __git_refs non-existing >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - non-existing URL remote' ' + ( + cur= && + __git_refs "file://$ROOT/non-existing" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - non-existing URL remote - full refs' ' + ( + cur=refs/ && + __git_refs "file://$ROOT/non-existing" >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - not in a git repository' ' + ( + GIT_CEILING_DIRECTORIES="$ROOT" && + export GIT_CEILING_DIRECTORIES && + cd subdir && + cur= && + __git_refs >"$actual" + ) && + test_must_be_empty "$actual" +' + +test_expect_success '__git_refs - unique remote branches for git checkout DWIMery' ' + cat >expected <<-EOF && + HEAD + master + matching-branch + other/ambiguous + other/branch-in-other + other/master-in-other + remote/ambiguous + remote/branch-in-remote + matching-tag + branch-in-other + branch-in-remote + master-in-other + EOF + for remote_ref in refs/remotes/other/ambiguous \ + refs/remotes/remote/ambiguous \ + refs/remotes/remote/branch-in-remote + do + git update-ref $remote_ref master && + test_when_finished "git update-ref -d $remote_ref" + done && + ( + cur= && + __git_refs "" 1 >"$actual" + ) && + test_cmp expected "$actual" +' + +test_expect_success 'teardown after ref completion' ' + git branch -d matching-branch && + git tag -d matching-tag && + git remote remove other +' + test_expect_success '__git_get_config_variables' ' cat >expect <<-EOF && name-1 @@ -480,7 +891,12 @@ test_expect_success 'general options plus command' ' 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_completion "git --no-replace-objects check" "checkout " && + test_completion "git --git-dir some/path check" "checkout " && + test_completion "git -c conf.var=value check" "checkout " && + test_completion "git -C some/path check" "checkout " && + test_completion "git --work-tree some/path check" "checkout " && + test_completion "git --namespace name/space check" "checkout " ' test_expect_success 'git --help completion' ' @@ -488,10 +904,10 @@ test_expect_success 'git --help completion' ' test_completion "git --help core" "core-tutorial " ' -test_expect_success 'setup for ref completion' ' +test_expect_success 'setup for integration tests' ' echo content >file1 && echo more >file2 && - git add . && + git add file1 file2 && git commit -m one && git branch mybranch && git tag mytag @@ -505,6 +921,12 @@ test_expect_success 'checkout completes ref names' ' EOF ' +test_expect_success 'git -C <path> checkout uses the right repo' ' + test_completion "git -C subdir -C subsubdir -C .. -C ../otherrepo checkout b" <<-\EOF + branch-in-other Z + EOF +' + test_expect_success 'show completes all refs' ' test_completion "git show m" <<-\EOF master Z @@ -522,7 +944,7 @@ test_expect_success '<ref>: completes paths' ' test_expect_success 'complete tree filename with spaces' ' echo content >"name with spaces" && - git add . && + git add "name with spaces" && git commit -m spaces && test_completion "git show HEAD:nam" <<-\EOF name with spaces Z @@ -531,7 +953,7 @@ test_expect_success 'complete tree filename with spaces' ' test_expect_success 'complete tree filename with metacharacters' ' echo content >"name with \${meta}" && - git add . && + git add "name with \${meta}" && git commit -m meta && test_completion "git show HEAD:nam" <<-\EOF name with ${meta} Z diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh index 4f7eadb596..bd357704cc 100644 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@ -81,6 +81,10 @@ test_decode_color () { ' } +lf_to_nul () { + perl -pe 'y/\012/\000/' +} + nul_to_q () { perl -pe 'y/\000/Q/' } @@ -153,16 +157,21 @@ debug () { GIT_TEST_GDB=1 "$@" } -# Call test_commit with the arguments "<message> [<file> [<contents> [<tag>]]]" +# Call test_commit with the arguments +# [-C <directory>] <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>. +# +# If the first argument is "-C", the second argument is used as a path for +# the git invocations. test_commit () { notick= && signoff= && + indir= && while test $# != 0 do case "$1" in @@ -172,21 +181,26 @@ test_commit () { --signoff) signoff="$1" ;; + -C) + indir="$2" + shift + ;; *) break ;; esac shift done && + indir=${indir:+"$indir"/} && file=${2:-"$1.t"} && - echo "${3-$1}" > "$file" && - git add "$file" && + echo "${3-$1}" > "$indir$file" && + git ${indir:+ -C "$indir"} add "$file" && if test -z "$notick" then test_tick fi && - git commit $signoff -m "$1" && - git tag "${4:-$1}" + git ${indir:+ -C "$indir"} commit $signoff -m "$1" && + git ${indir:+ -C "$indir"} tag "${4:-$1}" } # Call test_merge with the arguments "<message> <commit>", where <commit> @@ -990,3 +1004,17 @@ test_copy_bytes () { } ' - "$1" } + +# run "$@" inside a non-git directory +nongit () { + test -d non-repo || + mkdir non-repo || + return 1 + + ( + GIT_CEILING_DIRECTORIES=$(pwd) && + export GIT_CEILING_DIRECTORIES && + cd non-repo && + "$@" + ) +} diff --git a/t/test-lib.sh b/t/test-lib.sh index b021329e03..86d77c16dd 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -966,7 +966,8 @@ yes () { } # Fix some commands on Windows -case $(uname -s) in +uname_s=$(uname -s) +case $uname_s in *MINGW*) # Windows has its own (incompatible) sort and find sort () { @@ -1106,6 +1107,10 @@ test_lazy_prereq NOT_ROOT ' test "$uid" != 0 ' +test_lazy_prereq JGIT ' + type jgit +' + # SANITY is about "can you correctly predict what the filesystem would # do by only looking at the permission bits of the files and # directories?" A typical example of !SANITY is running the test @@ -1137,6 +1142,7 @@ test_lazy_prereq SANITY ' return $status ' +test FreeBSD != $uname_s || GIT_UNZIP=${GIT_UNZIP:-/usr/local/bin/unzip} GIT_UNZIP=${GIT_UNZIP:-unzip} test_lazy_prereq UNZIP ' "$GIT_UNZIP" -v |