summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/Makefile5
-rwxr-xr-xt/lib-submodule-update.sh680
-rwxr-xr-xt/t0011-hashmap.sh13
-rwxr-xr-xt/t0025-crlf-auto.sh122
-rwxr-xr-xt/t0027-auto-crlf.sh265
-rwxr-xr-xt/t1013-read-tree-submodule.sh12
-rwxr-xr-xt/t1700-split-index.sh194
-rwxr-xr-xt/t2013-checkout-submodule.sh5
-rwxr-xr-xt/t2104-update-index-skip-worktree.sh2
-rwxr-xr-xt/t3426-rebase-submodule.sh46
-rwxr-xr-xt/t3512-cherry-pick-submodule.sh13
-rwxr-xr-xt/t3513-revert-submodule.sh32
-rwxr-xr-xt/t3906-stash-submodule.sh24
-rwxr-xr-xt/t4137-apply-submodule.sh20
-rwxr-xr-xt/t4202-log.sh31
-rwxr-xr-xt/t4255-am-submodule.sh21
-rwxr-xr-xt/t5539-fetch-http-shallow.sh1
-rwxr-xr-xt/t5572-pull-submodule.sh45
-rwxr-xr-xt/t6023-merge-file.sh91
-rwxr-xr-xt/t6041-bisect-submodule.sh32
-rwxr-xr-xt/t6050-replace.sh139
-rwxr-xr-xt/t7003-filter-branch.sh11
-rwxr-xr-xt/t7004-tag.sh76
-rwxr-xr-xt/t7112-reset-submodule.sh14
-rwxr-xr-xt/t7613-merge-submodule.sh19
-rwxr-xr-xt/t7702-repack-cyclic-alternate.sh24
-rw-r--r--t/test-lib-functions.sh11
-rw-r--r--t/test-lib.sh4
28 files changed, 1841 insertions, 111 deletions
diff --git a/t/Makefile b/t/Makefile
index 8fd1a72357..43b15e36ae 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -13,7 +13,7 @@ TAR ?= $(TAR)
RM ?= rm -f
PROVE ?= prove
DEFAULT_TEST_TARGET ?= test
-TEST_LINT ?= test-lint-duplicates test-lint-executable
+TEST_LINT ?= test-lint
ifdef TEST_OUTPUT_DIRECTORY
TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
@@ -29,6 +29,7 @@ TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh))
TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
+THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
all: $(DEFAULT_TEST_TARGET)
@@ -65,7 +66,7 @@ test-lint-executable:
echo >&2 "non-executable tests:" $$bad; exit 1; }
test-lint-shell-syntax:
- @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T)
+ @'$(PERL_PATH_SQ)' check-non-portable-shell.pl $(T) $(THELPERS)
aggregate-results-and-cleanup: $(T)
$(MAKE) aggregate-results
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
new file mode 100755
index 0000000000..79cdd34a54
--- /dev/null
+++ b/t/lib-submodule-update.sh
@@ -0,0 +1,680 @@
+# Create a submodule layout used for all tests below.
+#
+# The following use cases are covered:
+# - New submodule (no_submodule => add_sub1)
+# - Removed submodule (add_sub1 => remove_sub1)
+# - Updated submodule (add_sub1 => modify_sub1)
+# - Submodule updated to invalid commit (add_sub1 => invalid_sub1)
+# - Submodule updated from invalid commit (invalid_sub1 => valid_sub1)
+# - Submodule replaced by tracked files in directory (add_sub1 =>
+# replace_sub1_with_directory)
+# - Directory containing tracked files replaced by submodule
+# (replace_sub1_with_directory => replace_directory_with_sub1)
+# - Submodule replaced by tracked file with the same name (add_sub1 =>
+# replace_sub1_with_file)
+# - Tracked file replaced by submodule (replace_sub1_with_file =>
+# replace_file_with_sub1)
+#
+# --O-----O
+# / ^ replace_directory_with_sub1
+# / replace_sub1_with_directory
+# /----O
+# / ^
+# / modify_sub1
+# O------O-------O
+# ^ ^\ ^
+# | | \ remove_sub1
+# | | -----O-----O
+# | | \ ^ replace_file_with_sub1
+# | | \ replace_sub1_with_file
+# | add_sub1 --O-----O
+# no_submodule ^ valid_sub1
+# invalid_sub1
+#
+create_lib_submodule_repo () {
+ git init submodule_update_repo &&
+ (
+ cd submodule_update_repo &&
+ echo "expect" >>.gitignore &&
+ echo "actual" >>.gitignore &&
+ echo "x" >file1 &&
+ echo "y" >file2 &&
+ git add .gitignore file1 file2 &&
+ git commit -m "Base" &&
+ git branch "no_submodule" &&
+
+ git checkout -b "add_sub1" &&
+ git submodule add ./. sub1 &&
+ git config -f .gitmodules submodule.sub1.ignore all &&
+ git config submodule.sub1.ignore all &&
+ git add .gitmodules &&
+ git commit -m "Add sub1" &&
+ git checkout -b remove_sub1 &&
+ git revert HEAD &&
+
+ git checkout -b "modify_sub1" "add_sub1" &&
+ git submodule update &&
+ (
+ cd sub1 &&
+ git fetch &&
+ git checkout -b "modifications" &&
+ echo "z" >file2 &&
+ echo "x" >file3 &&
+ git add file2 file3 &&
+ git commit -m "modified file2 and added file3" &&
+ git push origin modifications
+ ) &&
+ git add sub1 &&
+ git commit -m "Modify sub1" &&
+
+ git checkout -b "replace_sub1_with_directory" "add_sub1" &&
+ git submodule update &&
+ (
+ cd sub1 &&
+ git checkout modifications
+ ) &&
+ git rm --cached sub1 &&
+ rm sub1/.git* &&
+ git config -f .gitmodules --remove-section "submodule.sub1" &&
+ git add .gitmodules sub1/* &&
+ git commit -m "Replace sub1 with directory" &&
+ git checkout -b replace_directory_with_sub1 &&
+ git revert HEAD &&
+
+ git checkout -b "replace_sub1_with_file" "add_sub1" &&
+ git rm sub1 &&
+ echo "content" >sub1 &&
+ git add sub1 &&
+ git commit -m "Replace sub1 with file" &&
+ git checkout -b replace_file_with_sub1 &&
+ git revert HEAD &&
+
+ git checkout -b "invalid_sub1" "add_sub1" &&
+ git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 sub1 &&
+ git commit -m "Invalid sub1 commit" &&
+ git checkout -b valid_sub1 &&
+ git revert HEAD &&
+ git checkout master
+ )
+}
+
+# Helper function to replace gitfile with .git directory
+replace_gitfile_with_git_dir () {
+ (
+ cd "$1" &&
+ git_dir="$(git rev-parse --git-dir)" &&
+ rm -f .git &&
+ cp -R "$git_dir" .git &&
+ GIT_WORK_TREE=. git config --unset core.worktree
+ )
+}
+
+# Test that the .git directory in the submodule is unchanged (except for the
+# core.worktree setting, which appears only in $GIT_DIR/modules/$1/config).
+# Call this function before test_submodule_content as the latter might
+# write the index file leading to false positive index differences.
+#
+# Note that this only supports submodules at the root level of the
+# superproject, with the default name, i.e. same as its path.
+test_git_directory_is_unchanged () {
+ (
+ cd ".git/modules/$1" &&
+ # does core.worktree point at the right place?
+ test "$(git config core.worktree)" = "../../../$1" &&
+ # remove it temporarily before comparing, as
+ # "$1/.git/config" lacks it...
+ git config --unset core.worktree
+ ) &&
+ diff -r ".git/modules/$1" "$1/.git" &&
+ (
+ # ... and then restore.
+ cd ".git/modules/$1" &&
+ git config core.worktree "../../../$1"
+ )
+}
+
+# Helper function to be executed at the start of every test below, it sets up
+# the submodule repo if it doesn't exist and configures the most problematic
+# settings for diff.ignoreSubmodules.
+prolog () {
+ (test -d submodule_update_repo || create_lib_submodule_repo) &&
+ test_config_global diff.ignoreSubmodules all &&
+ test_config diff.ignoreSubmodules all
+}
+
+# Helper function to bring work tree back into the state given by the
+# commit. This includes trying to populate sub1 accordingly if it exists and
+# should be updated to an existing commit.
+reset_work_tree_to () {
+ rm -rf submodule_update &&
+ git clone submodule_update_repo submodule_update &&
+ (
+ cd submodule_update &&
+ rm -rf sub1 &&
+ git checkout -f "$1" &&
+ git status -u -s >actual &&
+ test_must_be_empty actual &&
+ sha1=$(git rev-parse --revs-only HEAD:sub1) &&
+ if test -n "$sha1" &&
+ test $(cd "sub1" && git rev-parse --verify "$sha1^{commit}")
+ then
+ git submodule update --init --recursive "sub1"
+ fi
+ )
+}
+
+# Test that the superproject contains the content according to commit "$1"
+# (the work tree must match the index for everything but submodules but the
+# index must exactly match the given commit including any submodule SHA-1s).
+test_superproject_content () {
+ git diff-index --cached "$1" >actual &&
+ test_must_be_empty actual &&
+ git diff-files --ignore-submodules >actual &&
+ test_must_be_empty actual
+}
+
+# Test that the given submodule at path "$1" contains the content according
+# to the submodule commit recorded in the superproject's commit "$2"
+test_submodule_content () {
+ if test $# != 2
+ then
+ echo "test_submodule_content needs two arguments"
+ return 1
+ fi &&
+ submodule="$1" &&
+ commit="$2" &&
+ test -d "$submodule"/ &&
+ if ! test -f "$submodule"/.git && ! test -d "$submodule"/.git
+ then
+ echo "Submodule $submodule is not populated"
+ return 1
+ fi &&
+ sha1=$(git rev-parse --verify "$commit:$submodule") &&
+ if test -z "$sha1"
+ then
+ echo "Couldn't retrieve SHA-1 of $submodule for $commit"
+ return 1
+ fi &&
+ (
+ cd "$submodule" &&
+ git status -u -s >actual &&
+ test_must_be_empty actual &&
+ git diff "$sha1" >actual &&
+ test_must_be_empty actual
+ )
+}
+
+# Test that the following transitions are correctly handled:
+# - Updated submodule
+# - New submodule
+# - Removed submodule
+# - Directory containing tracked files replaced by submodule
+# - Submodule replaced by tracked files in directory
+# - Submodule replaced by tracked file with the same name
+# - tracked file replaced by submodule
+#
+# The default is that submodule contents aren't changed until "git submodule
+# update" is run. And even then that command doesn't delete the work tree of
+# a removed submodule.
+#
+# Removing a submodule containing a .git directory must fail even when forced
+# to protect the history!
+#
+
+# Test that submodule contents are currently not updated when switching
+# between commits that change a submodule.
+test_submodule_switch () {
+ command="$1"
+ ######################### Appearing submodule #########################
+ # Switching to a commit letting a submodule appear creates empty dir ...
+ if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
+ then
+ # Restoring stash fails to restore submodule index entry
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: added submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... and doesn't care if it already exists ...
+ test_expect_$RESULT "$command: added submodule leaves existing empty directory alone" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ mkdir sub1 &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... unless there is an untracked file in its place.
+ test_expect_success "$command: added submodule doesn't remove untracked unignored file with same name" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ >sub1 &&
+ test_must_fail $command add_sub1 &&
+ test_superproject_content origin/no_submodule &&
+ test_must_be_empty sub1
+ )
+ '
+ # Replacing a tracked file with a submodule produces an empty
+ # directory ...
+ test_expect_$RESULT "$command: replace tracked file with submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_file &&
+ (
+ cd submodule_update &&
+ git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+ $command replace_file_with_sub1 &&
+ test_superproject_content origin/replace_file_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_file_with_sub1
+ )
+ '
+ # ... as does removing a directory with tracked files with a
+ # submodule.
+ if test "$KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR" = 1
+ then
+ # Non fast-forward merges fail with "Directory sub1 doesn't
+ # exist. sub1" because the empty submodule directory is not
+ # created
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: replace directory with submodule" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_directory &&
+ (
+ cd submodule_update &&
+ git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+ $command replace_directory_with_sub1 &&
+ test_superproject_content origin/replace_directory_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_directory_with_sub1
+ )
+ '
+
+ ######################## Disappearing submodule #######################
+ # Removing a submodule doesn't remove its work tree ...
+ if test "$KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES" = 1
+ then
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: removed submodule leaves submodule directory and its contents in place" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_$RESULT "$command: removed submodule leaves submodule containing a .git directory alone" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ replace_gitfile_with_git_dir sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing a submodule with files in a directory must fail as the
+ # submodule work tree isn't removed ...
+ if test "$KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES" = 1
+ then
+ # Non fast-forward merges attempt to merge the former
+ # submodule files with the newly checked out ones in the
+ # directory of the same name while it shouldn't.
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: replace submodule with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_$RESULT "$command: replace submodule containing a .git directory with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing it with a file must fail as it could throw away any local
+ # work tree changes ...
+ test_expect_failure "$command: replace submodule with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... or even destroy unpushed parts of submodule history if that
+ # still uses a .git directory.
+ test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+
+ ########################## Modified submodule #########################
+ # Updating a submodule sha1 doesn't update the submodule's work tree
+ if test "$KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT" = 1
+ then
+ # When cherry picking a SHA-1 update for an ignored submodule
+ # the commit incorrectly fails with "The previous cherry-pick
+ # is now empty, possibly due to conflict resolution."
+ RESULT="failure"
+ else
+ RESULT="success"
+ fi
+ test_expect_$RESULT "$command: modified submodule does not update submodule work tree" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t modify_sub1 origin/modify_sub1 &&
+ $command modify_sub1 &&
+ test_superproject_content origin/modify_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ git submodule update &&
+ test_submodule_content sub1 origin/modify_sub1
+ )
+ '
+
+ # Updating a submodule to an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will fail
+ test_expect_$RESULT "$command: modified submodule does not update submodule work tree to invalid commit" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t invalid_sub1 origin/invalid_sub1 &&
+ $command invalid_sub1 &&
+ test_superproject_content origin/invalid_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ test_must_fail git submodule update &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Updating a submodule from an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will succeed
+ test_expect_$RESULT "$command: modified submodule does not update submodule work tree from invalid commit" '
+ prolog &&
+ reset_work_tree_to invalid_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t valid_sub1 origin/valid_sub1 &&
+ $command valid_sub1 &&
+ test_superproject_content origin/valid_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/valid_sub1
+ )
+ '
+}
+
+# Test that submodule contents are currently not updated when switching
+# between commits that change a submodule, but throwing away local changes in
+# the superproject is allowed.
+test_submodule_forced_switch () {
+ command="$1"
+ ######################### Appearing submodule #########################
+ # Switching to a commit letting a submodule appear creates empty dir ...
+ test_expect_success "$command: added submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... and doesn't care if it already exists ...
+ test_expect_success "$command: added submodule leaves existing empty directory alone" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ mkdir sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... unless there is an untracked file in its place.
+ test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
+ prolog &&
+ reset_work_tree_to no_submodule &&
+ (
+ cd submodule_update &&
+ git branch -t add_sub1 origin/add_sub1 &&
+ >sub1 &&
+ $command add_sub1 &&
+ test_superproject_content origin/add_sub1 &&
+ test_dir_is_empty sub1
+ )
+ '
+ # Replacing a tracked file with a submodule produces an empty
+ # directory ...
+ test_expect_success "$command: replace tracked file with submodule creates empty directory" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_file &&
+ (
+ cd submodule_update &&
+ git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+ $command replace_file_with_sub1 &&
+ test_superproject_content origin/replace_file_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_file_with_sub1
+ )
+ '
+ # ... as does removing a directory with tracked files with a
+ # submodule.
+ test_expect_success "$command: replace directory with submodule" '
+ prolog &&
+ reset_work_tree_to replace_sub1_with_directory &&
+ (
+ cd submodule_update &&
+ git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+ $command replace_directory_with_sub1 &&
+ test_superproject_content origin/replace_directory_with_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/replace_directory_with_sub1
+ )
+ '
+
+ ######################## Disappearing submodule #######################
+ # Removing a submodule doesn't remove its work tree ...
+ test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t remove_sub1 origin/remove_sub1 &&
+ replace_gitfile_with_git_dir sub1 &&
+ $command remove_sub1 &&
+ test_superproject_content origin/remove_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing a submodule with files in a directory must fail as the
+ # submodule work tree isn't removed ...
+ test_expect_failure "$command: replace submodule with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... especially when it contains a .git directory.
+ test_expect_failure "$command: replace submodule containing a .git directory with a directory must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_directory &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Replacing it with a file must fail as it could throw away any local
+ # work tree changes ...
+ test_expect_failure "$command: replace submodule with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # ... or even destroy unpushed parts of submodule history if that
+ # still uses a .git directory.
+ test_expect_failure "$command: replace submodule containing a .git directory with a file must fail" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+ replace_gitfile_with_git_dir sub1 &&
+ test_must_fail $command replace_sub1_with_file &&
+ test_superproject_content origin/add_sub1 &&
+ test_git_directory_is_unchanged sub1 &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+
+ ########################## Modified submodule #########################
+ # Updating a submodule sha1 doesn't update the submodule's work tree
+ test_expect_success "$command: modified submodule does not update submodule work tree" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t modify_sub1 origin/modify_sub1 &&
+ $command modify_sub1 &&
+ test_superproject_content origin/modify_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ git submodule update &&
+ test_submodule_content sub1 origin/modify_sub1
+ )
+ '
+ # Updating a submodule to an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will fail
+ test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
+ prolog &&
+ reset_work_tree_to add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t invalid_sub1 origin/invalid_sub1 &&
+ $command invalid_sub1 &&
+ test_superproject_content origin/invalid_sub1 &&
+ test_submodule_content sub1 origin/add_sub1 &&
+ test_must_fail git submodule update &&
+ test_submodule_content sub1 origin/add_sub1
+ )
+ '
+ # Updating a submodule from an invalid sha1 doesn't update the
+ # submodule's work tree, subsequent update will succeed
+ test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" '
+ prolog &&
+ reset_work_tree_to invalid_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t valid_sub1 origin/valid_sub1 &&
+ $command valid_sub1 &&
+ test_superproject_content origin/valid_sub1 &&
+ test_dir_is_empty sub1 &&
+ git submodule update --init --recursive &&
+ test_submodule_content sub1 origin/valid_sub1
+ )
+ '
+}
diff --git a/t/t0011-hashmap.sh b/t/t0011-hashmap.sh
index 391e2b6492..f97c80556f 100755
--- a/t/t0011-hashmap.sh
+++ b/t/t0011-hashmap.sh
@@ -237,4 +237,17 @@ test_expect_success 'grow / shrink' '
'
+test_expect_success 'string interning' '
+
+test_hashmap "intern value1
+intern Value1
+intern value2
+intern value2
+" "value1
+Value1
+value2
+value2"
+
+'
+
test_done
diff --git a/t/t0025-crlf-auto.sh b/t/t0025-crlf-auto.sh
index 28102de1a0..c164b4662a 100755
--- a/t/t0025-crlf-auto.sh
+++ b/t/t0025-crlf-auto.sh
@@ -12,144 +12,144 @@ test_expect_success setup '
git config core.autocrlf false &&
- for w in Hello world how are you; do echo $w; done >one &&
- for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >two &&
- for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >three &&
+ for w in Hello world how are you; do echo $w; done >LFonly &&
+ for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >CRLFonly &&
+ for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >LFwithNUL &&
git add . &&
git commit -m initial &&
- one=$(git rev-parse HEAD:one) &&
- two=$(git rev-parse HEAD:two) &&
- three=$(git rev-parse HEAD:three) &&
+ LFonly=$(git rev-parse HEAD:LFonly) &&
+ CRLFonly=$(git rev-parse HEAD:CRLFonly) &&
+ LFwithNUL=$(git rev-parse HEAD:LFwithNUL) &&
echo happy.
'
test_expect_success 'default settings cause no changes' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git read-tree --reset -u HEAD &&
- ! has_cr one &&
- has_cr two &&
- onediff=$(git diff one) &&
- twodiff=$(git diff two) &&
- threediff=$(git diff three) &&
- test -z "$onediff" && test -z "$twodiff" && test -z "$threediff"
+ ! has_cr LFonly &&
+ has_cr CRLFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
'
test_expect_success 'crlf=true causes a CRLF file to be normalized' '
# Backwards compatibility check
- rm -f .gitattributes tmp one two three &&
- echo "two crlf" > .gitattributes &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+ echo "CRLFonly crlf" > .gitattributes &&
git read-tree --reset -u HEAD &&
# Note, "normalized" means that git will normalize it if added
- has_cr two &&
- twodiff=$(git diff two) &&
- test -n "$twodiff"
+ has_cr CRLFonly &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ test -n "$CRLFonlydiff"
'
test_expect_success 'text=true causes a CRLF file to be normalized' '
- rm -f .gitattributes tmp one two three &&
- echo "two text" > .gitattributes &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+ echo "CRLFonly text" > .gitattributes &&
git read-tree --reset -u HEAD &&
# Note, "normalized" means that git will normalize it if added
- has_cr two &&
- twodiff=$(git diff two) &&
- test -n "$twodiff"
+ has_cr CRLFonly &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ test -n "$CRLFonlydiff"
'
test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf false &&
- echo "one eol=crlf" > .gitattributes &&
+ echo "LFonly eol=crlf" > .gitattributes &&
git read-tree --reset -u HEAD &&
- has_cr one &&
- onediff=$(git diff one) &&
- test -z "$onediff"
+ has_cr LFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ test -z "$LFonlydiff"
'
test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=input' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf input &&
- echo "one eol=crlf" > .gitattributes &&
+ echo "LFonly eol=crlf" > .gitattributes &&
git read-tree --reset -u HEAD &&
- has_cr one &&
- onediff=$(git diff one) &&
- test -z "$onediff"
+ has_cr LFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ test -z "$LFonlydiff"
'
test_expect_success 'eol=lf gives a normalized file LFs with autocrlf=true' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf true &&
- echo "one eol=lf" > .gitattributes &&
+ echo "LFonly eol=lf" > .gitattributes &&
git read-tree --reset -u HEAD &&
- ! has_cr one &&
- onediff=$(git diff one) &&
- test -z "$onediff"
+ ! has_cr LFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ test -z "$LFonlydiff"
'
test_expect_success 'autocrlf=true does not normalize CRLF files' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf true &&
git read-tree --reset -u HEAD &&
- has_cr one &&
- has_cr two &&
- onediff=$(git diff one) &&
- twodiff=$(git diff two) &&
- threediff=$(git diff three) &&
- test -z "$onediff" && test -z "$twodiff" && test -z "$threediff"
+ has_cr LFonly &&
+ has_cr CRLFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
'
test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf true &&
echo "* text=auto" > .gitattributes &&
git read-tree --reset -u HEAD &&
- has_cr one &&
- has_cr two &&
- onediff=$(git diff one) &&
- twodiff=$(git diff two) &&
- threediff=$(git diff three) &&
- test -z "$onediff" && test -n "$twodiff" && test -z "$threediff"
+ has_cr LFonly &&
+ has_cr CRLFonly &&
+ LFonlydiff=$(git diff LFonly) &&
+ CRLFonlydiff=$(git diff CRLFonly) &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFonlydiff" -a -n "$CRLFonlydiff" -a -z "$LFwithNULdiff"
'
test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
- rm -f .gitattributes tmp one two three &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
git config core.autocrlf true &&
echo "* text=auto" > .gitattributes &&
git read-tree --reset -u HEAD &&
- ! has_cr three &&
- threediff=$(git diff three) &&
- test -z "$threediff"
+ ! has_cr LFwithNUL &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFwithNULdiff"
'
test_expect_success 'eol=crlf _does_ normalize binary files' '
- rm -f .gitattributes tmp one two three &&
- echo "three eol=crlf" > .gitattributes &&
+ rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
+ echo "LFwithNUL eol=crlf" > .gitattributes &&
git read-tree --reset -u HEAD &&
- has_cr three &&
- threediff=$(git diff three) &&
- test -z "$threediff"
+ has_cr LFwithNUL &&
+ LFwithNULdiff=$(git diff LFwithNUL) &&
+ test -z "$LFwithNULdiff"
'
test_done
diff --git a/t/t0027-auto-crlf.sh b/t/t0027-auto-crlf.sh
new file mode 100755
index 0000000000..72dd3e8bb4
--- /dev/null
+++ b/t/t0027-auto-crlf.sh
@@ -0,0 +1,265 @@
+#!/bin/sh
+
+test_description='CRLF conversion all combinations'
+
+. ./test-lib.sh
+
+if ! test_have_prereq EXPENSIVE
+then
+ skip_all="EXPENSIVE not set"
+ test_done
+fi
+
+
+compare_files()
+{
+ od -c <"$1" >"$1".expect &&
+ od -c <"$2" >"$2".actual &&
+ test_cmp "$1".expect "$2".actual &&
+ rm "$1".expect "$2".actual
+}
+
+compare_ws_file()
+{
+ pfx=$1
+ exp=$2.expect
+ act=$pfx.actual.$3
+ od -c <"$2" >"$exp" &&
+ od -c <"$3" >"$act" &&
+ test_cmp $exp $act &&
+ rm $exp $act
+}
+
+create_gitattributes()
+{
+ txtbin=$1
+ case "$txtbin" in
+ auto)
+ echo "*.txt text=auto" >.gitattributes
+ ;;
+ text)
+ echo "*.txt text" >.gitattributes
+ ;;
+ -text)
+ echo "*.txt -text" >.gitattributes
+ ;;
+ *)
+ echo >.gitattributes
+ ;;
+ esac
+}
+
+create_file_in_repo()
+{
+ crlf=$1
+ txtbin=$2
+ create_gitattributes "$txtbin" &&
+ for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+ do
+ pfx=crlf_${crlf}_attr_${txtbin}_$f.txt &&
+ cp $f $pfx && git -c core.autocrlf=$crlf add $pfx
+ done &&
+ git commit -m "core.autocrlf $crlf"
+}
+
+check_files_in_repo()
+{
+ crlf=$1
+ txtbin=$2
+ lfname=$3
+ crlfname=$4
+ lfmixcrlf=$5
+ lfmixcr=$6
+ crlfnul=$7
+ pfx=crlf_${crlf}_attr_${txtbin}_ &&
+ compare_files $lfname ${pfx}LF.txt &&
+ compare_files $crlfname ${pfx}CRLF.txt &&
+ compare_files $lfmixcrlf ${pfx}CRLF_mix_LF.txt &&
+ compare_files $lfmixcr ${pfx}LF_mix_CR.txt &&
+ compare_files $crlfnul ${pfx}CRLF_nul.txt
+}
+
+
+check_files_in_ws()
+{
+ eol=$1
+ crlf=$2
+ txtbin=$3
+ lfname=$4
+ crlfname=$5
+ lfmixcrlf=$6
+ lfmixcr=$7
+ crlfnul=$8
+ create_gitattributes $txtbin &&
+ git config core.autocrlf $crlf &&
+ pfx=eol_${eol}_crlf_${crlf}_attr_${txtbin}_ &&
+ src=crlf_false_attr__ &&
+ for f in LF CRLF LF_mix_CR CRLF_mix_LF CRLF_nul
+ do
+ rm $src$f.txt &&
+ if test -z "$eol"; then
+ git checkout $src$f.txt
+ else
+ git -c core.eol=$eol checkout $src$f.txt
+ fi
+ done
+
+
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF" "
+ compare_ws_file $pfx $lfname ${src}LF.txt
+ "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF" "
+ compare_ws_file $pfx $crlfname ${src}CRLF.txt
+ "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_mix_LF" "
+ compare_ws_file $pfx $lfmixcrlf ${src}CRLF_mix_LF.txt
+ "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=LF_mix_CR" "
+ compare_ws_file $pfx $lfmixcr ${src}LF_mix_CR.txt
+ "
+ test_expect_success "checkout core.eol=$eol core.autocrlf=$crlf gitattributes=$txtbin file=CRLF_nul" "
+ compare_ws_file $pfx $crlfnul ${src}CRLF_nul.txt
+ "
+}
+
+#######
+(
+ type od >/dev/null &&
+ printf "line1Q\r\nline2\r\nline3" | q_to_nul >CRLF_nul &&
+ cat >expect <<-EOF &&
+ 0000000 l i n e 1 \0 \r \n l i n e 2 \r \n l
+ 0000020 i n e 3
+ 0000024
+EOF
+ od -c CRLF_nul | sed -e "s/[ ][ ]*/ /g" -e "s/ *$//" >actual
+ test_cmp expect actual &&
+ rm expect actual
+) || {
+ skip_all="od not found or od -c not usable"
+ exit 0
+ test_done
+}
+
+test_expect_success 'setup master' '
+ echo >.gitattributes &&
+ git checkout -b master &&
+ git add .gitattributes &&
+ git commit -m "add .gitattributes" "" &&
+ printf "line1\nline2\nline3" >LF &&
+ printf "line1\r\nline2\r\nline3" >CRLF &&
+ printf "line1\r\nline2\nline3" >CRLF_mix_LF &&
+ printf "line1\nline2\rline3" >LF_mix_CR &&
+ printf "line1\r\nline2\rline3" >CRLF_mix_CR &&
+ printf "line1Q\nline2\nline3" | q_to_nul >LF_nul
+'
+# CRLF_nul had been created above
+
+test_expect_success 'create files' '
+ create_file_in_repo false "" &&
+ create_file_in_repo true "" &&
+ create_file_in_repo input "" &&
+
+ create_file_in_repo false "auto" &&
+ create_file_in_repo true "auto" &&
+ create_file_in_repo input "auto" &&
+
+ create_file_in_repo false "text" &&
+ create_file_in_repo true "text" &&
+ create_file_in_repo input "text" &&
+
+ create_file_in_repo false "-text" &&
+ create_file_in_repo true "-text" &&
+ create_file_in_repo input "-text" &&
+ rm -f *.txt &&
+ git reset --hard
+'
+
+test_expect_success 'commit empty gitattribues' '
+ check_files_in_repo false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo true "" LF LF LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo input "" LF LF LF LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text=auto' '
+ check_files_in_repo false "auto" LF LF LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo true "auto" LF LF LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo input "auto" LF LF LF LF_mix_CR CRLF_nul
+'
+
+test_expect_success 'commit text' '
+ check_files_in_repo false "text" LF LF LF LF_mix_CR LF_nul &&
+ check_files_in_repo true "text" LF LF LF LF_mix_CR LF_nul &&
+ check_files_in_repo input "text" LF LF LF LF_mix_CR LF_nul
+'
+
+test_expect_success 'commit -text' '
+ check_files_in_repo false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul &&
+ check_files_in_repo input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+'
+
+################################################################################
+# Check how files in the repo are changed when they are checked out
+# How to read the table below:
+# - check_files_in_ws will check multiple files, see below
+# - parameter $1 : core.eol lf | crlf
+# - parameter $2 : core.autocrlf false | true | input
+# - parameter $3 : text in .gitattributs "" (empty) | auto | text | -text
+# - parameter $4 : reference for a file with only LF in the repo
+# - parameter $5 : reference for a file with only CRLF in the repo
+# - parameter $6 : reference for a file with mixed LF and CRLF in the repo
+# - parameter $7 : reference for a file with LF and CR in the repo (does somebody uses this ?)
+# - parameter $8 : reference for a file with CRLF and a NUL (should be handled as binary when auto)
+
+check_files_in_ws lf false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws lf false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws lf false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws lf input "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws lf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws lf input "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+###########
+#core.autocrlf=input is forbidden with core.eol=crlf
+check_files_in_ws crlf false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws crlf false "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+
+check_files_in_ws crlf false "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws crlf true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+
+check_files_in_ws crlf false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws crlf true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+if test_have_prereq MINGW
+then
+check_files_in_ws "" false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws "" false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws "" false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws "" true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+
+check_files_in_ws native false "" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "" CRLF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native false "auto" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "auto" CRLF CRLF CRLF LF_mix_CR CRLF_nul
+check_files_in_ws native false "text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "text" CRLF CRLF CRLF CRLF_mix_CR CRLF_nul
+check_files_in_ws native false "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+check_files_in_ws native true "-text" LF CRLF CRLF_mix_LF LF_mix_CR CRLF_nul
+fi
+
+test_done
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
new file mode 100755
index 0000000000..20526aed34
--- /dev/null
+++ b/t/t1013-read-tree-submodule.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='read-tree can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+test_submodule_switch "git read-tree -u -m"
+
+test_submodule_forced_switch "git read-tree -u --reset"
+
+test_done
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
new file mode 100755
index 0000000000..94fb473e7c
--- /dev/null
+++ b/t/t1700-split-index.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+test_description='split index mode tests'
+
+. ./test-lib.sh
+
+# We need total control of index splitting here
+sane_unset GIT_TEST_SPLIT_INDEX
+
+test_expect_success 'enable split index' '
+ git update-index --split-index &&
+ test-dump-split-index .git/index >actual &&
+ cat >expect <<EOF &&
+own 8299b0bcd1ac364e5f1d7768efb62fa2da79a339
+base 39d890139ee5356c7ef572216cebcd27aa41f9df
+replacements:
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add one file' '
+ : >one &&
+ git update-index --add one &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+base 39d890139ee5356c7ef572216cebcd27aa41f9df
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+replacements:
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'disable split index' '
+ git update-index --no-split-index &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ BASE=`test-dump-split-index .git/index | grep "^own" | sed "s/own/base/"` &&
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+not a split index
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'enable split index again, "one" now belongs to base index"' '
+ git update-index --split-index &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+$BASE
+replacements:
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'modify original file, base index untouched' '
+ echo modified >one &&
+ git update-index one &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+replacements: 0
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add another file, which stays index' '
+ : >two &&
+ git update-index --add two &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+replacements: 0
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'remove file not in base index' '
+ git update-index --force-remove two &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ q_to_tab >expect <<EOF &&
+$BASE
+100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+replacements: 0
+deletions:
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'remove file in base index' '
+ git update-index --force-remove one &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+$BASE
+replacements:
+deletions: 0
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add original file back' '
+ : >one &&
+ git update-index --add one &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+EOF
+ test_cmp ls-files.expect ls-files.actual &&
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+$BASE
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+replacements:
+deletions: 0
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'add new file' '
+ : >two &&
+ git update-index --add two &&
+ git ls-files --stage >actual &&
+ cat >expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'unify index, two files remain' '
+ git update-index --no-split-index &&
+ git ls-files --stage >ls-files.actual &&
+ cat >ls-files.expect <<EOF &&
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 one
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 two
+EOF
+ test_cmp ls-files.expect ls-files.actual
+
+ test-dump-split-index .git/index | sed "/^own/d" >actual &&
+ cat >expect <<EOF &&
+not a split index
+EOF
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index 06b18f8bc1..6847f75822 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -3,6 +3,7 @@
test_description='checkout can handle submodules'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
test_expect_success 'setup' '
mkdir submodule &&
@@ -62,4 +63,8 @@ test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/
! test -s actual
'
+test_submodule_switch "git checkout"
+
+test_submodule_forced_switch "git checkout -f"
+
test_done
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
index 29c1fb10ca..cc830da58d 100755
--- a/t/t2104-update-index-skip-worktree.sh
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -7,6 +7,8 @@ test_description='skip-worktree bit test'
. ./test-lib.sh
+sane_unset GIT_TEST_SPLIT_INDEX
+
test_set_index_version 3
cat >expect.full <<EOF
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
new file mode 100755
index 0000000000..d5b896d445
--- /dev/null
+++ b/t/t3426-rebase-submodule.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='rebase can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+git_rebase () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ git checkout -b ours HEAD &&
+ echo x >>file1 &&
+ git add file1 &&
+ git commit -m add_x &&
+ git revert HEAD &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git rebase "$1"
+}
+
+test_submodule_switch "git_rebase"
+
+git_rebase_interactive () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ git checkout -b ours HEAD &&
+ echo x >>file1 &&
+ git add file1 &&
+ git commit -m add_x &&
+ git revert HEAD &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ set_fake_editor &&
+ echo "fake-editor.sh" >.git/info/exclude &&
+ 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/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
new file mode 100755
index 0000000000..6863b7bb6f
--- /dev/null
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+test_description='cherry-pick can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git cherry-pick"
+
+test_done
diff --git a/t/t3513-revert-submodule.sh b/t/t3513-revert-submodule.sh
new file mode 100755
index 0000000000..a1c4e0216f
--- /dev/null
+++ b/t/t3513-revert-submodule.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='revert can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+# Create a revert that moves from HEAD (including any test modifications to
+# the work tree) to $1 by first checking out $1 and reverting it. Reverting
+# the revert is the transition we test for. We tar the current work tree
+# first so we can restore the work tree test setup after doing the checkout
+# and revert. We test here that the restored work tree content is identical
+# to that at the beginning. The last revert is then tested by the framework.
+git_revert () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+ git checkout "$1" &&
+ git revert HEAD &&
+ rm -rf * &&
+ tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git revert HEAD
+}
+
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+test_submodule_switch "git_revert"
+
+test_done
diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh
new file mode 100755
index 0000000000..d7219d6f8f
--- /dev/null
+++ b/t/t3906-stash-submodule.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='stash apply can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+git_stash () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ git read-tree -u -m "$1" &&
+ git stash &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git stash apply
+}
+
+KNOWN_FAILURE_STASH_DOES_IGNORE_SUBMODULE_CHANGES=1
+KNOWN_FAILURE_CHERRY_PICK_SEES_EMPTY_COMMIT=1
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+test_submodule_switch "git_stash"
+
+test_done
diff --git a/t/t4137-apply-submodule.sh b/t/t4137-apply-submodule.sh
new file mode 100755
index 0000000000..a9bd40a6d0
--- /dev/null
+++ b/t/t4137-apply-submodule.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='git apply handling submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+apply_index () {
+ git diff --ignore-submodules=dirty "..$1" | git apply --index -
+}
+
+test_submodule_switch "apply_index"
+
+apply_3way () {
+ git diff --ignore-submodules=dirty "..$1" | git apply --3way -
+}
+
+test_submodule_switch "apply_3way"
+
+test_done
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index cb03d28769..99ab7ca21f 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -3,6 +3,7 @@
test_description='git log'
. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
test_expect_success setup '
@@ -841,4 +842,34 @@ test_expect_success 'dotdot is a parent directory' '
test_cmp expect actual
'
+test_expect_success GPG 'log --graph --show-signature' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ git checkout -b signed master &&
+ echo foo >foo &&
+ git add foo &&
+ git commit -S -m signed_commit &&
+ git log --graph --show-signature -n1 signed >actual &&
+ grep "^| gpg: Signature made" actual &&
+ grep "^| gpg: Good signature" actual
+'
+
+test_expect_success GPG 'log --graph --show-signature for merged tag' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ git checkout -b plain master &&
+ echo aaa >bar &&
+ git add bar &&
+ git commit -m bar_commit &&
+ git checkout -b tagged master &&
+ echo bbb >baz &&
+ git add baz &&
+ git commit -m baz_commit &&
+ git tag -s -m signed_tag_msg signed_tag &&
+ git checkout plain &&
+ git merge --no-ff -m msg signed_tag &&
+ git log --graph --show-signature -n1 plain >actual &&
+ grep "^|\\\ merged tag" actual &&
+ grep "^| | gpg: Signature made" actual &&
+ grep "^| | gpg: Good signature" actual
+'
+
test_done
diff --git a/t/t4255-am-submodule.sh b/t/t4255-am-submodule.sh
new file mode 100755
index 0000000000..8bde7dbb6d
--- /dev/null
+++ b/t/t4255-am-submodule.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+test_description='git am handling submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+am () {
+ git format-patch --stdout --ignore-submodules=dirty "..$1" | git am -
+}
+
+test_submodule_switch "am"
+
+am_3way () {
+ git format-patch --stdout --ignore-submodules=dirty "..$1" | git am --3way -
+}
+
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "am_3way"
+
+test_done
diff --git a/t/t5539-fetch-http-shallow.sh b/t/t5539-fetch-http-shallow.sh
index 94553e1039..b46118846c 100755
--- a/t/t5539-fetch-http-shallow.sh
+++ b/t/t5539-fetch-http-shallow.sh
@@ -54,6 +54,7 @@ EOF
test_expect_success 'no shallow lines after receiving ACK ready' '
(
cd shallow &&
+ test_tick &&
for i in $(test_seq 15)
do
git checkout --orphan unrelated$i &&
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
new file mode 100755
index 0000000000..accfa5cc0c
--- /dev/null
+++ b/t/t5572-pull-submodule.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='pull can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+reset_branch_to_HEAD () {
+ git branch -D "$1" &&
+ git checkout -b "$1" HEAD &&
+ git branch --set-upstream-to="origin/$1" "$1"
+}
+
+git_pull () {
+ reset_branch_to_HEAD "$1" &&
+ git pull
+}
+
+# pulls without conflicts
+test_submodule_switch "git_pull"
+
+git_pull_ff () {
+ reset_branch_to_HEAD "$1" &&
+ git pull --ff
+}
+
+test_submodule_switch "git_pull_ff"
+
+git_pull_ff_only () {
+ reset_branch_to_HEAD "$1" &&
+ git pull --ff-only
+}
+
+test_submodule_switch "git_pull_ff_only"
+
+git_pull_noff () {
+ reset_branch_to_HEAD "$1" &&
+ git pull --no-ff
+}
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git_pull_noff"
+
+test_done
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index 432f086c06..3758961765 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -77,12 +77,29 @@ test_expect_success "merge without conflict (--quiet)" \
"git merge-file --quiet test.txt orig.txt new2.txt"
cp new1.txt test2.txt
-test_expect_success "merge without conflict (missing LF at EOF)" \
- "git merge-file test2.txt orig.txt new2.txt"
+test_expect_failure "merge without conflict (missing LF at EOF)" \
+ "git merge-file test2.txt orig.txt new4.txt"
-test_expect_success "merge result added missing LF" \
+test_expect_failure "merge result added missing LF" \
"test_cmp test.txt test2.txt"
+cp new4.txt test3.txt
+test_expect_success "merge without conflict (missing LF at EOF, away from change in the other file)" \
+ "git merge-file --quiet test3.txt new2.txt new3.txt"
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+EOF
+printf "propter nomen suum." >> expect.txt
+
+test_expect_success "merge does not add LF away of change" \
+ "test_cmp test3.txt expect.txt"
+
cp test.txt backup.txt
test_expect_success "merge with conflicts" \
"test_must_fail git merge-file test.txt orig.txt new3.txt"
@@ -107,6 +124,55 @@ EOF
test_expect_success "expected conflict markers" "test_cmp test.txt expect.txt"
cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --ours" \
+ "git merge-file --ours test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --theirs" \
+ "git merge-file --theirs test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
+cat > expect.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+test_expect_success "merge conflicting with --union" \
+ "git merge-file --union test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+cp backup.txt test.txt
+
test_expect_success "merge with conflicts, using -L" \
"test_must_fail git merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
@@ -260,4 +326,23 @@ test_expect_success 'marker size' '
test_cmp expect actual
'
+printf "line1\nline2\nline3" >nolf-orig.txt
+printf "line1\nline2\nline3x" >nolf-diff1.txt
+printf "line1\nline2\nline3y" >nolf-diff2.txt
+
+test_expect_success 'conflict at EOF without LF resolved by --ours' \
+ 'git merge-file -p --ours nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+ printf "line1\nline2\nline3x" >expect.txt &&
+ test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --theirs' \
+ 'git merge-file -p --theirs nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+ printf "line1\nline2\nline3y" >expect.txt &&
+ test_cmp expect.txt output.txt'
+
+test_expect_success 'conflict at EOF without LF resolved by --union' \
+ 'git merge-file -p --union nolf-diff1.txt nolf-orig.txt nolf-diff2.txt >output.txt &&
+ printf "line1\nline2\nline3x\nline3y" >expect.txt &&
+ test_cmp expect.txt output.txt'
+
test_done
diff --git a/t/t6041-bisect-submodule.sh b/t/t6041-bisect-submodule.sh
new file mode 100755
index 0000000000..c6b7aa6977
--- /dev/null
+++ b/t/t6041-bisect-submodule.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='bisect can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+git_bisect () {
+ git status -su >expect &&
+ ls -1pR * >>expect &&
+ tar czf "$TRASH_DIRECTORY/tmp.tgz" * &&
+ GOOD=$(git rev-parse --verify HEAD) &&
+ git checkout "$1" &&
+ echo "foo" >bar &&
+ git add bar &&
+ git commit -m "bisect bad" &&
+ BAD=$(git rev-parse --verify HEAD) &&
+ git reset --hard HEAD^^ &&
+ git submodule update &&
+ git bisect start &&
+ git bisect good $GOOD &&
+ rm -rf * &&
+ tar xzf "$TRASH_DIRECTORY/tmp.tgz" &&
+ git status -su >actual &&
+ ls -1pR * >>actual &&
+ test_cmp expect actual &&
+ git bisect bad $BAD
+}
+
+test_submodule_switch "git_bisect"
+
+test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index 68b3cb26d9..4d5a25eedf 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -7,8 +7,9 @@ test_description='Tests replace refs functionality'
exec </dev/null
. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
-add_and_commit_file()
+add_and_commit_file ()
{
_file="$1"
_msg="$2"
@@ -18,6 +19,38 @@ add_and_commit_file()
git commit --quiet -m "$_file: $_msg"
}
+commit_buffer_contains_parents ()
+{
+ git cat-file commit "$1" >payload &&
+ sed -n -e '/^$/q' -e '/^parent /p' <payload >actual &&
+ shift &&
+ for _parent
+ do
+ echo "parent $_parent"
+ done >expected &&
+ test_cmp expected actual
+}
+
+commit_peeling_shows_parents ()
+{
+ _parent_number=1
+ _commit="$1"
+ shift &&
+ for _parent
+ do
+ _found=$(git rev-parse --verify $_commit^$_parent_number) || return 1
+ test "$_found" = "$_parent" || return 1
+ _parent_number=$(( $_parent_number + 1 ))
+ done &&
+ test_must_fail git rev-parse --verify $_commit^$_parent_number
+}
+
+commit_has_parents ()
+{
+ commit_buffer_contains_parents "$@" &&
+ commit_peeling_shows_parents "$@"
+}
+
HASH1=
HASH2=
HASH3=
@@ -27,36 +60,36 @@ HASH6=
HASH7=
test_expect_success 'set up buggy branch' '
- echo "line 1" >> hello &&
- echo "line 2" >> hello &&
- echo "line 3" >> hello &&
- echo "line 4" >> hello &&
+ echo "line 1" >>hello &&
+ echo "line 2" >>hello &&
+ echo "line 3" >>hello &&
+ echo "line 4" >>hello &&
add_and_commit_file hello "4 lines" &&
HASH1=$(git rev-parse --verify HEAD) &&
- echo "line BUG" >> hello &&
- echo "line 6" >> hello &&
- echo "line 7" >> hello &&
- echo "line 8" >> hello &&
+ echo "line BUG" >>hello &&
+ echo "line 6" >>hello &&
+ echo "line 7" >>hello &&
+ echo "line 8" >>hello &&
add_and_commit_file hello "4 more lines with a BUG" &&
HASH2=$(git rev-parse --verify HEAD) &&
- echo "line 9" >> hello &&
- echo "line 10" >> hello &&
+ echo "line 9" >>hello &&
+ echo "line 10" >>hello &&
add_and_commit_file hello "2 more lines" &&
HASH3=$(git rev-parse --verify HEAD) &&
- echo "line 11" >> hello &&
+ echo "line 11" >>hello &&
add_and_commit_file hello "1 more line" &&
HASH4=$(git rev-parse --verify HEAD) &&
- sed -e "s/BUG/5/" hello > hello.new &&
+ sed -e "s/BUG/5/" hello >hello.new &&
mv hello.new hello &&
add_and_commit_file hello "BUG fixed" &&
HASH5=$(git rev-parse --verify HEAD) &&
- echo "line 12" >> hello &&
- echo "line 13" >> hello &&
+ echo "line 12" >>hello &&
+ echo "line 13" >>hello &&
add_and_commit_file hello "2 more lines" &&
HASH6=$(git rev-parse --verify HEAD) &&
- echo "line 14" >> hello &&
- echo "line 15" >> hello &&
- echo "line 16" >> hello &&
+ echo "line 14" >>hello &&
+ echo "line 15" >>hello &&
+ echo "line 16" >>hello &&
add_and_commit_file hello "again 3 more lines" &&
HASH7=$(git rev-parse --verify HEAD)
'
@@ -95,7 +128,7 @@ test_expect_success 'tag replaced commit' '
'
test_expect_success '"git fsck" works' '
- git fsck master > fsck_master.out &&
+ git fsck master >fsck_master.out &&
grep "dangling commit $R" fsck_master.out &&
grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
test -z "$(git fsck)"
@@ -217,14 +250,14 @@ test_expect_success 'fetch branch with replacement' '
(
cd clone_dir &&
git fetch origin refs/heads/tofetch:refs/heads/parallel3 &&
- git log --pretty=oneline parallel3 > output.txt &&
+ git log --pretty=oneline parallel3 >output.txt &&
! grep $PARA3 output.txt &&
- git show $PARA3 > para3.txt &&
+ git show $PARA3 >para3.txt &&
grep "A U Thor" para3.txt &&
git fetch origin "refs/replace/*:refs/replace/*" &&
- git log --pretty=oneline parallel3 > output.txt &&
+ git log --pretty=oneline parallel3 >output.txt &&
grep $PARA3 output.txt &&
- git show $PARA3 > para3.txt &&
+ git show $PARA3 >para3.txt &&
grep "O Thor" para3.txt
)
'
@@ -302,7 +335,7 @@ test_expect_success 'test --format medium' '
echo "$PARA3 -> $S" &&
echo "$MYTAG -> $HASH1"
} | sort >expected &&
- git replace -l --format medium | sort > actual &&
+ git replace -l --format medium | sort >actual &&
test_cmp expected actual
'
@@ -314,7 +347,7 @@ test_expect_success 'test --format long' '
echo "$PARA3 (commit) -> $S (commit)" &&
echo "$MYTAG (tag) -> $HASH1 (commit)"
} | sort >expected &&
- git replace --format=long | sort > actual &&
+ git replace --format=long | sort >actual &&
test_cmp expected actual
'
@@ -351,4 +384,60 @@ test_expect_success 'replace ref cleanup' '
test -z "$(git replace)"
'
+test_expect_success '--graft with and without already replaced object' '
+ test $(git log --oneline | wc -l) = 7 &&
+ git replace --graft $HASH5 &&
+ test $(git log --oneline | wc -l) = 3 &&
+ commit_has_parents $HASH5 &&
+ test_must_fail git replace --graft $HASH5 $HASH4 $HASH3 &&
+ git replace --force -g $HASH5 $HASH4 $HASH3 &&
+ commit_has_parents $HASH5 $HASH4 $HASH3 &&
+ git replace -d $HASH5
+'
+
+test_expect_success GPG 'set up a signed commit' '
+ echo "line 17" >>hello &&
+ echo "line 18" >>hello &&
+ git add hello &&
+ test_tick &&
+ git commit --quiet -S -m "hello: 2 more lines in a signed commit" &&
+ HASH8=$(git rev-parse --verify HEAD) &&
+ git verify-commit $HASH8
+'
+
+test_expect_success GPG '--graft with a signed commit' '
+ git cat-file commit $HASH8 >orig &&
+ git replace --graft $HASH8 &&
+ git cat-file commit $HASH8 >repl &&
+ commit_has_parents $HASH8 &&
+ test_must_fail git verify-commit $HASH8 &&
+ sed -n -e "/^tree /p" -e "/^author /p" -e "/^committer /p" orig >expected &&
+ echo >>expected &&
+ sed -e "/^$/q" repl >actual &&
+ test_cmp expected actual &&
+ git replace -d $HASH8
+'
+
+test_expect_success GPG 'set up a merge commit with a mergetag' '
+ git reset --hard HEAD &&
+ git checkout -b test_branch HEAD~2 &&
+ echo "line 1 from test branch" >>hello &&
+ echo "line 2 from test branch" >>hello &&
+ git add hello &&
+ test_tick &&
+ git commit -m "hello: 2 more lines from a test branch" &&
+ HASH9=$(git rev-parse --verify HEAD) &&
+ git tag -s -m "tag for testing with a mergetag" test_tag HEAD &&
+ git checkout master &&
+ git merge -s ours test_tag &&
+ HASH10=$(git rev-parse --verify HEAD) &&
+ git cat-file commit $HASH10 | grep "^mergetag object"
+'
+
+test_expect_success GPG '--graft on a commit with a mergetag' '
+ test_must_fail git replace --graft $HASH10 $HASH8^1 &&
+ git replace --graft $HASH10 $HASH8^1 $HASH9 &&
+ git replace -d $HASH10
+'
+
test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index 9496736a89..66643e4bd7 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -308,6 +308,17 @@ test_expect_success 'Prune empty commits' '
test_cmp expect actual
'
+test_expect_success 'prune empty collapsed merges' '
+ test_config merge.ff false &&
+ git rev-list HEAD >expect &&
+ test_commit to_remove_2 &&
+ git reset --hard HEAD^ &&
+ test_merge non-ff to_remove_2 &&
+ git filter-branch -f --index-filter "git update-index --remove to_remove_2.t" --prune-empty HEAD &&
+ git rev-list HEAD >actual &&
+ test_cmp expect actual
+'
+
test_expect_success '--remap-to-ancestor with filename filters' '
git checkout master &&
git reset --hard A &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index e4ab0f5b64..0366653088 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1385,41 +1385,77 @@ test_expect_success 'lexical sort' '
git tag foo1.6 &&
git tag foo1.10 &&
git tag -l --sort=refname "foo*" >actual &&
- cat >expect <<EOF &&
-foo1.10
-foo1.3
-foo1.6
-EOF
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.3
+ foo1.6
+ EOF
test_cmp expect actual
'
test_expect_success 'version sort' '
git tag -l --sort=version:refname "foo*" >actual &&
- cat >expect <<EOF &&
-foo1.3
-foo1.6
-foo1.10
-EOF
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
test_cmp expect actual
'
test_expect_success 'reverse version sort' '
git tag -l --sort=-version:refname "foo*" >actual &&
- cat >expect <<EOF &&
-foo1.10
-foo1.6
-foo1.3
-EOF
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.6
+ foo1.3
+ EOF
test_cmp expect actual
'
test_expect_success 'reverse lexical sort' '
git tag -l --sort=-refname "foo*" >actual &&
- cat >expect <<EOF &&
-foo1.6
-foo1.3
-foo1.10
-EOF
+ cat >expect <<-\EOF &&
+ foo1.6
+ foo1.3
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'configured lexical sort' '
+ git config tag.sort "v:refname" &&
+ git tag -l "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.3
+ foo1.6
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'option override configured sort' '
+ git tag -l --sort=-refname "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.6
+ foo1.3
+ foo1.10
+ EOF
+ test_cmp expect actual
+'
+
+test_expect_success 'invalid sort parameter on command line' '
+ test_must_fail git tag -l --sort=notvalid "foo*" >actual
+'
+
+test_expect_success 'invalid sort parameter in configuratoin' '
+ git config tag.sort "v:notvalid" &&
+ git tag -l "foo*" >actual &&
+ cat >expect <<-\EOF &&
+ foo1.10
+ foo1.3
+ foo1.6
+ EOF
test_cmp expect actual
'
diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh
new file mode 100755
index 0000000000..2eda6adeb1
--- /dev/null
+++ b/t/t7112-reset-submodule.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+test_description='reset can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+test_submodule_switch "git reset --keep"
+
+test_submodule_switch "git reset --merge"
+
+test_submodule_forced_switch "git reset --hard"
+
+test_done
diff --git a/t/t7613-merge-submodule.sh b/t/t7613-merge-submodule.sh
new file mode 100755
index 0000000000..d1e9fcc781
--- /dev/null
+++ b/t/t7613-merge-submodule.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='merge can handle submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-submodule-update.sh
+
+# merges without conflicts
+test_submodule_switch "git merge"
+
+test_submodule_switch "git merge --ff"
+
+test_submodule_switch "git merge --ff-only"
+
+KNOWN_FAILURE_NOFF_MERGE_DOESNT_CREATE_EMPTY_SUBMODULE_DIR=1
+KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
+test_submodule_switch "git merge --no-ff"
+
+test_done
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
new file mode 100755
index 0000000000..93b74867ac
--- /dev/null
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Ephrim Khong
+#
+
+test_description='repack involving cyclic alternate'
+. ./test-lib.sh
+
+test_expect_success setup '
+ GIT_OBJECT_DIRECTORY=.git//../.git/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ touch a &&
+ git add a &&
+ git commit -m 1 &&
+ git repack -adl &&
+ echo "$(pwd)"/.git/objects/../objects >.git/objects/info/alternates
+'
+
+test_expect_success 're-packing repository with itsself as alternate' '
+ git repack -adl &&
+ git fsck
+'
+
+test_done
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 0377d3e296..dafd6ad21a 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -489,6 +489,17 @@ test_path_is_dir () {
fi
}
+# Check if the directory exists and is empty as expected, barf otherwise.
+test_dir_is_empty () {
+ test_path_is_dir "$1" &&
+ if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
+ then
+ echo "Directory '$1' is not empty, it contains:"
+ ls -la "$1"
+ return 1
+ fi
+}
+
test_path_is_missing () {
if [ -e "$1" ]
then
diff --git a/t/test-lib.sh b/t/test-lib.sh
index a4795373a6..b1bc65bfb5 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -109,6 +109,10 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
export EDITOR
+# Tests using GIT_TRACE typically don't want <timestamp> <file>:<line> output
+GIT_TRACE_BARE=1
+export GIT_TRACE_BARE
+
if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
then
GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"