summaryrefslogtreecommitdiff
path: root/t
diff options
context:
space:
mode:
Diffstat (limited to 't')
-rw-r--r--t/.gitattributes1
-rw-r--r--t/.gitignore2
-rw-r--r--t/Makefile51
-rw-r--r--t/README510
-rwxr-xr-xt/aggregate-results.sh34
-rw-r--r--t/annotate-tests.sh126
-rw-r--r--t/diff-lib.sh39
-rw-r--r--t/gitweb-lib.sh88
-rw-r--r--t/lib-cvs.sh75
-rw-r--r--t/lib-git-svn.sh172
-rw-r--r--t/lib-httpd.sh161
-rw-r--r--t/lib-httpd/apache.conf68
-rw-r--r--t/lib-httpd/ssl.cnf8
-rw-r--r--t/lib-pager.sh15
-rw-r--r--t/lib-patch-mode.sh43
-rw-r--r--t/lib-read-tree-m-3way.sh158
-rw-r--r--t/lib-rebase.sh65
-rw-r--r--t/lib-t6000.sh127
-rwxr-xr-xt/t0000-basic.sh389
-rwxr-xr-xt/t0001-init.sh327
-rwxr-xr-xt/t0002-gitfile.sh103
-rwxr-xr-xt/t0003-attributes.sh124
-rwxr-xr-xt/t0004-unwritable.sh68
-rwxr-xr-xt/t0005-signals.sh23
-rwxr-xr-xt/t0006-date.sh78
-rwxr-xr-xt/t0010-racy-git.sh33
-rwxr-xr-xt/t0020-crlf.sh509
-rwxr-xr-xt/t0021-conversion.sh96
-rwxr-xr-xt/t0022-crlf-rename.sh33
-rwxr-xr-xt/t0023-crlf-am.sh44
-rwxr-xr-xt/t0024-crlf-archive.sh46
-rwxr-xr-xt/t0025-crlf-auto.sh155
-rwxr-xr-xt/t0026-eol-config.sh83
-rwxr-xr-xt/t0030-stripspace.sh400
-rwxr-xr-xt/t0040-parse-options.sh340
-rwxr-xr-xt/t0050-filesystem.sh155
-rwxr-xr-xt/t0055-beyond-symlinks.sh25
-rwxr-xr-xt/t0060-path-utils.sh142
-rwxr-xr-xt/t0061-run-command.sh14
-rwxr-xr-xt/t0070-fundamental.sh15
-rwxr-xr-xt/t0100-previous.sh49
-rwxr-xr-xt/t0101-at-syntax.sh45
-rwxr-xr-xt/t1000-read-tree-m-3way.sh528
-rwxr-xr-xt/t1001-read-tree-m-2way.sh409
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh344
-rwxr-xr-xt/t1003-read-tree-prefix.sh27
-rwxr-xr-xt/t1004-read-tree-m-u-wf.sh241
-rwxr-xr-xt/t1005-read-tree-reset.sh90
-rwxr-xr-xt/t1006-cat-file.sh244
-rwxr-xr-xt/t1007-hash-object.sh191
-rwxr-xr-xt/t1008-read-tree-overlay.sh31
-rwxr-xr-xt/t1009-read-tree-new-index.sh25
-rwxr-xr-xt/t1010-mktree.sh69
-rwxr-xr-xt/t1011-read-tree-sparse-checkout.sh150
-rwxr-xr-xt/t1012-read-tree-df.sh102
-rwxr-xr-xt/t1020-subdirectory.sh138
-rwxr-xr-xt/t1100-commit-tree-options.sh45
-rwxr-xr-xt/t1200-tutorial.sh265
-rwxr-xr-xt/t1300-repo-config.sh857
-rwxr-xr-xt/t1301-shared-repo.sh170
-rwxr-xr-xt/t1302-repo-version.sh47
-rwxr-xr-xt/t1303-wacky-config.sh50
-rwxr-xr-xt/t1304-default-acl.sh59
-rwxr-xr-xt/t1400-update-ref.sh288
-rwxr-xr-xt/t1401-symbolic-ref.sh36
-rwxr-xr-xt/t1402-check-ref-format.sh61
-rwxr-xr-xt/t1410-reflog.sh258
-rwxr-xr-xt/t1411-reflog-show.sh76
-rwxr-xr-xt/t1420-lost-found.sh35
-rwxr-xr-xt/t1450-fsck.sh134
-rwxr-xr-xt/t1500-rev-parse.sh87
-rwxr-xr-xt/t1501-worktree.sh206
-rwxr-xr-xt/t1502-rev-parse-parseopt.sh102
-rwxr-xr-xt/t1503-rev-parse-verify.sh107
-rwxr-xr-xt/t1504-ceiling-dirs.sh163
-rwxr-xr-xt/t1505-rev-parse-last.sh69
-rwxr-xr-xt/t1506-rev-parse-diagnosis.sh69
-rwxr-xr-xt/t1507-rev-parse-upstream.sh139
-rwxr-xr-xt/t1508-at-combinations.sh51
-rwxr-xr-xt/t1509-root-worktree.sh249
-rw-r--r--t/t1509/excludes14
-rwxr-xr-xt/t1509/prepare-chroot.sh38
-rwxr-xr-xt/t2000-checkout-cache-clash.sh60
-rwxr-xr-xt/t2001-checkout-cache-clash.sh86
-rwxr-xr-xt/t2002-checkout-cache-u.sh33
-rwxr-xr-xt/t2003-checkout-cache-mkdir.sh96
-rwxr-xr-xt/t2004-checkout-cache-temp.sh212
-rwxr-xr-xt/t2005-checkout-index-symlinks.sh28
-rwxr-xr-xt/t2007-checkout-symlink.sh58
-rwxr-xr-xt/t2008-checkout-subdir.sh82
-rwxr-xr-xt/t2009-checkout-statinfo.sh52
-rwxr-xr-xt/t2010-checkout-ambiguous.sh50
-rwxr-xr-xt/t2011-checkout-invalid-head.sh22
-rwxr-xr-xt/t2012-checkout-last.sh119
-rwxr-xr-xt/t2013-checkout-submodule.sh42
-rwxr-xr-xt/t2014-switch.sh28
-rwxr-xr-xt/t2015-checkout-unborn.sh40
-rwxr-xr-xt/t2016-checkout-patch.sh115
-rwxr-xr-xt/t2017-checkout-orphan.sh119
-rwxr-xr-xt/t2030-unresolve-info.sh170
-rwxr-xr-xt/t2050-git-dir-relative.sh55
-rwxr-xr-xt/t2100-update-cache-badpath.sh61
-rwxr-xr-xt/t2101-update-index-reupdate.sh84
-rwxr-xr-xt/t2102-update-index-symlinks.sh31
-rwxr-xr-xt/t2103-update-index-ignore-missing.sh89
-rwxr-xr-xt/t2104-update-index-skip-worktree.sh57
-rwxr-xr-xt/t2105-update-index-gitfile.sh38
-rwxr-xr-xt/t2106-update-index-assume-unchanged.sh24
-rwxr-xr-xt/t2200-add-update.sh184
-rwxr-xr-xt/t2201-add-update-typechange.sh150
-rwxr-xr-xt/t2202-add-addremove.sh44
-rwxr-xr-xt/t2203-add-intent.sh64
-rwxr-xr-xt/t2204-add-ignored.sh79
-rwxr-xr-xt/t2300-cd-to-toplevel.sh37
-rwxr-xr-xt/t3000-ls-files-others.sh68
-rwxr-xr-xt/t3001-ls-files-others-exclude.sh217
-rwxr-xr-xt/t3002-ls-files-dashpath.sh69
-rwxr-xr-xt/t3003-ls-files-exclude.sh40
-rwxr-xr-xt/t3010-ls-files-killed-modified.sh107
-rwxr-xr-xt/t3020-ls-files-error-unmatch.sh29
-rwxr-xr-xt/t3030-merge-recursive.sh583
-rwxr-xr-xt/t3031-merge-criscross.sh95
-rwxr-xr-xt/t3040-subprojects-basic.sh85
-rwxr-xr-xt/t3050-subprojects-fetch.sh52
-rwxr-xr-xt/t3060-ls-files-with-tree.sh71
-rwxr-xr-xt/t3100-ls-tree-restrict.sh168
-rwxr-xr-xt/t3101-ls-tree-dirname.sh227
-rwxr-xr-xt/t3200-branch.sh518
-rwxr-xr-xt/t3201-branch-contains.sh98
-rwxr-xr-xt/t3202-show-branch-octopus.sh67
-rwxr-xr-xt/t3203-branch-output.sh81
-rwxr-xr-xt/t3210-pack-refs.sh115
-rwxr-xr-xt/t3300-funny-names.sh160
-rwxr-xr-xt/t3301-notes.sh1057
-rwxr-xr-xt/t3302-notes-index-expensive.sh118
-rwxr-xr-xt/t3303-notes-subtrees.sh194
-rwxr-xr-xt/t3304-notes-mixed.sh206
-rwxr-xr-xt/t3305-notes-fanout.sh95
-rwxr-xr-xt/t3306-notes-prune.sh138
-rwxr-xr-xt/t3307-notes-man.sh38
-rwxr-xr-xt/t3400-rebase.sh187
-rwxr-xr-xt/t3401-rebase-partial.sh61
-rwxr-xr-xt/t3402-rebase-merge.sh111
-rwxr-xr-xt/t3403-rebase-skip.sh73
-rwxr-xr-xt/t3404-rebase-interactive.sh657
-rwxr-xr-xt/t3405-rebase-malformed.sh48
-rwxr-xr-xt/t3406-rebase-message.sh65
-rwxr-xr-xt/t3407-rebase-abort.sh80
-rwxr-xr-xt/t3408-rebase-multi-line.sh41
-rwxr-xr-xt/t3409-rebase-preserve-merges.sh95
-rwxr-xr-xt/t3410-rebase-preserve-dropped-merges.sh85
-rwxr-xr-xt/t3411-rebase-preserve-around-merges.sh74
-rwxr-xr-xt/t3412-rebase-root.sh280
-rwxr-xr-xt/t3413-rebase-hook.sh146
-rwxr-xr-xt/t3414-rebase-preserve-onto.sh80
-rwxr-xr-xt/t3415-rebase-autosquash.sh73
-rwxr-xr-xt/t3416-rebase-onto-threedots.sh105
-rwxr-xr-xt/t3417-rebase-whitespace-fix.sh126
-rwxr-xr-xt/t3418-rebase-continue.sh43
-rwxr-xr-xt/t3500-cherry.sh58
-rwxr-xr-xt/t3501-revert-cherry-pick.sh93
-rwxr-xr-xt/t3502-cherry-pick-merge.sh123
-rwxr-xr-xt/t3503-cherry-pick-root.sh30
-rwxr-xr-xt/t3504-cherry-pick-rerere.sh45
-rwxr-xr-xt/t3505-cherry-pick-empty.sh33
-rwxr-xr-xt/t3506-cherry-pick-ff.sh98
-rwxr-xr-xt/t3507-cherry-pick-conflict.sh198
-rwxr-xr-xt/t3508-cherry-pick-many-commits.sh105
-rwxr-xr-xt/t3600-rm.sh282
-rwxr-xr-xt/t3700-add.sh291
-rwxr-xr-xt/t3701-add-interactive.sh268
-rwxr-xr-xt/t3702-add-edit.sh121
-rwxr-xr-xt/t3800-mktag.sh365
-rwxr-xr-xt/t3900-i18n-commit.sh136
-rw-r--r--t/t3900/1-UTF-8.txt3
-rw-r--r--t/t3900/2-UTF-8.txt4
-rw-r--r--t/t3900/ISO-2022-JP.txt4
-rw-r--r--t/t3900/ISO8859-1.txt3
-rw-r--r--t/t3900/eucJP.txt4
-rwxr-xr-xt/t3901-8859-1.txt4
-rwxr-xr-xt/t3901-i18n-patch.sh255
-rwxr-xr-xt/t3901-utf8.txt4
-rwxr-xr-xt/t3902-quoted.sh150
-rwxr-xr-xt/t3903-stash.sh381
-rwxr-xr-xt/t3904-stash-patch.sh55
-rwxr-xr-xt/t4000-diff-format.sh62
-rwxr-xr-xt/t4001-diff-rename.sh80
-rwxr-xr-xt/t4002-diff-basic.sh269
-rwxr-xr-xt/t4003-diff-rename-1.sh128
-rwxr-xr-xt/t4004-diff-rename-symlink.sh73
-rwxr-xr-xt/t4005-diff-rename-2.sh86
-rwxr-xr-xt/t4006-diff-mode.sh30
-rwxr-xr-xt/t4007-rename-3.sh90
-rwxr-xr-xt/t4008-diff-break-rewrite.sh188
-rwxr-xr-xt/t4009-diff-rename-4.sh95
-rwxr-xr-xt/t4010-diff-pathspec.sh73
-rwxr-xr-xt/t4011-diff-symlink.sh97
-rwxr-xr-xt/t4012-diff-binary.sh97
-rwxr-xr-xt/t4013-diff-various.sh285
-rw-r--r--t/t4013/diff.config_format.subjectprefix_DIFFERENT_PREFIX2
-rw-r--r--t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master34
-rw-r--r--t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side39
-rw-r--r--t/t4013/diff.diff-tree_--cc_--patch-with-stat_master34
-rw-r--r--t/t4013/diff.diff-tree_--cc_--stat_--summary_master6
-rw-r--r--t/t4013/diff.diff-tree_--cc_--stat_--summary_side8
-rw-r--r--t/t4013/diff.diff-tree_--cc_--stat_master6
-rw-r--r--t/t4013/diff.diff-tree_--cc_master30
-rw-r--r--t/t4013/diff.diff-tree_--patch-with-raw_initial2
-rw-r--r--t/t4013/diff.diff-tree_--patch-with-stat_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-raw_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-stat_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-raw_initial33
-rw-r--r--t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial34
-rw-r--r--t/t4013/diff.diff-tree_--pretty=oneline_--root_-p_initial29
-rw-r--r--t/t4013/diff.diff-tree_--pretty=oneline_--root_initial6
-rw-r--r--t/t4013/diff.diff-tree_--pretty=oneline_-p_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty=oneline_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--patch-with-raw_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--patch-with-stat_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side43
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--patch-with-raw_initial38
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial39
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial15
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--stat_initial12
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--summary_-r_initial11
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_--summary_initial11
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_-p_initial34
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--root_initial11
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--stat_--summary_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--stat_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty_--summary_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty_-p_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty_-p_side38
-rw-r--r--t/t4013/diff.diff-tree_--pretty_initial2
-rw-r--r--t/t4013/diff.diff-tree_--pretty_side11
-rw-r--r--t/t4013/diff.diff-tree_--root_--abbrev_initial6
-rw-r--r--t/t4013/diff.diff-tree_--root_--patch-with-raw_initial33
-rw-r--r--t/t4013/diff.diff-tree_--root_--patch-with-stat_initial34
-rw-r--r--t/t4013/diff.diff-tree_--root_-p_initial29
-rw-r--r--t/t4013/diff.diff-tree_--root_-r_--abbrev=4_initial6
-rw-r--r--t/t4013/diff.diff-tree_--root_-r_--abbrev_initial6
-rw-r--r--t/t4013/diff.diff-tree_--root_-r_initial6
-rw-r--r--t/t4013/diff.diff-tree_--root_initial6
-rw-r--r--t/t4013/diff.diff-tree_-c_--abbrev_master5
-rw-r--r--t/t4013/diff.diff-tree_-c_--stat_--summary_master6
-rw-r--r--t/t4013/diff.diff-tree_-c_--stat_--summary_side8
-rw-r--r--t/t4013/diff.diff-tree_-c_--stat_master6
-rw-r--r--t/t4013/diff.diff-tree_-c_master5
-rw-r--r--t/t4013/diff.diff-tree_-p_-m_master80
-rw-r--r--t/t4013/diff.diff-tree_-p_initial2
-rw-r--r--t/t4013/diff.diff-tree_-p_master2
-rw-r--r--t/t4013/diff.diff-tree_-r_--abbrev=4_initial2
-rw-r--r--t/t4013/diff.diff-tree_-r_--abbrev_initial2
-rw-r--r--t/t4013/diff.diff-tree_-r_initial2
-rw-r--r--t/t4013/diff.diff-tree_initial2
-rw-r--r--t/t4013/diff.diff-tree_master2
-rw-r--r--t/t4013/diff.diff_--abbrev_initial..side32
-rw-r--r--t/t4013/diff.diff_--dirstat_master~1_master~23
-rw-r--r--t/t4013/diff.diff_--name-status_dir2_dir2
-rw-r--r--t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir3
-rw-r--r--t/t4013/diff.diff_--no-index_--name-status_dir2_dir3
-rw-r--r--t/t4013/diff.diff_--no-index_dir_dir32
-rw-r--r--t/t4013/diff.diff_--patch-with-raw_-r_initial..side36
-rw-r--r--t/t4013/diff.diff_--patch-with-raw_initial..side36
-rw-r--r--t/t4013/diff.diff_--patch-with-stat_-r_initial..side37
-rw-r--r--t/t4013/diff.diff_--patch-with-stat_initial..side37
-rw-r--r--t/t4013/diff.diff_--stat_initial..side6
-rw-r--r--t/t4013/diff.diff_-r_--stat_initial..side6
-rw-r--r--t/t4013/diff.diff_-r_initial..side32
-rw-r--r--t/t4013/diff.diff_initial..side32
-rw-r--r--t/t4013/diff.diff_master_master^_side29
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side61
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..master170
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..master^110
-rw-r--r--t/t4013/diff.format-patch_--attach_--stdout_initial..side61
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master170
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master170
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master170
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master^110
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..master^^62
-rw-r--r--t/t4013/diff.format-patch_--inline_--stdout_initial..side61
-rw-r--r--t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^103
-rw-r--r--t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master127
-rw-r--r--t/t4013/diff.format-patch_--stdout_--numbered_initial..master127
-rw-r--r--t/t4013/diff.format-patch_--stdout_initial..master127
-rw-r--r--t/t4013/diff.format-patch_--stdout_initial..master^81
-rw-r--r--t/t4013/diff.format-patch_--stdout_initial..side47
-rw-r--r--t/t4013/diff.log_--decorate=full_--all34
-rw-r--r--t/t4013/diff.log_--decorate_--all34
-rw-r--r--t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_74
-rw-r--r--t/t4013/diff.log_--patch-with-stat_master129
-rw-r--r--t/t4013/diff.log_--patch-with-stat_master_--_dir_74
-rw-r--r--t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master199
-rw-r--r--t/t4013/diff.log_--root_--patch-with-stat_--summary_master167
-rw-r--r--t/t4013/diff.log_--root_--patch-with-stat_master161
-rw-r--r--t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master199
-rw-r--r--t/t4013/diff.log_--root_-p_master142
-rw-r--r--t/t4013/diff.log_--root_master34
-rw-r--r--t/t4013/diff.log_-SF_-p_master18
-rw-r--r--t/t4013/diff.log_-SF_master7
-rw-r--r--t/t4013/diff.log_-m_-p_--first-parent_master100
-rw-r--r--t/t4013/diff.log_-m_-p_master200
-rw-r--r--t/t4013/diff.log_-p_--first-parent_master78
-rw-r--r--t/t4013/diff.log_-p_master115
-rw-r--r--t/t4013/diff.log_master34
-rw-r--r--t/t4013/diff.rev-list_--children_HEAD7
-rw-r--r--t/t4013/diff.rev-list_--parents_HEAD7
-rw-r--r--t/t4013/diff.show_--first-parent_master30
-rw-r--r--t/t4013/diff.show_--patch-with-raw_side42
-rw-r--r--t/t4013/diff.show_--patch-with-stat_--summary_side44
-rw-r--r--t/t4013/diff.show_--patch-with-stat_side43
-rw-r--r--t/t4013/diff.show_--root_initial34
-rw-r--r--t/t4013/diff.show_--stat_--summary_side13
-rw-r--r--t/t4013/diff.show_--stat_side12
-rw-r--r--t/t4013/diff.show_-c_master36
-rw-r--r--t/t4013/diff.show_-m_master93
-rw-r--r--t/t4013/diff.show_initial7
-rw-r--r--t/t4013/diff.show_master36
-rw-r--r--t/t4013/diff.show_side38
-rw-r--r--t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_61
-rw-r--r--t/t4013/diff.whatchanged_--patch-with-stat_master116
-rw-r--r--t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_61
-rw-r--r--t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master199
-rw-r--r--t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master160
-rw-r--r--t/t4013/diff.whatchanged_--root_--patch-with-stat_master154
-rw-r--r--t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master199
-rw-r--r--t/t4013/diff.whatchanged_--root_-p_master135
-rw-r--r--t/t4013/diff.whatchanged_--root_master42
-rw-r--r--t/t4013/diff.whatchanged_-SF_-p_master18
-rw-r--r--t/t4013/diff.whatchanged_-SF_master9
-rw-r--r--t/t4013/diff.whatchanged_-p_master102
-rw-r--r--t/t4013/diff.whatchanged_master32
-rwxr-xr-xt/t4014-format-patch.sh668
-rwxr-xr-xt/t4015-diff-whitespace.sh494
-rwxr-xr-xt/t4016-diff-quote.sh70
-rwxr-xr-xt/t4017-diff-retval.sh166
-rwxr-xr-xt/t4018-diff-funcname.sh84
-rwxr-xr-xt/t4019-diff-wserror.sh202
-rwxr-xr-xt/t4020-diff-external.sh172
-rw-r--r--t/t4020/diff.NULbin0 -> 116 bytes
-rwxr-xr-xt/t4021-format-patch-numbered.sh124
-rwxr-xr-xt/t4022-diff-rewrite.sh29
-rwxr-xr-xt/t4023-diff-rename-typechange.sh92
-rwxr-xr-xt/t4024-diff-optimize-common.sh157
-rwxr-xr-xt/t4025-hunk-header.sh44
-rwxr-xr-xt/t4026-color.sh93
-rwxr-xr-xt/t4027-diff-submodule.sh195
-rwxr-xr-xt/t4028-format-patch-mime-headers.sh30
-rwxr-xr-xt/t4029-diff-trailing-space.sh39
-rwxr-xr-xt/t4030-diff-textconv.sh126
-rwxr-xr-xt/t4031-diff-rewrite-binary.sh67
-rwxr-xr-xt/t4032-diff-inter-hunk-context.sh92
-rwxr-xr-xt/t4033-diff-patience.sh168
-rwxr-xr-xt/t4034-diff-words.sh334
-rwxr-xr-xt/t4035-diff-quiet.sh80
-rwxr-xr-xt/t4036-format-patch-signer-mime.sh50
-rwxr-xr-xt/t4037-diff-r-t-dirs.sh53
-rwxr-xr-xt/t4038-diff-combined.sh92
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh31
-rwxr-xr-xt/t4040-whitespace-status.sh63
-rwxr-xr-xt/t4041-diff-submodule-option.sh428
-rwxr-xr-xt/t4042-diff-textconv-caching.sh109
-rwxr-xr-xt/t4043-diff-rename-binary.sh45
-rwxr-xr-xt/t4044-diff-index-unique-abbrev.sh35
-rwxr-xr-xt/t4100-apply-stat.sh40
-rw-r--r--t/t4100/t-apply-1.expect11
-rw-r--r--t/t4100/t-apply-1.patch194
-rw-r--r--t/t4100/t-apply-2.expect5
-rw-r--r--t/t4100/t-apply-2.patch72
-rw-r--r--t/t4100/t-apply-3.expect7
-rw-r--r--t/t4100/t-apply-3.patch567
-rw-r--r--t/t4100/t-apply-4.expect5
-rw-r--r--t/t4100/t-apply-4.patch7
-rw-r--r--t/t4100/t-apply-5.expect19
-rw-r--r--t/t4100/t-apply-5.patch612
-rw-r--r--t/t4100/t-apply-6.expect5
-rw-r--r--t/t4100/t-apply-6.patch101
-rw-r--r--t/t4100/t-apply-7.expect6
-rw-r--r--t/t4100/t-apply-7.patch494
-rw-r--r--t/t4100/t-apply-8.expect2
-rw-r--r--t/t4100/t-apply-8.patch11
-rw-r--r--t/t4100/t-apply-9.expect2
-rw-r--r--t/t4100/t-apply-9.patch11
-rwxr-xr-xt/t4101-apply-nonl.sh31
-rw-r--r--t/t4101/diff.0-16
-rw-r--r--t/t4101/diff.0-27
-rw-r--r--t/t4101/diff.0-38
-rw-r--r--t/t4101/diff.1-06
-rw-r--r--t/t4101/diff.1-28
-rw-r--r--t/t4101/diff.1-38
-rw-r--r--t/t4101/diff.2-07
-rw-r--r--t/t4101/diff.2-18
-rw-r--r--t/t4101/diff.2-37
-rw-r--r--t/t4101/diff.3-08
-rw-r--r--t/t4101/diff.3-18
-rw-r--r--t/t4101/diff.3-27
-rwxr-xr-xt/t4102-apply-rename.sh64
-rwxr-xr-xt/t4103-apply-binary.sh121
-rwxr-xr-xt/t4104-apply-boundary.sh146
-rwxr-xr-xt/t4105-apply-fuzz.sh57
-rwxr-xr-xt/t4106-apply-stdin.sh26
-rwxr-xr-xt/t4107-apply-ignore-whitespace.sh185
-rwxr-xr-xt/t4109-apply-multifrag.sh35
-rw-r--r--t/t4109/expect-131
-rw-r--r--t/t4109/expect-223
-rw-r--r--t/t4109/expect-324
-rw-r--r--t/t4109/patch1.patch28
-rw-r--r--t/t4109/patch2.patch30
-rw-r--r--t/t4109/patch3.patch31
-rw-r--r--t/t4109/patch4.patch30
-rwxr-xr-xt/t4110-apply-scan.sh22
-rw-r--r--t/t4110/expect20
-rw-r--r--t/t4110/patch1.patch17
-rw-r--r--t/t4110/patch2.patch11
-rw-r--r--t/t4110/patch3.patch14
-rw-r--r--t/t4110/patch4.patch11
-rw-r--r--t/t4110/patch5.patch11
-rwxr-xr-xt/t4112-apply-renames.sh144
-rwxr-xr-xt/t4113-apply-ending.sh53
-rwxr-xr-xt/t4114-apply-typechange.sh129
-rwxr-xr-xt/t4115-apply-symlink.sh55
-rwxr-xr-xt/t4116-apply-reverse.sh91
-rwxr-xr-xt/t4117-apply-reject.sh157
-rwxr-xr-xt/t4118-apply-empty-context.sh55
-rwxr-xr-xt/t4119-apply-config.sh162
-rwxr-xr-xt/t4120-apply-popt.sh30
-rwxr-xr-xt/t4121-apply-diffs.sh32
-rwxr-xr-xt/t4122-apply-symlink-inside.sh61
-rwxr-xr-xt/t4123-apply-shrink.sh58
-rwxr-xr-xt/t4124-apply-ws-rule.sh451
-rwxr-xr-xt/t4125-apply-ws-fuzz.sh103
-rwxr-xr-xt/t4126-apply-empty.sh57
-rwxr-xr-xt/t4127-apply-same-fn.sh90
-rwxr-xr-xt/t4128-apply-root.sh112
-rwxr-xr-xt/t4129-apply-samemode.sh69
-rwxr-xr-xt/t4130-apply-criss-cross-rename.sh66
-rwxr-xr-xt/t4131-apply-fake-ancestor.sh42
-rwxr-xr-xt/t4132-apply-removal.sh95
-rwxr-xr-xt/t4133-apply-filenames.sh38
-rwxr-xr-xt/t4134-apply-submodule.sh38
-rwxr-xr-xt/t4150-am.sh364
-rwxr-xr-xt/t4151-am-abort.sh65
-rwxr-xr-xt/t4200-rerere.sh240
-rwxr-xr-xt/t4201-shortlog.sh151
-rwxr-xr-xt/t4202-log.sh440
-rwxr-xr-xt/t4203-mailmap.sh215
-rwxr-xr-xt/t4204-patch-id.sh66
-rwxr-xr-xt/t4205-log-pretty-formats.sh74
-rwxr-xr-xt/t4206-log-follow-harder-copies.sh56
-rwxr-xr-xt/t4207-log-decoration-colors.sh66
-rwxr-xr-xt/t4252-am-options.sh78
-rw-r--r--t/t4252/am-test-1-119
-rw-r--r--t/t4252/am-test-1-221
-rw-r--r--t/t4252/am-test-2-119
-rw-r--r--t/t4252/am-test-2-221
-rw-r--r--t/t4252/am-test-3-119
-rw-r--r--t/t4252/am-test-3-221
-rw-r--r--t/t4252/am-test-4-119
-rw-r--r--t/t4252/am-test-4-222
-rw-r--r--t/t4252/am-test-5-120
-rw-r--r--t/t4252/am-test-5-215
-rw-r--r--t/t4252/am-test-6-121
-rw-r--r--t/t4252/file-1-07
-rw-r--r--t/t4252/file-2-07
-rwxr-xr-xt/t4253-am-keep-cr-dos.sh96
-rwxr-xr-xt/t4300-merge-tree.sh257
-rwxr-xr-xt/t5000-tar-tree.sh255
-rwxr-xr-xt/t5001-archive-attr.sh91
-rwxr-xr-xt/t5100-mailinfo.sh92
-rw-r--r--t/t5100/.gitattributes4
-rw-r--r--t/t5100/empty0
-rw-r--r--t/t5100/info-from.expect5
-rw-r--r--t/t5100/info-from.in8
-rw-r--r--t/t5100/info00015
-rw-r--r--t/t5100/info00025
-rw-r--r--t/t5100/info00035
-rw-r--r--t/t5100/info00045
-rw-r--r--t/t5100/info00055
-rw-r--r--t/t5100/info00065
-rw-r--r--t/t5100/info00075
-rw-r--r--t/t5100/info00085
-rw-r--r--t/t5100/info00095
-rw-r--r--t/t5100/info00105
-rw-r--r--t/t5100/info00115
-rw-r--r--t/t5100/info00125
-rw-r--r--t/t5100/info00135
-rw-r--r--t/t5100/info00145
-rw-r--r--t/t5100/info0014--scissors5
-rw-r--r--t/t5100/info00155
-rw-r--r--t/t5100/info0015--no-inbody-headers5
-rw-r--r--t/t5100/info00165
-rw-r--r--t/t5100/info0016--no-inbody-headers5
-rw-r--r--t/t5100/msg00012
-rw-r--r--t/t5100/msg000221
-rw-r--r--t/t5100/msg00039
-rw-r--r--t/t5100/msg00047
-rw-r--r--t/t5100/msg000513
-rw-r--r--t/t5100/msg00062
-rw-r--r--t/t5100/msg00072
-rw-r--r--t/t5100/msg00084
-rw-r--r--t/t5100/msg00092
-rw-r--r--t/t5100/msg00105
-rw-r--r--t/t5100/msg00112
-rw-r--r--t/t5100/msg00127
-rw-r--r--t/t5100/msg00130
-rw-r--r--t/t5100/msg001418
-rw-r--r--t/t5100/msg0014--scissors4
-rw-r--r--t/t5100/msg00152
-rw-r--r--t/t5100/msg0015--no-inbody-headers3
-rw-r--r--t/t5100/msg00162
-rw-r--r--t/t5100/msg0016--no-inbody-headers4
-rw-r--r--t/t5100/nul-b64.expectbin0 -> 1672 bytes
-rw-r--r--t/t5100/nul-b64.in37
-rw-r--r--t/t5100/nul-plainbin0 -> 91 bytes
-rw-r--r--t/t5100/patch000114
-rw-r--r--t/t5100/patch000214
-rw-r--r--t/t5100/patch000314
-rw-r--r--t/t5100/patch000493
-rw-r--r--t/t5100/patch000569
-rw-r--r--t/t5100/patch000614
-rw-r--r--t/t5100/patch00070
-rw-r--r--t/t5100/patch00080
-rw-r--r--t/t5100/patch000913
-rw-r--r--t/t5100/patch001020
-rw-r--r--t/t5100/patch001122
-rw-r--r--t/t5100/patch001230
-rw-r--r--t/t5100/patch00130
-rw-r--r--t/t5100/patch001464
-rw-r--r--t/t5100/patch0014--scissors64
-rw-r--r--t/t5100/patch00158
-rw-r--r--t/t5100/patch0015--no-inbody-headers8
-rw-r--r--t/t5100/patch00168
-rw-r--r--t/t5100/patch0016--no-inbody-headers8
-rw-r--r--t/t5100/rfc2047-info-00014
-rw-r--r--t/t5100/rfc2047-info-00024
-rw-r--r--t/t5100/rfc2047-info-00034
-rw-r--r--t/t5100/rfc2047-info-00044
-rw-r--r--t/t5100/rfc2047-info-00052
-rw-r--r--t/t5100/rfc2047-info-00062
-rw-r--r--t/t5100/rfc2047-info-00072
-rw-r--r--t/t5100/rfc2047-info-00082
-rw-r--r--t/t5100/rfc2047-info-00092
-rw-r--r--t/t5100/rfc2047-info-00102
-rw-r--r--t/t5100/rfc2047-info-00112
-rw-r--r--t/t5100/rfc2047-samples.mbox48
-rw-r--r--t/t5100/sample.mbox685
-rwxr-xr-xt/t5150-request-pull.sh228
-rwxr-xr-xt/t5300-pack-object.sh417
-rwxr-xr-xt/t5301-sliding-window.sh60
-rwxr-xr-xt/t5302-pack-index.sh225
-rwxr-xr-xt/t5303-pack-corruption-resilience.sh287
-rwxr-xr-xt/t5304-prune.sh197
-rwxr-xr-xt/t5305-include-tag.sh84
-rwxr-xr-xt/t5306-pack-nobase.sh80
-rwxr-xr-xt/t5307-pack-missing-commit.sh39
-rwxr-xr-xt/t5400-send-pack.sh202
-rwxr-xr-xt/t5401-update-hooks.sh138
-rwxr-xr-xt/t5402-post-merge-hook.sh56
-rwxr-xr-xt/t5403-post-checkout-hook.sh88
-rwxr-xr-xt/t5404-tracking-branches.sh62
-rwxr-xr-xt/t5405-send-pack-rewind.sh43
-rwxr-xr-xt/t5406-remote-rejects.sh24
-rwxr-xr-xt/t5407-post-rewrite-hook.sh199
-rwxr-xr-xt/t5500-fetch-pack.sh251
-rwxr-xr-xt/t5502-quickfetch.sh142
-rwxr-xr-xt/t5503-tagfollow.sh156
-rwxr-xr-xt/t5505-remote.sh867
-rwxr-xr-xt/t5506-remote-groups.sh98
-rwxr-xr-xt/t5510-fetch.sh362
-rwxr-xr-xt/t5511-refspec.sh87
-rwxr-xr-xt/t5512-ls-remote.sh126
-rwxr-xr-xt/t5513-fetch-track.sh30
-rwxr-xr-xt/t5514-fetch-multiple.sh154
-rwxr-xr-xt/t5515-fetch-merge-logic.sh175
-rw-r--r--t/t5515/fetch.br-branches-default8
-rw-r--r--t/t5515/fetch.br-branches-default-merge9
-rw-r--r--t/t5515/fetch.br-branches-default-merge_branches-default9
-rw-r--r--t/t5515/fetch.br-branches-default-octopus10
-rw-r--r--t/t5515/fetch.br-branches-default-octopus_branches-default10
-rw-r--r--t/t5515/fetch.br-branches-default_branches-default8
-rw-r--r--t/t5515/fetch.br-branches-one8
-rw-r--r--t/t5515/fetch.br-branches-one-merge9
-rw-r--r--t/t5515/fetch.br-branches-one-merge_branches-one9
-rw-r--r--t/t5515/fetch.br-branches-one-octopus9
-rw-r--r--t/t5515/fetch.br-branches-one-octopus_branches-one9
-rw-r--r--t/t5515/fetch.br-branches-one_branches-one8
-rw-r--r--t/t5515/fetch.br-config-explicit11
-rw-r--r--t/t5515/fetch.br-config-explicit-merge11
-rw-r--r--t/t5515/fetch.br-config-explicit-merge_config-explicit11
-rw-r--r--t/t5515/fetch.br-config-explicit-octopus11
-rw-r--r--t/t5515/fetch.br-config-explicit-octopus_config-explicit11
-rw-r--r--t/t5515/fetch.br-config-explicit_config-explicit11
-rw-r--r--t/t5515/fetch.br-config-glob11
-rw-r--r--t/t5515/fetch.br-config-glob-merge11
-rw-r--r--t/t5515/fetch.br-config-glob-merge_config-glob11
-rw-r--r--t/t5515/fetch.br-config-glob-octopus11
-rw-r--r--t/t5515/fetch.br-config-glob-octopus_config-glob11
-rw-r--r--t/t5515/fetch.br-config-glob_config-glob11
-rw-r--r--t/t5515/fetch.br-remote-explicit11
-rw-r--r--t/t5515/fetch.br-remote-explicit-merge11
-rw-r--r--t/t5515/fetch.br-remote-explicit-merge_remote-explicit11
-rw-r--r--t/t5515/fetch.br-remote-explicit-octopus11
-rw-r--r--t/t5515/fetch.br-remote-explicit-octopus_remote-explicit11
-rw-r--r--t/t5515/fetch.br-remote-explicit_remote-explicit11
-rw-r--r--t/t5515/fetch.br-remote-glob11
-rw-r--r--t/t5515/fetch.br-remote-glob-merge11
-rw-r--r--t/t5515/fetch.br-remote-glob-merge_remote-glob11
-rw-r--r--t/t5515/fetch.br-remote-glob-octopus11
-rw-r--r--t/t5515/fetch.br-remote-glob-octopus_remote-glob11
-rw-r--r--t/t5515/fetch.br-remote-glob_remote-glob11
-rw-r--r--t/t5515/fetch.br-unconfig11
-rw-r--r--t/t5515/fetch.br-unconfig_--tags_.._.git7
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git2
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_one2
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file8
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_one_two3
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file7
-rw-r--r--t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three7
-rw-r--r--t/t5515/fetch.br-unconfig_branches-default8
-rw-r--r--t/t5515/fetch.br-unconfig_branches-one8
-rw-r--r--t/t5515/fetch.br-unconfig_config-explicit11
-rw-r--r--t/t5515/fetch.br-unconfig_config-glob11
-rw-r--r--t/t5515/fetch.br-unconfig_remote-explicit11
-rw-r--r--t/t5515/fetch.br-unconfig_remote-glob11
-rw-r--r--t/t5515/fetch.master11
-rw-r--r--t/t5515/fetch.master_--tags_.._.git7
-rw-r--r--t/t5515/fetch.master_.._.git2
-rw-r--r--t/t5515/fetch.master_.._.git_one2
-rw-r--r--t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file8
-rw-r--r--t/t5515/fetch.master_.._.git_one_two3
-rw-r--r--t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file7
-rw-r--r--t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three7
-rw-r--r--t/t5515/fetch.master_branches-default8
-rw-r--r--t/t5515/fetch.master_branches-one8
-rw-r--r--t/t5515/fetch.master_config-explicit11
-rw-r--r--t/t5515/fetch.master_config-glob11
-rw-r--r--t/t5515/fetch.master_remote-explicit11
-rw-r--r--t/t5515/fetch.master_remote-glob11
-rw-r--r--t/t5515/refs.br-branches-default12
-rw-r--r--t/t5515/refs.br-branches-default-merge12
-rw-r--r--t/t5515/refs.br-branches-default-merge_branches-default12
-rw-r--r--t/t5515/refs.br-branches-default-octopus12
-rw-r--r--t/t5515/refs.br-branches-default-octopus_branches-default12
-rw-r--r--t/t5515/refs.br-branches-default_branches-default12
-rw-r--r--t/t5515/refs.br-branches-one12
-rw-r--r--t/t5515/refs.br-branches-one-merge12
-rw-r--r--t/t5515/refs.br-branches-one-merge_branches-one12
-rw-r--r--t/t5515/refs.br-branches-one-octopus12
-rw-r--r--t/t5515/refs.br-branches-one-octopus_branches-one12
-rw-r--r--t/t5515/refs.br-branches-one_branches-one12
-rw-r--r--t/t5515/refs.br-config-explicit15
-rw-r--r--t/t5515/refs.br-config-explicit-merge15
-rw-r--r--t/t5515/refs.br-config-explicit-merge_config-explicit15
-rw-r--r--t/t5515/refs.br-config-explicit-octopus15
-rw-r--r--t/t5515/refs.br-config-explicit-octopus_config-explicit15
-rw-r--r--t/t5515/refs.br-config-explicit_config-explicit15
-rw-r--r--t/t5515/refs.br-config-glob15
-rw-r--r--t/t5515/refs.br-config-glob-merge15
-rw-r--r--t/t5515/refs.br-config-glob-merge_config-glob15
-rw-r--r--t/t5515/refs.br-config-glob-octopus15
-rw-r--r--t/t5515/refs.br-config-glob-octopus_config-glob15
-rw-r--r--t/t5515/refs.br-config-glob_config-glob15
-rw-r--r--t/t5515/refs.br-remote-explicit15
-rw-r--r--t/t5515/refs.br-remote-explicit-merge15
-rw-r--r--t/t5515/refs.br-remote-explicit-merge_remote-explicit15
-rw-r--r--t/t5515/refs.br-remote-explicit-octopus15
-rw-r--r--t/t5515/refs.br-remote-explicit-octopus_remote-explicit15
-rw-r--r--t/t5515/refs.br-remote-explicit_remote-explicit15
-rw-r--r--t/t5515/refs.br-remote-glob15
-rw-r--r--t/t5515/refs.br-remote-glob-merge15
-rw-r--r--t/t5515/refs.br-remote-glob-merge_remote-glob15
-rw-r--r--t/t5515/refs.br-remote-glob-octopus15
-rw-r--r--t/t5515/refs.br-remote-glob-octopus_remote-glob15
-rw-r--r--t/t5515/refs.br-remote-glob_remote-glob15
-rw-r--r--t/t5515/refs.br-unconfig11
-rw-r--r--t/t5515/refs.br-unconfig_--tags_.._.git11
-rw-r--r--t/t5515/refs.br-unconfig_.._.git5
-rw-r--r--t/t5515/refs.br-unconfig_.._.git_one5
-rw-r--r--t/t5515/refs.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file11
-rw-r--r--t/t5515/refs.br-unconfig_.._.git_one_two5
-rw-r--r--t/t5515/refs.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file11
-rw-r--r--t/t5515/refs.br-unconfig_.._.git_tag_tag-one_tag_tag-three11
-rw-r--r--t/t5515/refs.br-unconfig_branches-default12
-rw-r--r--t/t5515/refs.br-unconfig_branches-one12
-rw-r--r--t/t5515/refs.br-unconfig_config-explicit15
-rw-r--r--t/t5515/refs.br-unconfig_config-glob15
-rw-r--r--t/t5515/refs.br-unconfig_remote-explicit15
-rw-r--r--t/t5515/refs.br-unconfig_remote-glob15
-rw-r--r--t/t5515/refs.master11
-rw-r--r--t/t5515/refs.master_--tags_.._.git11
-rw-r--r--t/t5515/refs.master_.._.git5
-rw-r--r--t/t5515/refs.master_.._.git_one5
-rw-r--r--t/t5515/refs.master_.._.git_one_tag_tag-one_tag_tag-three-file11
-rw-r--r--t/t5515/refs.master_.._.git_one_two5
-rw-r--r--t/t5515/refs.master_.._.git_tag_tag-one-tree_tag_tag-three-file11
-rw-r--r--t/t5515/refs.master_.._.git_tag_tag-one_tag_tag-three11
-rw-r--r--t/t5515/refs.master_branches-default12
-rw-r--r--t/t5515/refs.master_branches-one12
-rw-r--r--t/t5515/refs.master_config-explicit15
-rw-r--r--t/t5515/refs.master_config-glob15
-rw-r--r--t/t5515/refs.master_remote-explicit15
-rw-r--r--t/t5515/refs.master_remote-glob15
-rwxr-xr-xt/t5516-fetch-push.sh785
-rwxr-xr-xt/t5517-push-mirror.sh268
-rwxr-xr-xt/t5518-fetch-exit-status.sh37
-rwxr-xr-xt/t5519-push-alternates.sh143
-rwxr-xr-xt/t5520-pull.sh163
-rwxr-xr-xt/t5521-pull-options.sh93
-rwxr-xr-xt/t5522-pull-symlink.sh90
-rwxr-xr-xt/t5523-push-upstream.sh69
-rwxr-xr-xt/t5524-pull-msg.sh35
-rwxr-xr-xt/t5530-upload-pack-error.sh86
-rwxr-xr-xt/t5531-deep-submodule-push.sh35
-rwxr-xr-xt/t5540-http-push.sh145
-rwxr-xr-xt/t5541-http-push.sh141
-rwxr-xr-xt/t5550-http-fetch.sh102
-rwxr-xr-xt/t5551-http-fetch.sh105
-rwxr-xr-xt/t5560-http-backend-noserver.sh73
-rwxr-xr-xt/t5561-http-backend.sh149
-rwxr-xr-xt/t556x_common122
-rwxr-xr-xt/t5600-clone-fail-cleanup.sh42
-rwxr-xr-xt/t5601-clone.sh191
-rwxr-xr-xt/t5602-clone-remote-exec.sh26
-rwxr-xr-xt/t5700-clone-reference.sh149
-rwxr-xr-xt/t5701-clone-local.sh147
-rwxr-xr-xt/t5702-clone-options.sh36
-rwxr-xr-xt/t5704-bundle.sh49
-rwxr-xr-xt/t5705-clone-2gb.sh51
-rwxr-xr-xt/t5706-clone-branch.sh68
-rwxr-xr-xt/t5710-info-alternate.sh112
-rwxr-xr-xt/t5800-remote-helpers.sh82
-rwxr-xr-xt/t6001-rev-list-graft.sh113
-rwxr-xr-xt/t6002-rev-list-bisect.sh238
-rwxr-xr-xt/t6003-rev-list-topo-order.sh408
-rwxr-xr-xt/t6004-rev-list-path-optim.sh51
-rwxr-xr-xt/t6005-rev-list-count.sh51
-rwxr-xr-xt/t6006-rev-list-format.sh271
-rwxr-xr-xt/t6007-rev-list-cherry-pick-file.sh86
-rwxr-xr-xt/t6008-rev-list-submodule.sh42
-rwxr-xr-xt/t6009-rev-list-parent.sh38
-rwxr-xr-xt/t6010-merge-base.sh177
-rwxr-xr-xt/t6011-rev-list-with-bad-commit.sh60
-rwxr-xr-xt/t6012-rev-list-simplify.sh90
-rwxr-xr-xt/t6013-rev-list-reverse-parents.sh42
-rwxr-xr-xt/t6014-rev-list-all.sh38
-rwxr-xr-xt/t6015-rev-list-show-all-parents.sh31
-rwxr-xr-xt/t6016-rev-list-graph-simplify-history.sh276
-rwxr-xr-xt/t6017-rev-list-stdin.sh61
-rwxr-xr-xt/t6018-rev-list-glob.sh210
-rwxr-xr-xt/t6019-rev-list-ancestry-path.sh73
-rwxr-xr-xt/t6020-merge-df.sh48
-rwxr-xr-xt/t6021-merge-criss-cross.sh96
-rwxr-xr-xt/t6022-merge-rename.sh344
-rwxr-xr-xt/t6023-merge-file.sh255
-rwxr-xr-xt/t6024-recursive-merge.sh122
-rwxr-xr-xt/t6025-merge-symlinks.sh61
-rwxr-xr-xt/t6026-merge-attr.sh179
-rwxr-xr-xt/t6027-merge-binary.sh67
-rwxr-xr-xt/t6028-merge-up-to-date.sh77
-rwxr-xr-xt/t6029-merge-subtree.sh124
-rwxr-xr-xt/t6030-bisect-porcelain.sh577
-rwxr-xr-xt/t6031-merge-recursive.sh60
-rwxr-xr-xt/t6032-merge-large-rename.sh73
-rwxr-xr-xt/t6033-merge-crlf.sh44
-rwxr-xr-xt/t6034-merge-rename-nocruft.sh139
-rwxr-xr-xt/t6035-merge-dir-to-symlink.sh93
-rwxr-xr-xt/t6036-recursive-corner-cases.sh55
-rwxr-xr-xt/t6037-merge-ours-theirs.sh64
-rwxr-xr-xt/t6040-tracking-info.sh113
-rwxr-xr-xt/t6050-replace.sh224
-rwxr-xr-xt/t6101-rev-parse-parents.sh44
-rwxr-xr-xt/t6120-describe.sh174
-rwxr-xr-xt/t6200-fmt-merge-msg.sh371
-rwxr-xr-xt/t6300-for-each-ref.sh362
-rwxr-xr-xt/t7001-mv.sh258
-rwxr-xr-xt/t7003-filter-branch.sh348
-rwxr-xr-xt/t7004-tag.sh1219
-rw-r--r--t/t7004/pubring.gpgbin0 -> 1164 bytes
-rw-r--r--t/t7004/random_seedbin0 -> 600 bytes
-rw-r--r--t/t7004/secring.gpgbin0 -> 1237 bytes
-rw-r--r--t/t7004/trustdb.gpgbin0 -> 1280 bytes
-rwxr-xr-xt/t7005-editor.sh137
-rwxr-xr-xt/t7006-pager.sh372
-rwxr-xr-xt/t7006/test-terminal.perl58
-rwxr-xr-xt/t7007-show.sh20
-rwxr-xr-xt/t7008-grep-binary.sh102
-rwxr-xr-xt/t7010-setup.sh161
-rwxr-xr-xt/t7011-skip-worktree-reading.sh163
-rwxr-xr-xt/t7012-skip-worktree-writing.sh146
-rwxr-xr-xt/t7060-wtstatus.sh59
-rwxr-xr-xt/t7101-reset.sh63
-rwxr-xr-xt/t7102-reset.sh479
-rwxr-xr-xt/t7103-reset-bare.sh69
-rwxr-xr-xt/t7104-reset.sh46
-rwxr-xr-xt/t7105-reset-patch.sh69
-rwxr-xr-xt/t7110-reset-merge.sh295
-rwxr-xr-xt/t7111-reset-table.sh121
-rwxr-xr-xt/t7201-co.sh671
-rwxr-xr-xt/t7300-clean.sh441
-rwxr-xr-xt/t7400-submodule-basic.sh430
-rwxr-xr-xt/t7401-submodule-summary.sh237
-rwxr-xr-xt/t7402-submodule-rebase.sh92
-rwxr-xr-xt/t7403-submodule-sync.sh64
-rwxr-xr-xt/t7405-submodule-merge.sh74
-rwxr-xr-xt/t7406-submodule-update.sh206
-rwxr-xr-xt/t7407-submodule-foreach.sh241
-rwxr-xr-xt/t7408-submodule-reference.sh81
-rwxr-xr-xt/t7500-commit.sh218
-rwxr-xr-xt/t7500/add-comments4
-rwxr-xr-xt/t7500/add-content3
-rwxr-xr-xt/t7500/add-signed-off3
-rwxr-xr-xt/t7501-commit.sh440
-rwxr-xr-xt/t7502-commit.sh447
-rwxr-xr-xt/t7503-pre-commit-hook.sh88
-rwxr-xr-xt/t7504-commit-msg-hook.sh223
-rwxr-xr-xt/t7505-prepare-commit-msg-hook.sh159
-rwxr-xr-xt/t7506-status-submodule.sh190
-rwxr-xr-xt/t7507-commit-verbose.sh73
-rwxr-xr-xt/t7508-status.sh938
-rwxr-xr-xt/t7509-commit.sh160
-rwxr-xr-xt/t7600-merge.sh603
-rwxr-xr-xt/t7601-merge-pull-config.sh156
-rwxr-xr-xt/t7602-merge-octopus-many.sh103
-rwxr-xr-xt/t7603-merge-reduce-heads.sh116
-rwxr-xr-xt/t7604-merge-custom-message.sh50
-rwxr-xr-xt/t7605-merge-resolve.sh48
-rwxr-xr-xt/t7606-merge-custom.sh49
-rwxr-xr-xt/t7607-merge-overwrite.sh87
-rwxr-xr-xt/t7608-merge-messages.sh60
-rwxr-xr-xt/t7610-mergetool.sh89
-rwxr-xr-xt/t7700-repack.sh168
-rwxr-xr-xt/t7701-repack-unpack-unreachable.sh98
-rwxr-xr-xt/t7800-difftool.sh276
-rwxr-xr-xt/t7810-grep.sh530
-rwxr-xr-xt/t7811-grep-open.sh168
-rwxr-xr-xt/t8001-annotate.sh15
-rwxr-xr-xt/t8002-blame.sh9
-rwxr-xr-xt/t8003-blame.sh188
-rwxr-xr-xt/t8004-blame.sh73
-rwxr-xr-xt/t8005-blame-i18n.sh92
-rw-r--r--t/t8005/euc-japan.txt2
-rw-r--r--t/t8005/sjis.txt2
-rw-r--r--t/t8005/utf8.txt2
-rwxr-xr-xt/t8006-blame-textconv.sh80
-rwxr-xr-xt/t8007-cat-file-textconv.sh70
-rwxr-xr-xt/t9001-send-email.sh998
-rwxr-xr-xt/t9100-git-svn-basic.sh291
-rwxr-xr-xt/t9101-git-svn-props.sh220
-rwxr-xr-xt/t9102-git-svn-deep-rmdir.sh30
-rwxr-xr-xt/t9103-git-svn-tracked-directory-removed.sh39
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh213
-rwxr-xr-xt/t9105-git-svn-commit-diff.sh43
-rwxr-xr-xt/t9106-git-svn-commit-diff-clobber.sh101
-rwxr-xr-xt/t9107-git-svn-migrate.sh121
-rwxr-xr-xt/t9108-git-svn-glob.sh111
-rwxr-xr-xt/t9109-git-svn-multi-glob.sh160
-rwxr-xr-xt/t9110-git-svn-use-svm-props.sh61
-rw-r--r--t/t9110/svm.dump511
-rwxr-xr-xt/t9111-git-svn-use-svnsync-props.sh51
-rw-r--r--t/t9111/svnsync.dump560
-rwxr-xr-xt/t9112-git-svn-md5less-file.sh47
-rwxr-xr-xt/t9113-git-svn-dcommit-new-file.sh35
-rwxr-xr-xt/t9114-git-svn-dcommit-merge.sh94
-rwxr-xr-xt/t9115-git-svn-dcommit-funky-renames.sh87
-rw-r--r--t/t9115/funky-names.dump103
-rwxr-xr-xt/t9116-git-svn-log.sh128
-rwxr-xr-xt/t9117-git-svn-init-clone.sh55
-rwxr-xr-xt/t9118-git-svn-funky-branch-names.sh81
-rwxr-xr-xt/t9119-git-svn-info.sh378
-rwxr-xr-xt/t9120-git-svn-clone-with-percent-escapes.sh78
-rwxr-xr-xt/t9121-git-svn-fetch-renamed-dir.sh20
-rw-r--r--t/t9121/renamed-dir.dump90
-rwxr-xr-xt/t9122-git-svn-author.sh84
-rwxr-xr-xt/t9123-git-svn-rebuild-with-rewriteroot.sh32
-rwxr-xr-xt/t9124-git-svn-dcommit-auto-props.sh101
-rwxr-xr-xt/t9125-git-svn-multi-glob-branch-names.sh37
-rwxr-xr-xt/t9126-git-svn-follow-deleted-readded-directory.sh22
-rw-r--r--t/t9126/follow-deleted-readded.dump201
-rwxr-xr-xt/t9127-git-svn-partial-rebuild.sh59
-rwxr-xr-xt/t9128-git-svn-cmd-branch.sh78
-rwxr-xr-xt/t9129-git-svn-i18n-commitencoding.sh101
-rwxr-xr-xt/t9130-git-svn-authors-file.sh117
-rwxr-xr-xt/t9131-git-svn-empty-symlink.sh110
-rwxr-xr-xt/t9132-git-svn-broken-symlink.sh102
-rwxr-xr-xt/t9133-git-svn-nested-git-repo.sh101
-rwxr-xr-xt/t9134-git-svn-ignore-paths.sh147
-rwxr-xr-xt/t9135-git-svn-moved-branch-empty-file.sh21
-rw-r--r--t/t9135/svn.dump192
-rwxr-xr-xt/t9136-git-svn-recreated-branch-empty-file.sh12
-rw-r--r--t/t9136/svn.dump192
-rwxr-xr-xt/t9137-git-svn-dcommit-clobber-series.sh63
-rwxr-xr-xt/t9138-git-svn-authors-prog.sh83
-rwxr-xr-xt/t9139-git-svn-non-utf8-commitencoding.sh47
-rwxr-xr-xt/t9140-git-svn-reset.sh66
-rwxr-xr-xt/t9141-git-svn-multiple-branches.sh122
-rwxr-xr-xt/t9142-git-svn-shallow-clone.sh32
-rwxr-xr-xt/t9143-git-svn-gc.sh53
-rwxr-xr-xt/t9144-git-svn-old-rev_map.sh31
-rwxr-xr-xt/t9145-git-svn-master-branch.sh25
-rwxr-xr-xt/t9146-git-svn-empty-dirs.sh142
-rwxr-xr-xt/t9150-svk-mergetickets.sh25
-rw-r--r--t/t9150/make-svk-dump57
-rw-r--r--t/t9150/svk-merge.dump616
-rwxr-xr-xt/t9151-svn-mergeinfo.sh57
-rw-r--r--t/t9151/.gitignore2
-rw-r--r--t/t9151/make-svnmerge-dump305
-rw-r--r--t/t9151/svn-mergeinfo.dump2388
-rwxr-xr-xt/t9152-svn-empty-dirs-after-gc.sh40
-rwxr-xr-xt/t9153-git-svn-rewrite-uuid.sh25
-rw-r--r--t/t9153/svn.dump75
-rwxr-xr-xt/t9154-git-svn-fancy-glob.sh42
-rw-r--r--t/t9154/svn.dump222
-rwxr-xr-xt/t9200-git-cvsexportcommit.sh341
-rwxr-xr-xt/t9300-fast-import.sh1585
-rwxr-xr-xt/t9301-fast-import-notes.sh623
-rwxr-xr-xt/t9350-fast-export.sh379
-rwxr-xr-xt/t9400-git-cvsserver-server.sh520
-rwxr-xr-xt/t9401-git-cvsserver-crlf.sh332
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh679
-rwxr-xr-xt/t9501-gitweb-standalone-http-status.sh138
-rwxr-xr-xt/t9502-gitweb-standalone-parse-output.sh115
-rwxr-xr-xt/t9600-cvsimport.sh161
-rwxr-xr-xt/t9601-cvsimport-vendor-branch.sh86
-rw-r--r--t/t9601/cvsroot/.gitattributes1
-rw-r--r--t/t9601/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9601/cvsroot/module/added-imported.txt,v44
-rw-r--r--t/t9601/cvsroot/module/imported-anonymously.txt,v42
-rw-r--r--t/t9601/cvsroot/module/imported-modified-imported.txt,v76
-rw-r--r--t/t9601/cvsroot/module/imported-modified.txt,v59
-rw-r--r--t/t9601/cvsroot/module/imported-once.txt,v43
-rw-r--r--t/t9601/cvsroot/module/imported-twice.txt,v60
-rwxr-xr-xt/t9602-cvsimport-branches-tags.sh79
-rw-r--r--t/t9602/README62
-rw-r--r--t/t9602/cvsroot/.gitattributes1
-rw-r--r--t/t9602/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9602/cvsroot/module/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub1/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub1/subsubA/default,v101
-rw-r--r--t/t9602/cvsroot/module/sub1/subsubB/default,v107
-rw-r--r--t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v59
-rw-r--r--t/t9602/cvsroot/module/sub2/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub2/subsubA/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub3/default,v102
-rwxr-xr-xt/t9603-cvsimport-patchsets.sh40
-rw-r--r--t/t9603/cvsroot/.gitattributes1
-rw-r--r--t/t9603/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9603/cvsroot/module/a,v74
-rw-r--r--t/t9603/cvsroot/module/b,v90
-rwxr-xr-xt/t9700-perl-git.sh56
-rwxr-xr-xt/t9700/test.pl119
-rw-r--r--t/test-lib.sh889
-rw-r--r--t/test4012.pngbin0 -> 5660 bytes
-rw-r--r--t/test9200a.pngbin0 -> 5660 bytes
-rw-r--r--t/test9200b.pngbin0 -> 275 bytes
-rw-r--r--t/valgrind/.gitignore2
-rwxr-xr-xt/valgrind/analyze.sh123
-rw-r--r--t/valgrind/default.supp45
-rwxr-xr-xt/valgrind/valgrind.sh22
958 files changed, 93548 insertions, 0 deletions
diff --git a/t/.gitattributes b/t/.gitattributes
new file mode 100644
index 0000000..1b97c54
--- /dev/null
+++ b/t/.gitattributes
@@ -0,0 +1 @@
+t[0-9][0-9][0-9][0-9]/* -whitespace
diff --git a/t/.gitignore b/t/.gitignore
new file mode 100644
index 0000000..7dcbb23
--- /dev/null
+++ b/t/.gitignore
@@ -0,0 +1,2 @@
+/trash directory*
+/test-results
diff --git a/t/Makefile b/t/Makefile
new file mode 100644
index 0000000..cf5f9e2
--- /dev/null
+++ b/t/Makefile
@@ -0,0 +1,51 @@
+# Run tests
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+-include ../config.mak.autogen
+-include ../config.mak
+
+#GIT_TEST_OPTS=--verbose --debug
+SHELL_PATH ?= $(SHELL)
+TAR ?= $(TAR)
+RM ?= rm -f
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+TSVN = $(wildcard t91[0-9][0-9]-*.sh)
+
+all: pre-clean
+ $(MAKE) aggregate-results-and-cleanup
+
+$(T):
+ @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+pre-clean:
+ $(RM) -r test-results
+
+clean:
+ $(RM) -r 'trash directory'.* test-results
+ $(RM) t????/cvsroot/CVSROOT/?*
+ $(RM) -r valgrind/bin
+
+aggregate-results-and-cleanup: $(T)
+ $(MAKE) aggregate-results
+ $(MAKE) clean
+
+aggregate-results:
+ for f in test-results/t*-*.counts; do \
+ echo "$$f"; \
+ done | '$(SHELL_PATH_SQ)' ./aggregate-results.sh
+
+# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
+full-svn-test:
+ $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
+ $(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8
+
+valgrind:
+ GIT_TEST_OPTS=--valgrind $(MAKE)
+
+.PHONY: pre-clean $(T) aggregate-results clean valgrind
diff --git a/t/README b/t/README
new file mode 100644
index 0000000..0d1183c
--- /dev/null
+++ b/t/README
@@ -0,0 +1,510 @@
+Core GIT Tests
+==============
+
+This directory holds many test scripts for core GIT tools. The
+first part of this short document describes how to run the tests
+and read their output.
+
+When fixing the tools or adding enhancements, you are strongly
+encouraged to add tests in this directory to cover what you are
+trying to fix or enhance. The later part of this short document
+describes how your test scripts should be organized.
+
+
+Running Tests
+-------------
+
+The easiest way to run tests is to say "make". This runs all
+the tests.
+
+ *** t0000-basic.sh ***
+ ok 1 - .git/objects should be empty after git init in an empty repo.
+ ok 2 - .git/objects should have 3 subdirectories.
+ ok 3 - success is reported like this
+ ...
+ ok 43 - very long name in the index handled sanely
+ # fixed 1 known breakage(s)
+ # still have 1 known breakage(s)
+ # passed all remaining 42 test(s)
+ 1..43
+ *** t0001-init.sh ***
+ ok 1 - plain
+ ok 2 - plain with GIT_WORK_TREE
+ ok 3 - plain bare
+
+Since the tests all output TAP (see http://testanything.org) they can
+be run with any TAP harness. Here's an example of parallel testing
+powered by a recent version of prove(1):
+
+ $ prove --timer --jobs 15 ./t[0-9]*.sh
+ [19:17:33] ./t0005-signals.sh ................................... ok 36 ms
+ [19:17:33] ./t0022-crlf-rename.sh ............................... ok 69 ms
+ [19:17:33] ./t0024-crlf-archive.sh .............................. ok 154 ms
+ [19:17:33] ./t0004-unwritable.sh ................................ ok 289 ms
+ [19:17:33] ./t0002-gitfile.sh ................................... ok 480 ms
+ ===( 102;0 25/? 6/? 5/? 16/? 1/? 4/? 2/? 1/? 3/? 1... )===
+
+prove and other harnesses come with a lot of useful options. The
+--state option in particular is very useful:
+
+ # Repeat until no more failures
+ $ prove -j 15 --state=failed,save ./t[0-9]*.sh
+
+You can also run each test individually from command line, like this:
+
+ $ sh ./t3010-ls-files-killed-modified.sh
+ ok 1 - git update-index --add to add various paths.
+ ok 2 - git ls-files -k to show killed files.
+ ok 3 - validate git ls-files -k output.
+ ok 4 - git ls-files -m to show modified files.
+ ok 5 - validate git ls-files -m output.
+ # passed all 5 test(s)
+ 1..5
+
+You can pass --verbose (or -v), --debug (or -d), and --immediate
+(or -i) command line argument to the test, or by setting GIT_TEST_OPTS
+appropriately before running "make".
+
+--verbose::
+ This makes the test more verbose. Specifically, the
+ command being run and their output if any are also
+ output.
+
+--debug::
+ This may help the person who is developing a new test.
+ It causes the command defined with test_debug to run.
+
+--immediate::
+ This causes the test to immediately exit upon the first
+ failed test.
+
+--long-tests::
+ This causes additional long-running tests to be run (where
+ available), for more exhaustive testing.
+
+--valgrind::
+ Execute all Git binaries with valgrind and exit with status
+ 126 on errors (just like regular tests, this will only stop
+ the test script when running under -i). Valgrind errors
+ go to stderr, so you might want to pass the -v option, too.
+
+ Since it makes no sense to run the tests with --valgrind and
+ not see any output, this option implies --verbose. For
+ convenience, it also implies --tee.
+
+--tee::
+ In addition to printing the test output to the terminal,
+ write it to files named 't/test-results/$TEST_NAME.out'.
+ As the names depend on the tests' file names, it is safe to
+ run the tests with this option in parallel.
+
+--with-dashes::
+ By default tests are run without dashed forms of
+ commands (like git-commit) in the PATH (it only uses
+ wrappers from ../bin-wrappers). Use this option to include
+ the build directory (..) in the PATH, which contains all
+ the dashed forms of commands. This option is currently
+ implied by other options like --valgrind and
+ GIT_TEST_INSTALLED.
+
+--root=<directory>::
+ Create "trash" directories used to store all temporary data during
+ testing under <directory>, instead of the t/ directory.
+ Using this option with a RAM-based filesystem (such as tmpfs)
+ can massively speed up the test suite.
+
+You can also set the GIT_TEST_INSTALLED environment variable to
+the bindir of an existing git installation to test that installation.
+You still need to have built this git sandbox, from which various
+test-* support programs, templates, and perl libraries are used.
+If your installed git is incomplete, it will silently test parts of
+your built version instead.
+
+When using GIT_TEST_INSTALLED, you can also set GIT_TEST_EXEC_PATH to
+override the location of the dashed-form subcommands (what
+GIT_EXEC_PATH would be used for during normal operation).
+GIT_TEST_EXEC_PATH defaults to `$GIT_TEST_INSTALLED/git --exec-path`.
+
+
+Skipping Tests
+--------------
+
+In some environments, certain tests have no way of succeeding
+due to platform limitation, such as lack of 'unzip' program, or
+filesystem that do not allow arbitrary sequence of non-NUL bytes
+as pathnames.
+
+You should be able to say something like
+
+ $ GIT_SKIP_TESTS=t9200.8 sh ./t9200-git-cvsexport-commit.sh
+
+and even:
+
+ $ GIT_SKIP_TESTS='t[0-4]??? t91?? t9200.8' make
+
+to omit such tests. The value of the environment variable is a
+SP separated list of patterns that tells which tests to skip,
+and either can match the "t[0-9]{4}" part to skip the whole
+test, or t[0-9]{4} followed by ".$number" to say which
+particular test to skip.
+
+Note that some tests in the existing test suite rely on previous
+test item, so you cannot arbitrarily disable one and expect the
+remainder of test to check what the test originally was intended
+to check.
+
+
+Naming Tests
+------------
+
+The test files are named as:
+
+ tNNNN-commandname-details.sh
+
+where N is a decimal digit.
+
+First digit tells the family:
+
+ 0 - the absolute basics and global stuff
+ 1 - the basic commands concerning database
+ 2 - the basic commands concerning the working tree
+ 3 - the other basic commands (e.g. ls-files)
+ 4 - the diff commands
+ 5 - the pull and exporting commands
+ 6 - the revision tree commands (even e.g. merge-base)
+ 7 - the porcelainish commands concerning the working tree
+ 8 - the porcelainish commands concerning forensics
+ 9 - the git tools
+
+Second digit tells the particular command we are testing.
+
+Third digit (optionally) tells the particular switch or group of switches
+we are testing.
+
+If you create files under t/ directory (i.e. here) that is not
+the top-level test script, never name the file to match the above
+pattern. The Makefile here considers all such files as the
+top-level test script and tries to run all of them. A care is
+especially needed if you are creating a common test library
+file, similar to test-lib.sh, because such a library file may
+not be suitable for standalone execution.
+
+
+Writing Tests
+-------------
+
+The test script is written as a shell script. It should start
+with the standard "#!/bin/sh" with copyright notices, and an
+assignment to variable 'test_description', like this:
+
+ #!/bin/sh
+ #
+ # Copyright (c) 2005 Junio C Hamano
+ #
+
+ test_description='xxx test (option --frotz)
+
+ This test registers the following structure in the cache
+ and tries to run git-ls-files with option --frotz.'
+
+
+Source 'test-lib.sh'
+--------------------
+
+After assigning test_description, the test script should source
+test-lib.sh like this:
+
+ . ./test-lib.sh
+
+This test harness library does the following things:
+
+ - If the script is invoked with command line argument --help
+ (or -h), it shows the test_description and exits.
+
+ - Creates an empty test directory with an empty .git/objects database
+ and chdir(2) into it. This directory is 't/trash
+ directory.$test_name_without_dotsh', with t/ subject to change by
+ the --root option documented above.
+
+ - Defines standard test helper functions for your scripts to
+ use. These functions are designed to make all scripts behave
+ consistently when command line arguments --verbose (or -v),
+ --debug (or -d), and --immediate (or -i) is given.
+
+Do's, don'ts & things to keep in mind
+-------------------------------------
+
+Here are a few examples of things you probably should and shouldn't do
+when writing tests.
+
+Do:
+
+ - Put all code inside test_expect_success and other assertions.
+
+ Even code that isn't a test per se, but merely some setup code
+ should be inside a test assertion.
+
+ - Chain your test assertions
+
+ Write test code like this:
+
+ git merge foo &&
+ git push bar &&
+ test ...
+
+ Instead of:
+
+ git merge hla
+ git push gh
+ test ...
+
+ That way all of the commands in your tests will succeed or fail. If
+ you must ignore the return value of something (e.g., the return
+ after unsetting a variable that was already unset is unportable) it's
+ best to indicate so explicitly with a semicolon:
+
+ unset HLAGH;
+ git merge hla &&
+ git push gh &&
+ test ...
+
+Don't:
+
+ - exit() within a <script> part.
+
+ The harness will catch this as a programming error of the test.
+ Use test_done instead if you need to stop the tests early (see
+ "Skipping tests" below).
+
+ - Break the TAP output
+
+ The raw output from your test may be interpreted by a TAP harness. TAP
+ harnesses will ignore everything they don't know about, but don't step
+ on their toes in these areas:
+
+ - Don't print lines like "$x..$y" where $x and $y are integers.
+
+ - Don't print lines that begin with "ok" or "not ok".
+
+ TAP harnesses expect a line that begins with either "ok" and "not
+ ok" to signal a test passed or failed (and our harness already
+ produces such lines), so your script shouldn't emit such lines to
+ their output.
+
+ You can glean some further possible issues from the TAP grammar
+ (see http://search.cpan.org/perldoc?TAP::Parser::Grammar#TAP_Grammar)
+ but the best indication is to just run the tests with prove(1),
+ it'll complain if anything is amiss.
+
+Keep in mind:
+
+ - Inside <script> part, the standard output and standard error
+ streams are discarded, and the test harness only reports "ok" or
+ "not ok" to the end user running the tests. Under --verbose, they
+ are shown to help debugging the tests.
+
+
+Skipping tests
+--------------
+
+If you need to skip all the remaining tests you should set skip_all
+and immediately call test_done. The string you give to skip_all will
+be used as an explanation for why the test was skipped. for instance:
+
+ if ! test_have_prereq PERL
+ then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+ fi
+
+End with test_done
+------------------
+
+Your script will be a sequence of tests, using helper functions
+from the test harness library. At the end of the script, call
+'test_done'.
+
+
+Test harness library
+--------------------
+
+There are a handful helper functions defined in the test harness
+library for your script to use.
+
+ - test_expect_success [<prereq>] <message> <script>
+
+ Usually takes two strings as parameter, and evaluates the
+ <script>. If it yields success, test is considered
+ successful. <message> should state what it is testing.
+
+ Example:
+
+ test_expect_success \
+ 'git-write-tree should be able to write an empty tree.' \
+ 'tree=$(git-write-tree)'
+
+ If you supply three parameters the first will be taken to be a
+ prerequisite, see the test_set_prereq and test_have_prereq
+ documentation below:
+
+ test_expect_success TTY 'git --paginate rev-list uses a pager' \
+ ' ... '
+
+ - test_expect_failure [<prereq>] <message> <script>
+
+ This is NOT the opposite of test_expect_success, but is used
+ to mark a test that demonstrates a known breakage. Unlike
+ the usual test_expect_success tests, which say "ok" on
+ success and "FAIL" on failure, this will say "FIXED" on
+ success and "still broken" on failure. Failures from these
+ tests won't cause -i (immediate) to stop.
+
+ Like test_expect_success this function can optionally use a three
+ argument invocation with a prerequisite as the first argument.
+
+ - test_expect_code [<prereq>] <code> <message> <script>
+
+ Analogous to test_expect_success, but pass the test if it exits
+ with a given exit <code>
+
+ test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
+
+ - test_debug <script>
+
+ This takes a single argument, <script>, and evaluates it only
+ when the test script is started with --debug command line
+ argument. This is primarily meant for use during the
+ development of a new test script.
+
+ - test_done
+
+ Your test script must have test_done at the end. Its purpose
+ is to summarize successes and failures in the test script and
+ exit with an appropriate error code.
+
+ - test_tick
+
+ Make commit and tag names consistent by setting the author and
+ committer times to defined stated. Subsequent calls will
+ advance the times by a fixed amount.
+
+ - test_commit <message> [<filename> [<contents>]]
+
+ Creates a commit with the given message, committing the given
+ file with the given contents (default for both is to reuse the
+ message string), and adds a tag (again reusing the message
+ string as name). Calls test_tick to make the SHA-1s
+ reproducible.
+
+ - test_merge <message> <commit-or-tag>
+
+ Merges the given rev using the given message. Like test_commit,
+ creates a tag and calls test_tick before committing.
+
+ - test_set_prereq SOME_PREREQ
+
+ Set a test prerequisite to be used later with test_have_prereq. The
+ test-lib will set some prerequisites for you, e.g. PERL and PYTHON
+ which are derived from ./GIT-BUILD-OPTIONS (grep test_set_prereq
+ test-lib.sh for more). Others you can set yourself and use later
+ with either test_have_prereq directly, or the three argument
+ invocation of test_expect_success and test_expect_failure.
+
+ - test_have_prereq SOME PREREQ
+
+ Check if we have a prerequisite previously set with
+ test_set_prereq. The most common use of this directly is to skip
+ all the tests if we don't have some essential prerequisite:
+
+ if ! test_have_prereq PERL
+ then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+ fi
+
+ - test_external [<prereq>] <message> <external> <script>
+
+ Execute a <script> with an <external> interpreter (like perl). This
+ was added for tests like t9700-perl-git.sh which do most of their
+ work in an external test script.
+
+ test_external \
+ 'GitwebCache::*FileCache*' \
+ "$PERL_PATH" "$TEST_DIRECTORY"/t9503/test_cache_interface.pl
+
+ If the test is outputting its own TAP you should set the
+ test_external_has_tap variable somewhere before calling the first
+ test_external* function. See t9700-perl-git.sh for an example.
+
+ # The external test will outputs its own plan
+ test_external_has_tap=1
+
+ - test_external_without_stderr [<prereq>] <message> <external> <script>
+
+ Like test_external but fail if there's any output on stderr,
+ instead of checking the exit code.
+
+ test_external_without_stderr \
+ 'Perl API' \
+ "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
+
+ - test_must_fail <git-command>
+
+ Run a git command and ensure it fails in a controlled way. Use
+ this instead of "! <git-command>". When git-command dies due to a
+ segfault, test_must_fail diagnoses it as an error; "! <git-command>"
+ treats it as just another expected failure, which would let such a
+ bug go unnoticed.
+
+ - test_might_fail <git-command>
+
+ Similar to test_must_fail, but tolerate success, too. Use this
+ instead of "<git-command> || :" to catch failures due to segv.
+
+ - test_cmp <expected> <actual>
+
+ Check whether the content of the <actual> file matches the
+ <expected> file. This behaves like "cmp" but produces more
+ helpful output when the test is run with "-v" option.
+
+ - test_when_finished <script>
+
+ Prepend <script> to a list of commands to run to clean up
+ at the end of the current test. If some clean-up command
+ fails, the test will not pass.
+
+ Example:
+
+ test_expect_success 'branch pointing to non-commit' '
+ git rev-parse HEAD^{tree} >.git/refs/heads/invalid &&
+ test_when_finished "git update-ref -d refs/heads/invalid" &&
+ ...
+ '
+
+
+Tips for Writing Tests
+----------------------
+
+As with any programming projects, existing programs are the best
+source of the information. However, do _not_ emulate
+t0000-basic.sh when writing your tests. The test is special in
+that it tries to validate the very core of GIT. For example, it
+knows that there will be 256 subdirectories under .git/objects/,
+and it knows that the object ID of an empty tree is a certain
+40-byte string. This is deliberately done so in t0000-basic.sh
+because the things the very basic core test tries to achieve is
+to serve as a basis for people who are changing the GIT internal
+drastically. For these people, after making certain changes,
+not seeing failures from the basic test _is_ a failure. And
+such drastic changes to the core GIT that even changes these
+otherwise supposedly stable object IDs should be accompanied by
+an update to t0000-basic.sh.
+
+However, other tests that simply rely on basic parts of the core
+GIT working properly should not have that level of intimate
+knowledge of the core GIT internals. If all the test scripts
+hardcoded the object IDs like t0000-basic.sh does, that defeats
+the purpose of t0000-basic.sh, which is to isolate that level of
+validation in one place. Your test also ends up needing
+updating when such a change to the internal happens, so do _not_
+do it and leave the low level of validation to t0000-basic.sh.
diff --git a/t/aggregate-results.sh b/t/aggregate-results.sh
new file mode 100755
index 0000000..d206b7c
--- /dev/null
+++ b/t/aggregate-results.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+fixed=0
+success=0
+failed=0
+broken=0
+total=0
+
+while read file
+do
+ while read type value
+ do
+ case $type in
+ '')
+ continue ;;
+ fixed)
+ fixed=$(($fixed + $value)) ;;
+ success)
+ success=$(($success + $value)) ;;
+ failed)
+ failed=$(($failed + $value)) ;;
+ broken)
+ broken=$(($broken + $value)) ;;
+ total)
+ total=$(($total + $value)) ;;
+ esac
+ done <"$file"
+done
+
+printf "%-8s%d\n" fixed $fixed
+printf "%-8s%d\n" success $success
+printf "%-8s%d\n" failed $failed
+printf "%-8s%d\n" broken $broken
+printf "%-8s%d\n" total $total
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
new file mode 100644
index 0000000..396b965
--- /dev/null
+++ b/t/annotate-tests.sh
@@ -0,0 +1,126 @@
+# This file isn't used as a test script directly, instead it is
+# sourced from t8001-annotate.sh and t8001-blame.sh.
+
+check_count () {
+ head=
+ case "$1" in -h) head="$2"; shift; shift ;; esac
+ echo "$PROG file $head" >&4
+ $PROG file $head >.result || return 1
+ cat .result | perl -e '
+ my %expect = (@ARGV);
+ my %count = ();
+ while (<STDIN>) {
+ if (/^[0-9a-f]+\t\(([^\t]+)\t/) {
+ my $author = $1;
+ for ($author) { s/^\s*//; s/\s*$//; }
+ if (exists $expect{$author}) {
+ $count{$author}++;
+ }
+ }
+ }
+ my $bad = 0;
+ while (my ($author, $count) = each %count) {
+ my $ok;
+ if ($expect{$author} != $count) {
+ $bad = 1;
+ $ok = "bad";
+ }
+ else {
+ $ok = "good";
+ }
+ print STDERR "Author $author (expected $expect{$author}, attributed $count) $ok\n";
+ }
+ exit($bad);
+ ' "$@"
+}
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'echo "1A quick brown fox jumps over the" >file &&
+ echo "lazy dog" >>file &&
+ git add file
+ GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
+
+test_expect_success \
+ 'check all lines blamed on A' \
+ 'check_count A 2'
+
+test_expect_success \
+ 'Setup new lines blamed on B' \
+ 'echo "2A quick brown fox jumps over the" >>file &&
+ echo "lazy dog" >> file &&
+ GIT_AUTHOR_NAME="B" git commit -a -m "Second."'
+
+test_expect_success \
+ 'Two lines blamed on A, two on B' \
+ 'check_count A 2 B 2'
+
+test_expect_success \
+ 'merge-setup part 1' \
+ 'git checkout -b branch1 master &&
+ echo "3A slow green fox jumps into the" >> file &&
+ echo "well." >> file &&
+ GIT_AUTHOR_NAME="B1" git commit -a -m "Branch1-1"'
+
+test_expect_success \
+ 'Two lines blamed on A, two on B, two on B1' \
+ 'check_count A 2 B 2 B1 2'
+
+test_expect_success \
+ 'merge-setup part 2' \
+ 'git checkout -b branch2 master &&
+ sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new &&
+ mv file.new file &&
+ GIT_AUTHOR_NAME="B2" git commit -a -m "Branch2-1"'
+
+test_expect_success \
+ 'Two lines blamed on A, one on B, one on B2' \
+ 'check_count A 2 B 1 B2 1'
+
+test_expect_success \
+ 'merge-setup part 3' \
+ 'git pull . branch1'
+
+test_expect_success \
+ 'Two lines blamed on A, one on B, two on B1, one on B2' \
+ 'check_count A 2 B 1 B1 2 B2 1'
+
+test_expect_success \
+ 'Annotating an old revision works' \
+ 'check_count -h master A 2 B 2'
+
+test_expect_success \
+ 'Annotating an old revision works' \
+ 'check_count -h master^ A 2'
+
+test_expect_success \
+ 'merge-setup part 4' \
+ 'echo "evil merge." >>file &&
+ git commit -a --amend'
+
+test_expect_success \
+ 'Two lines blamed on A, one on B, two on B1, one on B2, one on A U Thor' \
+ 'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1'
+
+test_expect_success \
+ 'an incomplete line added' \
+ 'echo "incomplete" | tr -d "\\012" >>file &&
+ GIT_AUTHOR_NAME="C" git commit -a -m "Incomplete"'
+
+test_expect_success \
+ 'With incomplete lines.' \
+ 'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1'
+
+test_expect_success \
+ 'some edit' \
+ 'mv file file.orig &&
+ {
+ cat file.orig &&
+ echo
+ } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" > file &&
+ echo "incomplete" | tr -d "\\012" >>file &&
+ GIT_AUTHOR_NAME="D" git commit -a -m "edit"'
+
+test_expect_success \
+ 'some edit' \
+ 'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1'
diff --git a/t/diff-lib.sh b/t/diff-lib.sh
new file mode 100644
index 0000000..75a35fc
--- /dev/null
+++ b/t/diff-lib.sh
@@ -0,0 +1,39 @@
+:
+
+sanitize_diff_raw='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]* / X X \1# /'
+compare_diff_raw () {
+ # When heuristics are improved, the score numbers would change.
+ # Ignore them while comparing.
+ # Also we do not check SHA1 hash generation in this test, which
+ # is a job for t0000-basic.sh
+
+ sed -e "$sanitize_diff_raw" <"$1" >.tmp-1
+ sed -e "$sanitize_diff_raw" <"$2" >.tmp-2
+ test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+sanitize_diff_raw_z='/^:/s/ '"$_x40"' '"$_x40"' \([A-Z]\)[0-9]*$/ X X \1#/'
+compare_diff_raw_z () {
+ # When heuristics are improved, the score numbers would change.
+ # Ignore them while comparing.
+ # Also we do not check SHA1 hash generation in this test, which
+ # is a job for t0000-basic.sh
+
+ perl -pe 'y/\000/\012/' <"$1" | sed -e "$sanitize_diff_raw_z" >.tmp-1
+ perl -pe 'y/\000/\012/' <"$2" | sed -e "$sanitize_diff_raw_z" >.tmp-2
+ test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
+
+compare_diff_patch () {
+ # When heuristics are improved, the score numbers would change.
+ # Ignore them while comparing.
+ sed -e '
+ /^[dis]*imilarity index [0-9]*%$/d
+ /^index [0-9a-f]*\.\.[0-9a-f]/d
+ ' <"$1" >.tmp-1
+ sed -e '
+ /^[dis]*imilarity index [0-9]*%$/d
+ /^index [0-9a-f]*\.\.[0-9a-f]/d
+ ' <"$2" >.tmp-2
+ test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2
+}
diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh
new file mode 100644
index 0000000..81ef2a0
--- /dev/null
+++ b/t/gitweb-lib.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Jakub Narebski
+#
+
+gitweb_init () {
+ safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
+ cat >gitweb_config.perl <<EOF
+#!/usr/bin/perl
+
+# gitweb configuration for tests
+
+our \$version = 'current';
+our \$GIT = 'git';
+our \$projectroot = "$safe_pwd";
+our \$project_maxdepth = 8;
+our \$home_link_str = 'projects';
+our \$site_name = '[localhost]';
+our \$site_header = '';
+our \$site_footer = '';
+our \$home_text = 'indextext.html';
+our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/static/gitweb.css');
+our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/static/git-logo.png';
+our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/static/git-favicon.png';
+our \$projects_list = '';
+our \$export_ok = '';
+our \$strict_export = '';
+our \$maxload = undef;
+
+EOF
+
+ cat >.git/description <<EOF
+$0 test repository
+EOF
+}
+
+gitweb_run () {
+ GATEWAY_INTERFACE='CGI/1.1'
+ HTTP_ACCEPT='*/*'
+ REQUEST_METHOD='GET'
+ SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
+ QUERY_STRING=""$1""
+ PATH_INFO=""$2""
+ export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
+ SCRIPT_NAME QUERY_STRING PATH_INFO
+
+ GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+ export GITWEB_CONFIG
+
+ # some of git commands write to STDERR on error, but this is not
+ # written to web server logs, so we are not interested in that:
+ # we are interested only in properly formatted errors/warnings
+ rm -f gitweb.log &&
+ perl -- "$SCRIPT_NAME" \
+ >gitweb.output 2>gitweb.log &&
+ perl -w -e '
+ open O, ">gitweb.headers";
+ while (<>) {
+ print O;
+ last if (/^\r$/ || /^$/);
+ }
+ open O, ">gitweb.body";
+ while (<>) {
+ print O;
+ }
+ close O;
+ ' gitweb.output &&
+ if grep '^[[]' gitweb.log >/dev/null 2>&1; then false; else true; fi
+
+ # gitweb.log is left for debugging
+ # gitweb.output is used to parse HTTP output
+ # gitweb.headers contains only HTTP headers
+ # gitweb.body contains body of message, without headers
+}
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping gitweb tests, perl not available'
+ test_done
+fi
+
+perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
+ skip_all='skipping gitweb tests, perl version is too old'
+ test_done
+}
+
+gitweb_init
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
new file mode 100644
index 0000000..648d161
--- /dev/null
+++ b/t/lib-cvs.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+. ./test-lib.sh
+
+unset CVS_SERVER
+# for clean cvsps cache
+HOME=$(pwd)
+export HOME
+
+if ! type cvs >/dev/null 2>&1
+then
+ skip_all='skipping cvsimport tests, cvs not found'
+ test_done
+fi
+
+CVS="cvs -f"
+export CVS
+
+cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
+case "$cvsps_version" in
+2.1 | 2.2*)
+ ;;
+'')
+ skip_all='skipping cvsimport tests, cvsps not found'
+ test_done
+ ;;
+*)
+ skip_all='skipping cvsimport tests, unsupported cvsps version'
+ test_done
+ ;;
+esac
+
+test_cvs_co () {
+ # Usage: test_cvs_co BRANCH_NAME
+ rm -rf module-cvs-"$1"
+ if [ "$1" = "master" ]
+ then
+ $CVS co -P -d module-cvs-"$1" -A module
+ else
+ $CVS co -P -d module-cvs-"$1" -r "$1" module
+ fi
+}
+
+test_git_co () {
+ # Usage: test_git_co BRANCH_NAME
+ (cd module-git && git checkout "$1")
+}
+
+test_cmp_branch_file () {
+ # Usage: test_cmp_branch_file BRANCH_NAME PATH
+ # The branch must already be checked out of CVS and git.
+ test_cmp module-cvs-"$1"/"$2" module-git/"$2"
+}
+
+test_cmp_branch_tree () {
+ # Usage: test_cmp_branch_tree BRANCH_NAME
+ # Check BRANCH_NAME out of CVS and git and make sure that all
+ # of the files and directories are identical.
+
+ test_cvs_co "$1" &&
+ test_git_co "$1" &&
+ (
+ cd module-cvs-"$1"
+ find . -type d -name CVS -prune -o -type f -print
+ ) | sort >module-cvs-"$1".list &&
+ (
+ cd module-git
+ find . -type d -name .git -prune -o -type f -print
+ ) | sort >module-git-"$1".list &&
+ test_cmp module-cvs-"$1".list module-git-"$1".list &&
+ cat module-cvs-"$1".list | while read f
+ do
+ test_cmp_branch_file "$1" "$f" || return 1
+ done
+}
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
new file mode 100644
index 0000000..c3f6676
--- /dev/null
+++ b/t/lib-git-svn.sh
@@ -0,0 +1,172 @@
+. ./test-lib.sh
+
+remotes_git_svn=remotes/git""-svn
+git_svn_id=git""-svn-id
+
+if test -n "$NO_SVN_TESTS"
+then
+ skip_all='skipping git svn tests, NO_SVN_TESTS defined'
+ test_done
+fi
+if ! test_have_prereq PERL; then
+ skip_all='skipping git svn tests, perl not available'
+ test_done
+fi
+
+GIT_DIR=$PWD/.git
+GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
+SVN_TREE=$GIT_SVN_DIR/svn-tree
+PERL=${PERL:-perl}
+
+svn >/dev/null 2>&1
+if test $? -ne 1
+then
+ skip_all='skipping git svn tests, svn not found'
+ test_done
+fi
+
+svnrepo=$PWD/svnrepo
+export svnrepo
+svnconf=$PWD/svnconf
+export svnconf
+
+$PERL -w -e "
+use SVN::Core;
+use SVN::Repos;
+\$SVN::Core::VERSION gt '1.1.0' or exit(42);
+system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
+" >&3 2>&4
+x=$?
+if test $x -ne 0
+then
+ if test $x -eq 42; then
+ skip_all='Perl SVN libraries must be >= 1.1.0'
+ elif test $x -eq 41; then
+ skip_all='svnadmin failed to create fsfs repository'
+ else
+ skip_all='Perl SVN libraries not found or unusable'
+ fi
+ test_done
+fi
+
+rawsvnrepo="$svnrepo"
+svnrepo="file://$svnrepo"
+
+poke() {
+ test-chmtime +1 "$1"
+}
+
+# We need this, because we should pass empty configuration directory to
+# the 'svn commit' to avoid automated property changes and other stuff
+# that could be set from user's configuration files in ~/.subversion.
+svn_cmd () {
+ [ -d "$svnconf" ] || mkdir "$svnconf"
+ orig_svncmd="$1"; shift
+ if [ -z "$orig_svncmd" ]; then
+ svn
+ return
+ fi
+ svn "$orig_svncmd" --config-dir "$svnconf" "$@"
+}
+
+for d in \
+ "$SVN_HTTPD_PATH" \
+ /usr/sbin/apache2 \
+ /usr/sbin/httpd \
+; do
+ if test -f "$d"
+ then
+ SVN_HTTPD_PATH="$d"
+ break
+ fi
+done
+for d in \
+ "$SVN_HTTPD_MODULE_PATH" \
+ /usr/lib/apache2/modules \
+ /usr/libexec/apache2 \
+; do
+ if test -d "$d"
+ then
+ SVN_HTTPD_MODULE_PATH="$d"
+ break
+ fi
+done
+
+start_httpd () {
+ repo_base_path="$1"
+ if test -z "$SVN_HTTPD_PORT"
+ then
+ echo >&2 'SVN_HTTPD_PORT is not defined!'
+ return
+ fi
+ if test -z "$repo_base_path"
+ then
+ repo_base_path=svn
+ fi
+
+ mkdir "$GIT_DIR"/logs
+
+ cat > "$GIT_DIR/httpd.conf" <<EOF
+ServerName "git svn test"
+ServerRoot "$GIT_DIR"
+DocumentRoot "$GIT_DIR"
+PidFile "$GIT_DIR/httpd.pid"
+LockFile logs/accept.lock
+Listen 127.0.0.1:$SVN_HTTPD_PORT
+LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
+LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
+<Location /$repo_base_path>
+ DAV svn
+ SVNPath "$rawsvnrepo"
+</Location>
+EOF
+ "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
+ svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path"
+}
+
+stop_httpd () {
+ test -z "$SVN_HTTPD_PORT" && return
+ "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
+}
+
+convert_to_rev_db () {
+ $PERL -w -- - "$@" <<\EOF
+use strict;
+@ARGV == 2 or die "Usage: convert_to_rev_db <input> <output>";
+open my $wr, '+>', $ARGV[1] or die "$!: couldn't open: $ARGV[1]";
+open my $rd, '<', $ARGV[0] or die "$!: couldn't open: $ARGV[0]";
+my $size = (stat($rd))[7];
+($size % 24) == 0 or die "Inconsistent size: $size";
+while (sysread($rd, my $buf, 24) == 24) {
+ my ($r, $c) = unpack('NH40', $buf);
+ my $offset = $r * 41;
+ seek $wr, 0, 2 or die $!;
+ my $pos = tell $wr;
+ if ($pos < $offset) {
+ for (1 .. (($offset - $pos) / 41)) {
+ print $wr (('0' x 40),"\n") or die $!;
+ }
+ }
+ seek $wr, $offset, 0 or die $!;
+ print $wr $c,"\n" or die $!;
+}
+close $wr or die $!;
+close $rd or die $!;
+EOF
+}
+
+require_svnserve () {
+ if test -z "$SVNSERVE_PORT"
+ then
+ skip_all='skipping svnserve test. (set $SVNSERVE_PORT to enable)'
+ test_done
+ fi
+}
+
+start_svnserve () {
+ svnserve --listen-port $SVNSERVE_PORT \
+ --root "$rawsvnrepo" \
+ --listen-once \
+ --listen-host 127.0.0.1 &
+}
+
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
new file mode 100644
index 0000000..e733f65
--- /dev/null
+++ b/t/lib-httpd.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+if test -z "$GIT_TEST_HTTPD"
+then
+ skip_all="Network testing disabled (define GIT_TEST_HTTPD to enable)"
+ test_done
+fi
+
+HTTPD_PARA=""
+
+for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' '/usr/sbin/apache2'
+do
+ if test -x "$DEFAULT_HTTPD_PATH"
+ then
+ break
+ fi
+done
+
+for DEFAULT_HTTPD_MODULE_PATH in '/usr/libexec/apache2' \
+ '/usr/lib/apache2/modules' \
+ '/usr/lib64/httpd/modules' \
+ '/usr/lib/httpd/modules'
+do
+ if test -d "$DEFAULT_HTTPD_MODULE_PATH"
+ then
+ break
+ fi
+done
+
+case $(uname) in
+ Darwin)
+ HTTPD_PARA="$HTTPD_PARA -DDarwin"
+ ;;
+esac
+
+LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"}
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'}
+
+TEST_PATH="$TEST_DIRECTORY"/lib-httpd
+HTTPD_ROOT_PATH="$PWD"/httpd
+HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www
+
+if ! test -x "$LIB_HTTPD_PATH"
+then
+ skip_all="skipping test, no web server found at '$LIB_HTTPD_PATH'"
+ test_done
+fi
+
+HTTPD_VERSION=`$LIB_HTTPD_PATH -v | \
+ sed -n 's/^Server version: Apache\/\([0-9]*\)\..*$/\1/p; q'`
+
+if test -n "$HTTPD_VERSION"
+then
+ if test -z "$LIB_HTTPD_MODULE_PATH"
+ then
+ if ! test $HTTPD_VERSION -ge 2
+ then
+ skip_all="skipping test, at least Apache version 2 is required"
+ test_done
+ fi
+ if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
+ then
+ skip_all="Apache module directory not found. Skipping tests."
+ test_done
+ fi
+
+ LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH"
+ fi
+else
+ error "Could not identify web server at '$LIB_HTTPD_PATH'"
+fi
+
+prepare_httpd() {
+ mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
+
+ ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
+
+ if test -n "$LIB_HTTPD_SSL"
+ then
+ HTTPD_URL=https://127.0.0.1:$LIB_HTTPD_PORT
+
+ RANDFILE_PATH="$HTTPD_ROOT_PATH"/.rnd openssl req \
+ -config "$TEST_PATH/ssl.cnf" \
+ -new -x509 -nodes \
+ -out "$HTTPD_ROOT_PATH/httpd.pem" \
+ -keyout "$HTTPD_ROOT_PATH/httpd.pem"
+ GIT_SSL_NO_VERIFY=t
+ export GIT_SSL_NO_VERIFY
+ HTTPD_PARA="$HTTPD_PARA -DSSL"
+ else
+ HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT
+ fi
+
+ if test -n "$LIB_HTTPD_DAV" -o -n "$LIB_HTTPD_SVN"
+ then
+ HTTPD_PARA="$HTTPD_PARA -DDAV"
+
+ if test -n "$LIB_HTTPD_SVN"
+ then
+ HTTPD_PARA="$HTTPD_PARA -DSVN"
+ rawsvnrepo="$HTTPD_ROOT_PATH/svnrepo"
+ svnrepo="http://127.0.0.1:$LIB_HTTPD_PORT/svn"
+ fi
+ fi
+}
+
+start_httpd() {
+ prepare_httpd >&3 2>&4
+
+ trap 'code=$?; stop_httpd; (exit $code); die' EXIT
+
+ "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
+ -f "$TEST_PATH/apache.conf" $HTTPD_PARA \
+ -c "Listen 127.0.0.1:$LIB_HTTPD_PORT" -k start \
+ >&3 2>&4
+ if test $? -ne 0
+ then
+ skip_all="skipping test, web server setup failed"
+ trap 'die' EXIT
+ test_done
+ fi
+}
+
+stop_httpd() {
+ trap 'die' EXIT
+
+ "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
+ -f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
+}
+
+test_http_push_nonff() {
+ REMOTE_REPO=$1
+ LOCAL_REPO=$2
+ BRANCH=$3
+
+ test_expect_success 'non-fast-forward push fails' '
+ cd "$REMOTE_REPO" &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+
+ cd "$LOCAL_REPO" &&
+ git checkout $BRANCH &&
+ echo "changed" > path2 &&
+ git commit -a -m path2 --amend &&
+
+ test_must_fail git push -v origin >output 2>&1 &&
+ (cd "$REMOTE_REPO" &&
+ test $HEAD = $(git rev-parse --verify HEAD))
+ '
+
+ test_expect_success 'non-fast-forward push show ref status' '
+ grep "^ ! \[rejected\][ ]*$BRANCH -> $BRANCH (non-fast-forward)$" output
+ '
+
+ test_expect_success 'non-fast-forward push shows help message' '
+ grep "To prevent you from losing history, non-fast-forward updates were rejected" \
+ output
+ '
+}
diff --git a/t/lib-httpd/apache.conf b/t/lib-httpd/apache.conf
new file mode 100644
index 0000000..4961505
--- /dev/null
+++ b/t/lib-httpd/apache.conf
@@ -0,0 +1,68 @@
+ServerName dummy
+LockFile accept.lock
+PidFile httpd.pid
+DocumentRoot www
+LogFormat "%h %l %u %t \"%r\" %>s %b" common
+CustomLog access.log common
+ErrorLog error.log
+<IfModule !mod_log_config.c>
+ LoadModule log_config_module modules/mod_log_config.so
+</IfModule>
+<IfModule !mod_alias.c>
+ LoadModule alias_module modules/mod_alias.so
+</IfModule>
+<IfModule !mod_cgi.c>
+ LoadModule cgi_module modules/mod_cgi.so
+</IfModule>
+<IfModule !mod_env.c>
+ LoadModule env_module modules/mod_env.so
+</IfModule>
+
+Alias /dumb/ www/
+
+<Location /smart/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+ SetEnv GIT_HTTP_EXPORT_ALL
+</Location>
+<Location /smart_noexport/>
+ SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+</Location>
+ScriptAlias /smart/ ${GIT_EXEC_PATH}/git-http-backend/
+ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/
+<Directory ${GIT_EXEC_PATH}>
+ Options None
+</Directory>
+<Files ${GIT_EXEC_PATH}/git-http-backend>
+ Options ExecCGI
+</Files>
+
+<IfDefine SSL>
+LoadModule ssl_module modules/mod_ssl.so
+
+SSLCertificateFile httpd.pem
+SSLCertificateKeyFile httpd.pem
+SSLRandomSeed startup file:/dev/urandom 512
+SSLRandomSeed connect file:/dev/urandom 512
+SSLSessionCache none
+SSLMutex file:ssl_mutex
+SSLEngine On
+</IfDefine>
+
+<IfDefine DAV>
+ LoadModule dav_module modules/mod_dav.so
+ LoadModule dav_fs_module modules/mod_dav_fs.so
+
+ DAVLockDB DAVLock
+ <Location /dumb/>
+ Dav on
+ </Location>
+</IfDefine>
+
+<IfDefine SVN>
+ LoadModule dav_svn_module modules/mod_dav_svn.so
+
+ <Location /svn>
+ DAV svn
+ SVNPath svnrepo
+ </Location>
+</IfDefine>
diff --git a/t/lib-httpd/ssl.cnf b/t/lib-httpd/ssl.cnf
new file mode 100644
index 0000000..6dab257
--- /dev/null
+++ b/t/lib-httpd/ssl.cnf
@@ -0,0 +1,8 @@
+RANDFILE = $ENV::RANDFILE_PATH
+
+[ req ]
+default_bits = 1024
+distinguished_name = req_distinguished_name
+prompt = no
+[ req_distinguished_name ]
+commonName = 127.0.0.1
diff --git a/t/lib-pager.sh b/t/lib-pager.sh
new file mode 100644
index 0000000..ba03eab
--- /dev/null
+++ b/t/lib-pager.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+test_expect_success 'determine default pager' '
+ test_might_fail git config --unset core.pager &&
+ less=$(
+ unset PAGER GIT_PAGER;
+ git var GIT_PAGER
+ ) &&
+ test -n "$less"
+'
+
+if expr "$less" : '[a-z][a-z]*$' >/dev/null
+then
+ test_set_prereq SIMPLEPAGER
+fi
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
new file mode 100644
index 0000000..375e248
--- /dev/null
+++ b/t/lib-patch-mode.sh
@@ -0,0 +1,43 @@
+: included from t2016 and others
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping --patch tests, perl not available'
+ test_done
+fi
+
+set_state () {
+ echo "$3" > "$1" &&
+ git add "$1" &&
+ echo "$2" > "$1"
+}
+
+save_state () {
+ noslash="$(echo "$1" | tr / _)" &&
+ cat "$1" > _worktree_"$noslash" &&
+ git show :"$1" > _index_"$noslash"
+}
+
+set_and_save_state () {
+ set_state "$@" &&
+ save_state "$1"
+}
+
+verify_state () {
+ test "$(cat "$1")" = "$2" &&
+ test "$(git show :"$1")" = "$3"
+}
+
+verify_saved_state () {
+ noslash="$(echo "$1" | tr / _)" &&
+ verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
+}
+
+save_head () {
+ git rev-parse HEAD > _head
+}
+
+verify_saved_head () {
+ test "$(cat _head)" = "$(git rev-parse HEAD)"
+}
diff --git a/t/lib-read-tree-m-3way.sh b/t/lib-read-tree-m-3way.sh
new file mode 100644
index 0000000..168329a
--- /dev/null
+++ b/t/lib-read-tree-m-3way.sh
@@ -0,0 +1,158 @@
+: Included from t1000-read-tree-m-3way.sh and others
+# Original tree.
+mkdir Z
+for a in N D M
+do
+ for b in N D M
+ do
+ p=$a$b
+ echo This is $p from the original tree. >$p
+ echo This is Z/$p from the original tree. >Z/$p
+ test_expect_success \
+ "adding test file $p and Z/$p" \
+ 'git update-index --add $p &&
+ git update-index --add Z/$p'
+ done
+done
+echo This is SS from the original tree. >SS
+test_expect_success \
+ 'adding test file SS' \
+ 'git update-index --add SS'
+cat >TT <<\EOF
+This is a trivial merge sample text.
+Branch A is expected to upcase this word, here.
+There are some filler lines to avoid diff context
+conflicts here,
+like this one,
+and this one,
+and this one is yet another one of them.
+At the very end, here comes another line, that is
+the word, expected to be upcased by Branch B.
+This concludes the trivial merge sample file.
+EOF
+test_expect_success \
+ 'adding test file TT' \
+ 'git update-index --add TT'
+test_expect_success \
+ 'prepare initial tree' \
+ 'tree_O=$(git write-tree)'
+
+################################################################
+# Branch A and B makes the changes according to the above matrix.
+
+################################################################
+# Branch A
+
+to_remove=$(echo D? Z/D?)
+rm -f $to_remove
+test_expect_success \
+ 'change in branch A (removal)' \
+ 'git update-index --remove $to_remove'
+
+for p in M? Z/M?
+do
+ echo This is modified $p in the branch A. >$p
+ test_expect_success \
+ 'change in branch A (modification)' \
+ "git update-index $p"
+done
+
+for p in AN AA Z/AN Z/AA
+do
+ echo This is added $p in the branch A. >$p
+ test_expect_success \
+ 'change in branch A (addition)' \
+ "git update-index --add $p"
+done
+
+echo This is SS from the modified tree. >SS
+echo This is LL from the modified tree. >LL
+test_expect_success \
+ 'change in branch A (addition)' \
+ 'git update-index --add LL &&
+ git update-index SS'
+mv TT TT-
+sed -e '/Branch A/s/word/WORD/g' <TT- >TT
+rm -f TT-
+test_expect_success \
+ 'change in branch A (edit)' \
+ 'git update-index TT'
+
+mkdir DF
+echo Branch A makes a file at DF/DF, creating a directory DF. >DF/DF
+test_expect_success \
+ 'change in branch A (change file to directory)' \
+ 'git update-index --add DF/DF'
+
+test_expect_success \
+ 'recording branch A tree' \
+ 'tree_A=$(git write-tree)'
+
+################################################################
+# Branch B
+# Start from O
+
+rm -rf [NDMASLT][NDMASLT] Z DF
+mkdir Z
+test_expect_success \
+ 'reading original tree and checking out' \
+ 'git read-tree $tree_O &&
+ git checkout-index -a'
+
+to_remove=$(echo ?D Z/?D)
+rm -f $to_remove
+test_expect_success \
+ 'change in branch B (removal)' \
+ "git update-index --remove $to_remove"
+
+for p in ?M Z/?M
+do
+ echo This is modified $p in the branch B. >$p
+ test_expect_success \
+ 'change in branch B (modification)' \
+ "git update-index $p"
+done
+
+for p in NA AA Z/NA Z/AA
+do
+ echo This is added $p in the branch B. >$p
+ test_expect_success \
+ 'change in branch B (addition)' \
+ "git update-index --add $p"
+done
+echo This is SS from the modified tree. >SS
+echo This is LL from the modified tree. >LL
+test_expect_success \
+ 'change in branch B (addition and modification)' \
+ 'git update-index --add LL &&
+ git update-index SS'
+mv TT TT-
+sed -e '/Branch B/s/word/WORD/g' <TT- >TT
+rm -f TT-
+test_expect_success \
+ 'change in branch B (modification)' \
+ 'git update-index TT'
+
+echo Branch B makes a file at DF. >DF
+test_expect_success \
+ 'change in branch B (addition of a file to conflict with directory)' \
+ 'git update-index --add DF'
+
+test_expect_success \
+ 'recording branch B tree' \
+ 'tree_B=$(git write-tree)'
+
+test_expect_success \
+ 'keep contents of 3 trees for easy access' \
+ 'rm -f .git/index &&
+ git read-tree $tree_O &&
+ mkdir .orig-O &&
+ git checkout-index --prefix=.orig-O/ -f -q -a &&
+ rm -f .git/index &&
+ git read-tree $tree_A &&
+ mkdir .orig-A &&
+ git checkout-index --prefix=.orig-A/ -f -q -a &&
+ rm -f .git/index &&
+ git read-tree $tree_B &&
+ mkdir .orig-B &&
+ git checkout-index --prefix=.orig-B/ -f -q -a'
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
new file mode 100644
index 0000000..6aefe27
--- /dev/null
+++ b/t/lib-rebase.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+# After setting the fake editor with this function, you can
+#
+# - override the commit message with $FAKE_COMMIT_MESSAGE
+# - amend the commit message with $FAKE_COMMIT_AMEND
+# - check that non-commit messages have a certain line count with $EXPECT_COUNT
+# - check the commit count in the commit message header with $EXPECT_HEADER_COUNT
+# - rewrite a rebase -i script as directed by $FAKE_LINES.
+# $FAKE_LINES consists of a sequence of words separated by spaces.
+# The following word combinations are possible:
+#
+# "<lineno>" -- add a "pick" line with the SHA1 taken from the
+# specified line.
+#
+# "<cmd> <lineno>" -- add a line with the specified command
+# ("squash", "fixup", "edit", or "reword") and the SHA1 taken
+# from the specified line.
+#
+# "#" -- Add a comment line.
+#
+# ">" -- Add a blank line.
+
+set_fake_editor () {
+ echo "#!$SHELL_PATH" >fake-editor.sh
+ cat >> fake-editor.sh <<\EOF
+case "$1" in
+*/COMMIT_EDITMSG)
+ test -z "$EXPECT_HEADER_COUNT" ||
+ test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+ exit
+ test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+ test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
+ exit
+ ;;
+esac
+test -z "$EXPECT_COUNT" ||
+ test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+ exit
+test -z "$FAKE_LINES" && exit
+grep -v '^#' < "$1" > "$1".tmp
+rm -f "$1"
+echo 'rebase -i script before editing:'
+cat "$1".tmp
+action=pick
+for line in $FAKE_LINES; do
+ case $line in
+ squash|fixup|edit|reword)
+ action="$line";;
+ "#")
+ echo '# comment' >> "$1";;
+ ">")
+ echo >> "$1";;
+ *)
+ sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+ action=pick;;
+ esac
+done
+echo 'rebase -i script after editing:'
+cat "$1"
+EOF
+
+ test_set_editor "$(pwd)/fake-editor.sh"
+ chmod a+x fake-editor.sh
+}
diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh
new file mode 100644
index 0000000..ea25dd8
--- /dev/null
+++ b/t/lib-t6000.sh
@@ -0,0 +1,127 @@
+: included from 6002 and others
+
+[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+
+:> sed.script
+
+# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
+tag()
+{
+ _tag=$1
+ [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
+ cat .git/refs/tags/$_tag
+}
+
+# Generate a commit using the text specified to make it unique and the tree
+# named by the tag specified.
+unique_commit()
+{
+ _text=$1
+ _tree=$2
+ shift 2
+ echo $_text | git commit-tree $(tag $_tree) "$@"
+}
+
+# Save the output of a command into the tag specified. Prepend
+# a substitution script for the tag onto the front of sed.script
+save_tag()
+{
+ _tag=$1
+ [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+ shift 1
+ "$@" >.git/refs/tags/$_tag
+
+ echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
+ cat sed.script >> sed.script.tmp
+ rm sed.script
+ mv sed.script.tmp sed.script
+}
+
+# Replace unhelpful sha1 hashses with their symbolic equivalents
+entag()
+{
+ sed -f sed.script
+}
+
+# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
+# tag to a specified value. Restore the original value on return.
+as_author()
+{
+ _author=$1
+ shift 1
+ _save=$GIT_AUTHOR_EMAIL
+
+ GIT_AUTHOR_EMAIL="$_author"
+ export GIT_AUTHOR_EMAIL
+ "$@"
+ if test -z "$_save"
+ then
+ unset GIT_AUTHOR_EMAIL
+ else
+ GIT_AUTHOR_EMAIL="$_save"
+ export GIT_AUTHOR_EMAIL
+ fi
+}
+
+commit_date()
+{
+ _commit=$1
+ git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+}
+
+on_committer_date()
+{
+ _date=$1
+ shift 1
+ GIT_COMMITTER_DATE="$_date"
+ export GIT_COMMITTER_DATE
+ "$@"
+ unset GIT_COMMITTER_DATE
+}
+
+# Execute a command and suppress any error output.
+hide_error()
+{
+ "$@" 2>/dev/null
+}
+
+check_output()
+{
+ _name=$1
+ shift 1
+ if eval "$*" | entag > $_name.actual
+ then
+ test_cmp $_name.expected $_name.actual
+ else
+ return 1;
+ fi
+}
+
+# Turn a reasonable test description into a reasonable test name.
+# All alphanums translated into -'s which are then compressed and stripped
+# from front and back.
+name_from_description()
+{
+ perl -pe '
+ s/[^A-Za-z0-9.]/-/g;
+ s/-+/-/g;
+ s/-$//;
+ s/^-//;
+ y/A-Z/a-z/;
+ '
+}
+
+
+# Execute the test described by the first argument, by eval'ing
+# command line specified in the 2nd argument. Check the status code
+# is zero and that the output matches the stream read from
+# stdin.
+test_output_expect_success()
+{
+ _description=$1
+ _test=$2
+ [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
+ _name=$(echo $_description | name_from_description)
+ cat > $_name.expected
+ test_expect_success "$_description" "check_output $_name \"$_test\""
+}
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
new file mode 100755
index 0000000..f2c7336
--- /dev/null
+++ b/t/t0000-basic.sh
@@ -0,0 +1,389 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test the very basics part #1.
+
+The rest of the test suite does not check the basic operation of git
+plumbing commands to work very carefully. Their job is to concentrate
+on tricky features that caused bugs in the past to detect regression.
+
+This test runs very basic features, like registering things in cache,
+writing tree, etc.
+
+Note that this test *deliberately* hard-codes many expected object
+IDs. When object ID computation changes, like in the previous case of
+swapping compression and hashing order, the person who is making the
+modification *should* take notice and update the test vectors here.
+'
+
+################################################################
+# It appears that people try to run tests without building...
+
+../git >/dev/null
+if test $? != 1
+then
+ echo >&2 'You do not seem to have built git yet.'
+ exit 1
+fi
+
+. ./test-lib.sh
+
+################################################################
+# git init has been done in an empty repository.
+# make sure it is empty.
+
+find .git/objects -type f -print >should-be-empty
+test_expect_success \
+ '.git/objects should be empty after git init in an empty repo.' \
+ 'cmp -s /dev/null should-be-empty'
+
+# also it should have 2 subdirectories; no fan-out anymore, pack, and info.
+# 3 is counting "objects" itself
+find .git/objects -type d -print >full-of-directories
+test_expect_success \
+ '.git/objects should have 3 subdirectories.' \
+ 'test $(wc -l < full-of-directories) = 3'
+
+################################################################
+# Test harness
+test_expect_success 'success is reported like this' '
+ :
+'
+test_expect_failure 'pretend we have a known breakage' '
+ false
+'
+test_expect_failure 'pretend we have fixed a known breakage' '
+ :
+'
+test_set_prereq HAVEIT
+haveit=no
+test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
+ test_have_prereq HAVEIT &&
+ haveit=yes
+'
+donthaveit=yes
+test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' '
+ donthaveit=no
+'
+if test $haveit$donthaveit != yesyes
+then
+ say "bug in test framework: prerequisite tags do not work reliably"
+ exit 1
+fi
+
+clean=no
+test_expect_success 'tests clean up after themselves' '
+ test_when_finished clean=yes
+'
+
+cleaner=no
+test_expect_code 1 'tests clean up even after a failure' '
+ test_when_finished cleaner=yes &&
+ (exit 1)
+'
+
+if test $clean$cleaner != yesyes
+then
+ say "bug in test framework: cleanup commands do not work reliably"
+ exit 1
+fi
+
+test_expect_code 2 'failure to clean up causes the test to fail' '
+ test_when_finished "(exit 2)"
+'
+
+################################################################
+# Basics of the basics
+
+# updating a new file without --add should fail.
+test_expect_success 'git update-index without --add should fail adding.' '
+ test_must_fail git update-index should-be-empty
+'
+
+# and with --add it should succeed, even if it is empty (it used to fail).
+test_expect_success \
+ 'git update-index with --add should succeed.' \
+ 'git update-index --add should-be-empty'
+
+test_expect_success \
+ 'writing tree out with git write-tree' \
+ 'tree=$(git write-tree)'
+
+# we know the shape and contents of the tree and know the object ID for it.
+test_expect_success \
+ 'validate object ID of a known tree.' \
+ 'test "$tree" = 7bb943559a305bdd6bdee2cef6e5df2413c3d30a'
+
+# Removing paths.
+rm -f should-be-empty full-of-directories
+test_expect_success 'git update-index without --remove should fail removing.' '
+ test_must_fail git update-index should-be-empty
+'
+
+test_expect_success \
+ 'git update-index with --remove should be able to remove.' \
+ 'git update-index --remove should-be-empty'
+
+# Empty tree can be written with recent write-tree.
+test_expect_success \
+ 'git write-tree should be able to write an empty tree.' \
+ 'tree=$(git write-tree)'
+
+test_expect_success \
+ 'validate object ID of a known tree.' \
+ 'test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904'
+
+# Various types of objects
+# Some filesystems do not support symblic links; on such systems
+# some expected values are different
+mkdir path2 path3 path3/subp3
+paths='path0 path2/file2 path3/file3 path3/subp3/file3'
+for p in $paths
+do
+ echo "hello $p" >$p
+done
+if test_have_prereq SYMLINKS
+then
+ for p in $paths
+ do
+ ln -s "hello $p" ${p}sym
+ done
+ expectfilter=cat
+ expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b
+ expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3
+ expectedptree2=3c5e5399f3a333eddecce7a9b9465b63f65f51e2
+else
+ expectfilter='grep -v sym'
+ expectedtree=8e18edf7d7edcf4371a3ac6ae5f07c2641db7c46
+ expectedptree1=cfb8591b2f65de8b8cc1020cd7d9e67e7793b325
+ expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f
+fi
+
+test_expect_success \
+ 'adding various types of objects with git update-index --add.' \
+ 'find path* ! -type d -print | xargs git update-index --add'
+
+# Show them and see that matches what we expect.
+test_expect_success \
+ 'showing stage with git ls-files --stage' \
+ 'git ls-files --stage >current'
+
+$expectfilter >expected <<\EOF
+100644 f87290f8eb2cbbea7857214459a0739927eab154 0 path0
+120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0 path0sym
+100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0 path2/file2
+120000 d8ce161addc5173867a3c3c730924388daedbc38 0 path2/file2sym
+100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0 path3/file3
+120000 8599103969b43aff7e430efea79ca4636466794f 0 path3/file3sym
+100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0 path3/subp3/file3
+120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0 path3/subp3/file3sym
+EOF
+test_expect_success \
+ 'validate git ls-files output for a known tree.' \
+ 'test_cmp expected current'
+
+test_expect_success \
+ 'writing tree out with git write-tree.' \
+ 'tree=$(git write-tree)'
+test_expect_success \
+ 'validate object ID for a known tree.' \
+ 'test "$tree" = "$expectedtree"'
+
+test_expect_success \
+ 'showing tree with git ls-tree' \
+ 'git ls-tree $tree >current'
+cat >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
+040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
+EOF
+test_expect_success SYMLINKS \
+ 'git ls-tree output for a known tree.' \
+ 'test_cmp expected current'
+
+# This changed in ls-tree pathspec change -- recursive does
+# not show tree nodes anymore.
+test_expect_success \
+ 'showing tree with git ls-tree -r' \
+ 'git ls-tree -r $tree >current'
+$expectfilter >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
+120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
+100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
+120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
+100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
+120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
+EOF
+test_expect_success \
+ 'git ls-tree -r output for a known tree.' \
+ 'test_cmp expected current'
+
+# But with -r -t we can have both.
+test_expect_success \
+ 'showing tree with git ls-tree -r -t' \
+ 'git ls-tree -r -t $tree >current'
+cat >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
+100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
+120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
+040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
+100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
+120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
+040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 path3/subp3
+100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
+120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
+EOF
+test_expect_success SYMLINKS \
+ 'git ls-tree -r output for a known tree.' \
+ 'test_cmp expected current'
+
+test_expect_success \
+ 'writing partial tree out with git write-tree --prefix.' \
+ 'ptree=$(git write-tree --prefix=path3)'
+test_expect_success \
+ 'validate object ID for a known tree.' \
+ 'test "$ptree" = "$expectedptree1"'
+
+test_expect_success \
+ 'writing partial tree out with git write-tree --prefix.' \
+ 'ptree=$(git write-tree --prefix=path3/subp3)'
+test_expect_success \
+ 'validate object ID for a known tree.' \
+ 'test "$ptree" = "$expectedptree2"'
+
+cat >badobjects <<EOF
+100644 blob 1000000000000000000000000000000000000000 dir/file1
+100644 blob 2000000000000000000000000000000000000000 dir/file2
+100644 blob 3000000000000000000000000000000000000000 dir/file3
+100644 blob 4000000000000000000000000000000000000000 dir/file4
+100644 blob 5000000000000000000000000000000000000000 dir/file5
+EOF
+
+rm .git/index
+test_expect_success \
+ 'put invalid objects into the index.' \
+ 'git update-index --index-info < badobjects'
+
+test_expect_success 'writing this tree without --missing-ok.' '
+ test_must_fail git write-tree
+'
+
+test_expect_success \
+ 'writing this tree with --missing-ok.' \
+ 'git write-tree --missing-ok'
+
+
+################################################################
+rm .git/index
+test_expect_success \
+ 'git read-tree followed by write-tree should be idempotent.' \
+ 'git read-tree $tree &&
+ test -f .git/index &&
+ newtree=$(git write-tree) &&
+ test "$newtree" = "$tree"'
+
+$expectfilter >expected <<\EOF
+:100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M path0
+:120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M path0sym
+:100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M path2/file2
+:120000 120000 d8ce161addc5173867a3c3c730924388daedbc38 0000000000000000000000000000000000000000 M path2/file2sym
+:100644 100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0000000000000000000000000000000000000000 M path3/file3
+:120000 120000 8599103969b43aff7e430efea79ca4636466794f 0000000000000000000000000000000000000000 M path3/file3sym
+:100644 100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0000000000000000000000000000000000000000 M path3/subp3/file3
+:120000 120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0000000000000000000000000000000000000000 M path3/subp3/file3sym
+EOF
+test_expect_success \
+ 'validate git diff-files output for a know cache/work tree state.' \
+ 'git diff-files >current && test_cmp current expected >/dev/null'
+
+test_expect_success \
+ 'git update-index --refresh should succeed.' \
+ 'git update-index --refresh'
+
+test_expect_success \
+ 'no diff after checkout and git update-index --refresh.' \
+ 'git diff-files >current && cmp -s current /dev/null'
+
+################################################################
+P=$expectedtree
+test_expect_success \
+ 'git commit-tree records the correct tree in a commit.' \
+ 'commit0=$(echo NO | git commit-tree $P) &&
+ tree=$(git show --pretty=raw $commit0 |
+ sed -n -e "s/^tree //p" -e "/^author /q") &&
+ test "z$tree" = "z$P"'
+
+test_expect_success \
+ 'git commit-tree records the correct parent in a commit.' \
+ 'commit1=$(echo NO | git commit-tree $P -p $commit0) &&
+ parent=$(git show --pretty=raw $commit1 |
+ sed -n -e "s/^parent //p" -e "/^author /q") &&
+ test "z$commit0" = "z$parent"'
+
+test_expect_success \
+ 'git commit-tree omits duplicated parent in a commit.' \
+ 'commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
+ parent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ sort -u) &&
+ test "z$commit0" = "z$parent" &&
+ numparent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ wc -l) &&
+ test $numparent = 1'
+
+test_expect_success 'update-index D/F conflict' '
+ mv path0 tmp &&
+ mv path2 path0 &&
+ mv tmp path2 &&
+ git update-index --add --replace path2 path0/file2 &&
+ numpath0=$(git ls-files path0 | wc -l) &&
+ test $numpath0 = 1
+'
+
+test_expect_success SYMLINKS 'absolute path works as expected' '
+ mkdir first &&
+ ln -s ../.git first/.git &&
+ mkdir second &&
+ ln -s ../first second/other &&
+ mkdir third &&
+ dir="$(cd .git; pwd -P)" &&
+ dir2=third/../second/other/.git &&
+ test "$dir" = "$(test-path-utils make_absolute_path $dir2)" &&
+ file="$dir"/index &&
+ test "$file" = "$(test-path-utils make_absolute_path $dir2/index)" &&
+ basename=blub &&
+ test "$dir/$basename" = "$(cd .git && test-path-utils make_absolute_path "$basename")" &&
+ ln -s ../first/file .git/syml &&
+ sym="$(cd first; pwd -P)"/file &&
+ test "$sym" = "$(test-path-utils make_absolute_path "$dir2/syml")"
+'
+
+test_expect_success 'very long name in the index handled sanely' '
+
+ a=a && # 1
+ a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 16
+ a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 256
+ a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 4096
+ a=${a}q &&
+
+ >path4 &&
+ git update-index --add path4 &&
+ (
+ git ls-files -s path4 |
+ sed -e "s/ .*/ /" |
+ tr -d "\012"
+ echo "$a"
+ ) | git update-index --index-info &&
+ len=$(git ls-files "a*" | wc -c) &&
+ test $len = 4098
+'
+
+test_done
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
new file mode 100755
index 0000000..7c0a698
--- /dev/null
+++ b/t/t0001-init.sh
@@ -0,0 +1,327 @@
+#!/bin/sh
+
+test_description='git init'
+
+. ./test-lib.sh
+
+check_config () {
+ if test -d "$1" && test -f "$1/config" && test -d "$1/refs"
+ then
+ : happy
+ else
+ echo "expected a directory $1, a file $1/config and $1/refs"
+ return 1
+ fi
+ bare=$(GIT_CONFIG="$1/config" git config --bool core.bare)
+ worktree=$(GIT_CONFIG="$1/config" git config core.worktree) ||
+ worktree=unset
+
+ test "$bare" = "$2" && test "$worktree" = "$3" || {
+ echo "expected bare=$2 worktree=$3"
+ echo " got bare=$bare worktree=$worktree"
+ return 1
+ }
+}
+
+test_expect_success 'plain' '
+ (
+ unset GIT_DIR GIT_WORK_TREE
+ mkdir plain &&
+ cd plain &&
+ git init
+ ) &&
+ check_config plain/.git false unset
+'
+
+test_expect_success 'plain with GIT_WORK_TREE' '
+ if (
+ unset GIT_DIR
+ mkdir plain-wt &&
+ cd plain-wt &&
+ GIT_WORK_TREE=$(pwd) git init
+ )
+ then
+ echo Should have failed -- GIT_WORK_TREE should not be used
+ false
+ fi
+'
+
+test_expect_success 'plain bare' '
+ (
+ unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+ mkdir plain-bare-1 &&
+ cd plain-bare-1 &&
+ git --bare init
+ ) &&
+ check_config plain-bare-1 true unset
+'
+
+test_expect_success 'plain bare with GIT_WORK_TREE' '
+ if (
+ unset GIT_DIR GIT_CONFIG
+ mkdir plain-bare-2 &&
+ cd plain-bare-2 &&
+ GIT_WORK_TREE=$(pwd) git --bare init
+ )
+ then
+ echo Should have failed -- GIT_WORK_TREE should not be used
+ false
+ fi
+'
+
+test_expect_success 'GIT_DIR bare' '
+
+ (
+ unset GIT_CONFIG
+ mkdir git-dir-bare.git &&
+ GIT_DIR=git-dir-bare.git git init
+ ) &&
+ check_config git-dir-bare.git true unset
+'
+
+test_expect_success 'init --bare' '
+
+ (
+ unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+ mkdir init-bare.git &&
+ cd init-bare.git &&
+ git init --bare
+ ) &&
+ check_config init-bare.git true unset
+'
+
+test_expect_success 'GIT_DIR non-bare' '
+
+ (
+ unset GIT_CONFIG
+ mkdir non-bare &&
+ cd non-bare &&
+ GIT_DIR=.git git init
+ ) &&
+ check_config non-bare/.git false unset
+'
+
+test_expect_success 'GIT_DIR & GIT_WORK_TREE (1)' '
+
+ (
+ unset GIT_CONFIG
+ mkdir git-dir-wt-1.git &&
+ GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-1.git git init
+ ) &&
+ check_config git-dir-wt-1.git false "$(pwd)"
+'
+
+test_expect_success 'GIT_DIR & GIT_WORK_TREE (2)' '
+
+ if (
+ unset GIT_CONFIG
+ mkdir git-dir-wt-2.git &&
+ GIT_WORK_TREE=$(pwd) GIT_DIR=git-dir-wt-2.git git --bare init
+ )
+ then
+ echo Should have failed -- --bare should not be used
+ false
+ fi
+'
+
+test_expect_success 'reinit' '
+
+ (
+ unset GIT_CONFIG GIT_WORK_TREE GIT_CONFIG
+
+ mkdir again &&
+ cd again &&
+ git init >out1 2>err1 &&
+ git init >out2 2>err2
+ ) &&
+ grep "Initialized empty" again/out1 &&
+ grep "Reinitialized existing" again/out2 &&
+ >again/empty &&
+ test_cmp again/empty again/err1 &&
+ test_cmp again/empty again/err2
+'
+
+test_expect_success 'init with --template' '
+ mkdir template-source &&
+ echo content >template-source/file &&
+ (
+ mkdir template-custom &&
+ cd template-custom &&
+ git init --template=../template-source
+ ) &&
+ test_cmp template-source/file template-custom/.git/file
+'
+
+test_expect_success 'init with --template (blank)' '
+ (
+ mkdir template-plain &&
+ cd template-plain &&
+ git init
+ ) &&
+ test -f template-plain/.git/info/exclude &&
+ (
+ mkdir template-blank &&
+ cd template-blank &&
+ git init --template=
+ ) &&
+ ! test -f template-blank/.git/info/exclude
+'
+
+test_expect_success 'init with init.templatedir set' '
+ mkdir templatedir-source &&
+ echo Content >templatedir-source/file &&
+ (
+ HOME="`pwd`" &&
+ export HOME &&
+ test_config="${HOME}/.gitconfig" &&
+ git config -f "$test_config" init.templatedir "${HOME}/templatedir-source" &&
+ mkdir templatedir-set &&
+ cd templatedir-set &&
+ unset GIT_CONFIG_NOGLOBAL &&
+ unset GIT_TEMPLATE_DIR &&
+ NO_SET_GIT_TEMPLATE_DIR=t &&
+ export NO_SET_GIT_TEMPLATE_DIR &&
+ git init
+ ) &&
+ test_cmp templatedir-source/file templatedir-set/.git/file
+'
+
+test_expect_success 'init --bare/--shared overrides system/global config' '
+ (
+ HOME="`pwd`" &&
+ export HOME &&
+ test_config="$HOME"/.gitconfig &&
+ unset GIT_CONFIG_NOGLOBAL &&
+ git config -f "$test_config" core.bare false &&
+ git config -f "$test_config" core.sharedRepository 0640 &&
+ mkdir init-bare-shared-override &&
+ cd init-bare-shared-override &&
+ git init --bare --shared=0666
+ ) &&
+ check_config init-bare-shared-override true unset &&
+ test x0666 = \
+ x`git config -f init-bare-shared-override/config core.sharedRepository`
+'
+
+test_expect_success 'init honors global core.sharedRepository' '
+ (
+ HOME="`pwd`" &&
+ export HOME &&
+ test_config="$HOME"/.gitconfig &&
+ unset GIT_CONFIG_NOGLOBAL &&
+ git config -f "$test_config" core.sharedRepository 0666 &&
+ mkdir shared-honor-global &&
+ cd shared-honor-global &&
+ git init
+ ) &&
+ test x0666 = \
+ x`git config -f shared-honor-global/.git/config core.sharedRepository`
+'
+
+test_expect_success 'init rejects insanely long --template' '
+ (
+ insane=$(printf "x%09999dx" 1) &&
+ mkdir test &&
+ cd test &&
+ test_must_fail git init --template=$insane
+ )
+'
+
+test_expect_success 'init creates a new directory' '
+ rm -fr newdir &&
+ (
+ git init newdir &&
+ test -d newdir/.git/refs
+ )
+'
+
+test_expect_success 'init creates a new bare directory' '
+ rm -fr newdir &&
+ (
+ git init --bare newdir &&
+ test -d newdir/refs
+ )
+'
+
+test_expect_success 'init recreates a directory' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ git init newdir &&
+ test -d newdir/.git/refs
+ )
+'
+
+test_expect_success 'init recreates a new bare directory' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ git init --bare newdir &&
+ test -d newdir/refs
+ )
+'
+
+test_expect_success 'init creates a new deep directory' '
+ rm -fr newdir &&
+ git init newdir/a/b/c &&
+ test -d newdir/a/b/c/.git/refs
+'
+
+test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shared)' '
+ rm -fr newdir &&
+ (
+ # Leading directories should honor umask while
+ # the repository itself should follow "shared"
+ umask 002 &&
+ git init --bare --shared=0660 newdir/a/b/c &&
+ test -d newdir/a/b/c/refs &&
+ ls -ld newdir/a newdir/a/b > lsab.out &&
+ ! grep -v "^drwxrw[sx]r-x" lsab.out &&
+ ls -ld newdir/a/b/c > lsc.out &&
+ ! grep -v "^drwxrw[sx]---" lsc.out
+ )
+'
+
+test_expect_success 'init notices EEXIST (1)' '
+ rm -fr newdir &&
+ (
+ >newdir &&
+ test_must_fail git init newdir &&
+ test -f newdir
+ )
+'
+
+test_expect_success 'init notices EEXIST (2)' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ >newdir/a
+ test_must_fail git init newdir/a/b &&
+ test -f newdir/a
+ )
+'
+
+test_expect_success POSIXPERM 'init notices EPERM' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ chmod -w newdir &&
+ test_must_fail git init newdir/a/b
+ )
+'
+
+test_expect_success 'init creates a new bare directory with global --bare' '
+ rm -rf newdir &&
+ git --bare init newdir &&
+ test -d newdir/refs
+'
+
+test_expect_success 'init prefers command line to GIT_DIR' '
+ rm -rf newdir &&
+ mkdir otherdir &&
+ GIT_DIR=otherdir git --bare init newdir &&
+ test -d newdir/refs &&
+ ! test -d otherdir/refs
+'
+
+test_done
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
new file mode 100755
index 0000000..cb14425
--- /dev/null
+++ b/t/t0002-gitfile.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='.git file
+
+Verify that plumbing commands work when .git is a file
+'
+. ./test-lib.sh
+
+objpath() {
+ echo "$1" | sed -e 's|\(..\)|\1/|'
+}
+
+objck() {
+ p=$(objpath "$1")
+ if test ! -f "$REAL/objects/$p"
+ then
+ echo "Object not found: $REAL/objects/$p"
+ false
+ fi
+}
+
+
+test_expect_success 'initial setup' '
+ REAL="$(pwd)/.real" &&
+ mv .git "$REAL"
+'
+
+test_expect_success 'bad setup: invalid .git file format' '
+ echo "gitdir $REAL" >.git &&
+ if git rev-parse 2>.err
+ then
+ echo "git rev-parse accepted an invalid .git file"
+ false
+ fi &&
+ if ! grep "Invalid gitfile format" .err
+ then
+ echo "git rev-parse returned wrong error"
+ false
+ fi
+'
+
+test_expect_success 'bad setup: invalid .git file path' '
+ echo "gitdir: $REAL.not" >.git &&
+ if git rev-parse 2>.err
+ then
+ echo "git rev-parse accepted an invalid .git file path"
+ false
+ fi &&
+ if ! grep "Not a git repository" .err
+ then
+ echo "git rev-parse returned wrong error"
+ false
+ fi
+'
+
+test_expect_success 'final setup + check rev-parse --git-dir' '
+ echo "gitdir: $REAL" >.git &&
+ test "$REAL" = "$(git rev-parse --git-dir)"
+'
+
+test_expect_success 'check hash-object' '
+ echo "foo" >bar &&
+ SHA=$(cat bar | git hash-object -w --stdin) &&
+ objck $SHA
+'
+
+test_expect_success 'check cat-file' '
+ git cat-file blob $SHA >actual &&
+ test_cmp bar actual
+'
+
+test_expect_success 'check update-index' '
+ if test -f "$REAL/index"
+ then
+ echo "Hmm, $REAL/index exists?"
+ false
+ fi &&
+ rm -f "$REAL/objects/$(objpath $SHA)" &&
+ git update-index --add bar &&
+ if ! test -f "$REAL/index"
+ then
+ echo "$REAL/index not found"
+ false
+ fi &&
+ objck $SHA
+'
+
+test_expect_success 'check write-tree' '
+ SHA=$(git write-tree) &&
+ objck $SHA
+'
+
+test_expect_success 'check commit-tree' '
+ SHA=$(echo "commit bar" | git commit-tree $SHA) &&
+ objck $SHA
+'
+
+test_expect_success 'check rev-list' '
+ echo $SHA >"$REAL/HEAD" &&
+ test "$SHA" = "$(git rev-list HEAD)"
+'
+
+test_done
diff --git a/t/t0003-attributes.sh b/t/t0003-attributes.sh
new file mode 100755
index 0000000..53bd7fc
--- /dev/null
+++ b/t/t0003-attributes.sh
@@ -0,0 +1,124 @@
+#!/bin/sh
+
+test_description=gitattributes
+
+. ./test-lib.sh
+
+attr_check () {
+
+ path="$1"
+ expect="$2"
+
+ git check-attr test -- "$path" >actual &&
+ echo "$path: test: $2" >expect &&
+ test_cmp expect actual
+
+}
+
+
+test_expect_success 'setup' '
+
+ mkdir -p a/b/d a/c &&
+ (
+ echo "[attr]notest !test"
+ echo "f test=f"
+ echo "a/i test=a/i"
+ echo "onoff test -test"
+ echo "offon -test test"
+ echo "no notest"
+ ) >.gitattributes &&
+ (
+ echo "g test=a/g" &&
+ echo "b/g test=a/b/g"
+ ) >a/.gitattributes &&
+ (
+ echo "h test=a/b/h" &&
+ echo "d/* test=a/b/d/*"
+ echo "d/yes notest"
+ ) >a/b/.gitattributes
+
+'
+
+test_expect_success 'attribute test' '
+
+ attr_check f f &&
+ attr_check a/f f &&
+ attr_check a/c/f f &&
+ attr_check a/g a/g &&
+ attr_check a/b/g a/b/g &&
+ attr_check b/g unspecified &&
+ attr_check a/b/h a/b/h &&
+ attr_check a/b/d/g "a/b/d/*"
+ attr_check onoff unset
+ attr_check offon set
+ attr_check no unspecified
+ attr_check a/b/d/no "a/b/d/*"
+ attr_check a/b/d/yes unspecified
+
+'
+
+test_expect_success 'attribute test: read paths from stdin' '
+
+ cat <<EOF > expect
+f: test: f
+a/f: test: f
+a/c/f: test: f
+a/g: test: a/g
+a/b/g: test: a/b/g
+b/g: test: unspecified
+a/b/h: test: a/b/h
+a/b/d/g: test: a/b/d/*
+onoff: test: unset
+offon: test: set
+no: test: unspecified
+a/b/d/no: test: a/b/d/*
+a/b/d/yes: test: unspecified
+EOF
+
+ sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'root subdir attribute test' '
+
+ attr_check a/i a/i &&
+ attr_check subdir/a/i unspecified
+
+'
+
+test_expect_success 'setup bare' '
+
+ git clone --bare . bare.git &&
+ cd bare.git
+
+'
+
+test_expect_success 'bare repository: check that .gitattribute is ignored' '
+
+ (
+ echo "f test=f"
+ echo "a/i test=a/i"
+ ) >.gitattributes &&
+ attr_check f unspecified &&
+ attr_check a/f unspecified &&
+ attr_check a/c/f unspecified &&
+ attr_check a/i unspecified &&
+ attr_check subdir/a/i unspecified
+
+'
+
+test_expect_success 'bare repository: test info/attributes' '
+
+ (
+ echo "f test=f"
+ echo "a/i test=a/i"
+ ) >info/attributes &&
+ attr_check f f &&
+ attr_check a/f f &&
+ attr_check a/c/f f &&
+ attr_check a/i a/i &&
+ attr_check subdir/a/i unspecified
+
+'
+
+test_done
diff --git a/t/t0004-unwritable.sh b/t/t0004-unwritable.sh
new file mode 100755
index 0000000..2342ac5
--- /dev/null
+++ b/t/t0004-unwritable.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+test_description='detect unwritable repository and fail correctly'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ echo >file &&
+ git add file
+
+'
+
+test_expect_success POSIXPERM 'write-tree should notice unwritable repository' '
+
+ (
+ chmod a-w .git/objects .git/objects/?? &&
+ test_must_fail git write-tree
+ )
+ status=$?
+ chmod 775 .git/objects .git/objects/??
+ (exit $status)
+
+'
+
+test_expect_success POSIXPERM 'commit should notice unwritable repository' '
+
+ (
+ chmod a-w .git/objects .git/objects/?? &&
+ test_must_fail git commit -m second
+ )
+ status=$?
+ chmod 775 .git/objects .git/objects/??
+ (exit $status)
+
+'
+
+test_expect_success POSIXPERM 'update-index should notice unwritable repository' '
+
+ (
+ echo 6O >file &&
+ chmod a-w .git/objects .git/objects/?? &&
+ test_must_fail git update-index file
+ )
+ status=$?
+ chmod 775 .git/objects .git/objects/??
+ (exit $status)
+
+'
+
+test_expect_success POSIXPERM 'add should notice unwritable repository' '
+
+ (
+ echo b >file &&
+ chmod a-w .git/objects .git/objects/?? &&
+ test_must_fail git add file
+ )
+ status=$?
+ chmod 775 .git/objects .git/objects/??
+ (exit $status)
+
+'
+
+test_done
diff --git a/t/t0005-signals.sh b/t/t0005-signals.sh
new file mode 100755
index 0000000..93e58c0
--- /dev/null
+++ b/t/t0005-signals.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='signals work as we expect'
+. ./test-lib.sh
+
+cat >expect <<EOF
+three
+two
+one
+EOF
+
+test_expect_success 'sigchain works' '
+ test-sigchain >actual
+ case "$?" in
+ 143) true ;; # POSIX w/ SIGTERM=15
+ 271) true ;; # ksh w/ SIGTERM=15
+ 3) true ;; # Windows
+ *) false ;;
+ esac &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
new file mode 100755
index 0000000..1d4d0a5
--- /dev/null
+++ b/t/t0006-date.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='test date parsing and printing'
+. ./test-lib.sh
+
+# arbitrary reference time: 2009-08-30 19:20:00
+TEST_DATE_NOW=1251660000; export TEST_DATE_NOW
+
+check_show() {
+ t=$(($TEST_DATE_NOW - $1))
+ echo "$t -> $2" >expect
+ test_expect_${3:-success} "relative date ($2)" "
+ test-date show $t >actual &&
+ test_cmp expect actual
+ "
+}
+
+check_show 5 '5 seconds ago'
+check_show 300 '5 minutes ago'
+check_show 18000 '5 hours ago'
+check_show 432000 '5 days ago'
+check_show 1728000 '3 weeks ago'
+check_show 13000000 '5 months ago'
+check_show 37500000 '1 year, 2 months ago'
+check_show 55188000 '1 year, 9 months ago'
+check_show 630000000 '20 years ago'
+check_show 31449600 '12 months ago'
+
+check_parse() {
+ echo "$1 -> $2" >expect
+ test_expect_${4:-success} "parse date ($1${3:+ TZ=$3})" "
+ TZ=${3:-$TZ} test-date parse '$1' >actual &&
+ test_cmp expect actual
+ "
+}
+
+check_parse 2008 bad
+check_parse 2008-02 bad
+check_parse 2008-02-14 bad
+check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
+check_parse '2008-02-14 20:30:45 -0500' '2008-02-14 20:30:45 -0500'
+check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 -0500' EST5
+
+check_approxidate() {
+ echo "$1 -> $2 +0000" >expect
+ test_expect_${3:-success} "parse approxidate ($1)" "
+ test-date approxidate '$1' >actual &&
+ test_cmp expect actual
+ "
+}
+
+check_approxidate now '2009-08-30 19:20:00'
+check_approxidate '5 seconds ago' '2009-08-30 19:19:55'
+check_approxidate 5.seconds.ago '2009-08-30 19:19:55'
+check_approxidate 10.minutes.ago '2009-08-30 19:10:00'
+check_approxidate yesterday '2009-08-29 19:20:00'
+check_approxidate 3.days.ago '2009-08-27 19:20:00'
+check_approxidate 3.weeks.ago '2009-08-09 19:20:00'
+check_approxidate 3.months.ago '2009-05-30 19:20:00'
+check_approxidate 2.years.3.months.ago '2007-05-30 19:20:00'
+
+check_approxidate '6am yesterday' '2009-08-29 06:00:00'
+check_approxidate '6pm yesterday' '2009-08-29 18:00:00'
+check_approxidate '3:00' '2009-08-30 03:00:00'
+check_approxidate '15:00' '2009-08-30 15:00:00'
+check_approxidate 'noon today' '2009-08-30 12:00:00'
+check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
+
+check_approxidate 'last tuesday' '2009-08-25 19:20:00'
+check_approxidate 'July 5th' '2009-07-05 19:20:00'
+check_approxidate '06/05/2009' '2009-06-05 19:20:00'
+check_approxidate '06.05.2009' '2009-05-06 19:20:00'
+
+check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'
+check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'
+check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00'
+
+test_done
diff --git a/t/t0010-racy-git.sh b/t/t0010-racy-git.sh
new file mode 100755
index 0000000..e45a9e4
--- /dev/null
+++ b/t/t0010-racy-git.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='racy GIT'
+
+. ./test-lib.sh
+
+# This test can give false success if your machine is sufficiently
+# slow or your trial happened to happen on second boundary.
+
+for trial in 0 1 2 3 4
+do
+ rm -f .git/index
+ echo frotz >infocom
+ git update-index --add infocom
+ echo xyzzy >infocom
+
+ files=`git diff-files -p`
+ test_expect_success \
+ "Racy GIT trial #$trial part A" \
+ 'test "" != "$files"'
+
+ sleep 1
+ echo xyzzy >cornerstone
+ git update-index --add cornerstone
+
+ files=`git diff-files -p`
+ test_expect_success \
+ "Racy GIT trial #$trial part B" \
+ 'test "" != "$files"'
+
+done
+
+test_done
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
new file mode 100755
index 0000000..234a94f
--- /dev/null
+++ b/t/t0020-crlf.sh
@@ -0,0 +1,509 @@
+#!/bin/sh
+
+test_description='CRLF conversion'
+
+. ./test-lib.sh
+
+has_cr() {
+ tr '\015' Q <"$1" | grep Q >/dev/null
+}
+
+test_expect_success setup '
+
+ git config core.autocrlf false &&
+
+ for w in Hello world how are you; do echo $w; done >one &&
+ mkdir dir &&
+ for w in I am very very fine thank you; do echo $w; done >dir/two &&
+ for w in Oh here is NULQin text here; do echo $w; done | q_to_nul >three &&
+ git add . &&
+
+ git commit -m initial &&
+
+ one=`git rev-parse HEAD:one` &&
+ dir=`git rev-parse HEAD:dir` &&
+ two=`git rev-parse HEAD:dir/two` &&
+ three=`git rev-parse HEAD:three` &&
+
+ for w in Some extra lines here; do echo $w; done >>one &&
+ git diff >patch.file &&
+ patched=`git hash-object --stdin <one` &&
+ git read-tree --reset -u HEAD &&
+
+ echo happy.
+'
+
+test_expect_success 'safecrlf: autocrlf=input, all CRLF' '
+
+ git config core.autocrlf input &&
+ git config core.safecrlf true &&
+
+ for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+ test_must_fail git add allcrlf
+'
+
+test_expect_success 'safecrlf: autocrlf=input, mixed LF/CRLF' '
+
+ git config core.autocrlf input &&
+ git config core.safecrlf true &&
+
+ for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+ test_must_fail git add mixed
+'
+
+test_expect_success 'safecrlf: autocrlf=true, all LF' '
+
+ git config core.autocrlf true &&
+ git config core.safecrlf true &&
+
+ for w in I am all LF; do echo $w; done >alllf &&
+ test_must_fail git add alllf
+'
+
+test_expect_success 'safecrlf: autocrlf=true mixed LF/CRLF' '
+
+ git config core.autocrlf true &&
+ git config core.safecrlf true &&
+
+ for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+ test_must_fail git add mixed
+'
+
+test_expect_success 'safecrlf: print warning only once' '
+
+ git config core.autocrlf input &&
+ git config core.safecrlf warn &&
+
+ for w in I am all LF; do echo $w; done >doublewarn &&
+ 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
+'
+
+test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' '
+ git config core.autocrlf false &&
+ git config core.safecrlf false &&
+ git reset --hard HEAD^
+'
+
+test_expect_success 'update with autocrlf=input' '
+
+ rm -f tmp one dir/two three &&
+ git read-tree --reset -u HEAD &&
+ git config core.autocrlf input &&
+
+ for f in one dir/two
+ do
+ append_cr <$f >tmp && mv -f tmp $f &&
+ git update-index -- $f || {
+ echo Oops
+ false
+ break
+ }
+ done &&
+
+ differs=`git diff-index --cached HEAD` &&
+ test -z "$differs" || {
+ echo Oops "$differs"
+ false
+ }
+
+'
+
+test_expect_success 'update with autocrlf=true' '
+
+ rm -f tmp one dir/two three &&
+ git read-tree --reset -u HEAD &&
+ git config core.autocrlf true &&
+
+ for f in one dir/two
+ do
+ append_cr <$f >tmp && mv -f tmp $f &&
+ git update-index -- $f || {
+ echo "Oops $f"
+ false
+ break
+ }
+ done &&
+
+ differs=`git diff-index --cached HEAD` &&
+ test -z "$differs" || {
+ echo Oops "$differs"
+ false
+ }
+
+'
+
+test_expect_success 'checkout with autocrlf=true' '
+
+ rm -f tmp one dir/two three &&
+ git config core.autocrlf true &&
+ git read-tree --reset -u HEAD &&
+
+ for f in one dir/two
+ do
+ remove_cr <"$f" >tmp && mv -f tmp $f &&
+ git update-index -- $f || {
+ echo "Eh? $f"
+ false
+ break
+ }
+ done &&
+ test "$one" = `git hash-object --stdin <one` &&
+ test "$two" = `git hash-object --stdin <dir/two` &&
+ differs=`git diff-index --cached HEAD` &&
+ test -z "$differs" || {
+ echo Oops "$differs"
+ false
+ }
+'
+
+test_expect_success 'checkout with autocrlf=input' '
+
+ rm -f tmp one dir/two three &&
+ git config core.autocrlf input &&
+ git read-tree --reset -u HEAD &&
+
+ for f in one dir/two
+ do
+ if has_cr "$f"
+ then
+ echo "Eh? $f"
+ false
+ break
+ else
+ git update-index -- $f
+ fi
+ done &&
+ test "$one" = `git hash-object --stdin <one` &&
+ test "$two" = `git hash-object --stdin <dir/two` &&
+ differs=`git diff-index --cached HEAD` &&
+ test -z "$differs" || {
+ echo Oops "$differs"
+ false
+ }
+'
+
+test_expect_success 'apply patch (autocrlf=input)' '
+
+ rm -f tmp one dir/two three &&
+ git config core.autocrlf input &&
+ git read-tree --reset -u HEAD &&
+
+ git apply patch.file &&
+ test "$patched" = "`git hash-object --stdin <one`" || {
+ echo "Eh? apply without index"
+ false
+ }
+'
+
+test_expect_success 'apply patch --cached (autocrlf=input)' '
+
+ rm -f tmp one dir/two three &&
+ git config core.autocrlf input &&
+ git read-tree --reset -u HEAD &&
+
+ git apply --cached patch.file &&
+ test "$patched" = `git rev-parse :one` || {
+ echo "Eh? apply with --cached"
+ false
+ }
+'
+
+test_expect_success 'apply patch --index (autocrlf=input)' '
+
+ rm -f tmp one dir/two three &&
+ git config core.autocrlf input &&
+ git read-tree --reset -u HEAD &&
+
+ git apply --index patch.file &&
+ test "$patched" = `git rev-parse :one` &&
+ test "$patched" = `git hash-object --stdin <one` || {
+ echo "Eh? apply with --index"
+ false
+ }
+'
+
+test_expect_success 'apply patch (autocrlf=true)' '
+
+ rm -f tmp one dir/two three &&
+ git config core.autocrlf true &&
+ git read-tree --reset -u HEAD &&
+
+ git apply patch.file &&
+ test "$patched" = "`remove_cr <one | git hash-object --stdin`" || {
+ echo "Eh? apply without index"
+ false
+ }
+'
+
+test_expect_success 'apply patch --cached (autocrlf=true)' '
+
+ rm -f tmp one dir/two three &&
+ git config core.autocrlf true &&
+ git read-tree --reset -u HEAD &&
+
+ git apply --cached patch.file &&
+ test "$patched" = `git rev-parse :one` || {
+ echo "Eh? apply without index"
+ false
+ }
+'
+
+test_expect_success 'apply patch --index (autocrlf=true)' '
+
+ rm -f tmp one dir/two three &&
+ git config core.autocrlf true &&
+ git read-tree --reset -u HEAD &&
+
+ git apply --index patch.file &&
+ test "$patched" = `git rev-parse :one` &&
+ test "$patched" = "`remove_cr <one | git hash-object --stdin`" || {
+ echo "Eh? apply with --index"
+ false
+ }
+'
+
+test_expect_success '.gitattributes says two is binary' '
+
+ rm -f tmp one dir/two three &&
+ echo "two -crlf" >.gitattributes &&
+ git config core.autocrlf true &&
+ git read-tree --reset -u HEAD &&
+
+ if has_cr dir/two
+ then
+ echo "Huh?"
+ false
+ else
+ : happy
+ fi &&
+
+ if has_cr one
+ then
+ : happy
+ else
+ echo "Huh?"
+ false
+ fi &&
+
+ if has_cr three
+ then
+ echo "Huh?"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success '.gitattributes says two is input' '
+
+ rm -f tmp one dir/two three &&
+ echo "two crlf=input" >.gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ if has_cr dir/two
+ then
+ echo "Huh?"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success '.gitattributes says two and three are text' '
+
+ rm -f tmp one dir/two three &&
+ echo "t* crlf" >.gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ if has_cr dir/two
+ then
+ : happy
+ else
+ echo "Huh?"
+ false
+ fi &&
+
+ if has_cr three
+ then
+ : happy
+ else
+ echo "Huh?"
+ false
+ fi
+'
+
+test_expect_success 'in-tree .gitattributes (1)' '
+
+ echo "one -crlf" >>.gitattributes &&
+ git add .gitattributes &&
+ git commit -m "Add .gitattributes" &&
+
+ rm -rf tmp one dir .gitattributes patch.file three &&
+ git read-tree --reset -u HEAD &&
+
+ if has_cr one
+ then
+ echo "Eh? one should not have CRLF"
+ false
+ else
+ : happy
+ fi &&
+ has_cr three || {
+ echo "Eh? three should still have CRLF"
+ false
+ }
+'
+
+test_expect_success 'in-tree .gitattributes (2)' '
+
+ rm -rf tmp one dir .gitattributes patch.file three &&
+ git read-tree --reset HEAD &&
+ git checkout-index -f -q -u -a &&
+
+ if has_cr one
+ then
+ echo "Eh? one should not have CRLF"
+ false
+ else
+ : happy
+ fi &&
+ has_cr three || {
+ echo "Eh? three should still have CRLF"
+ false
+ }
+'
+
+test_expect_success 'in-tree .gitattributes (3)' '
+
+ rm -rf tmp one dir .gitattributes patch.file three &&
+ git read-tree --reset HEAD &&
+ git checkout-index -u .gitattributes &&
+ git checkout-index -u one dir/two three &&
+
+ if has_cr one
+ then
+ echo "Eh? one should not have CRLF"
+ false
+ else
+ : happy
+ fi &&
+ has_cr three || {
+ echo "Eh? three should still have CRLF"
+ false
+ }
+'
+
+test_expect_success 'in-tree .gitattributes (4)' '
+
+ rm -rf tmp one dir .gitattributes patch.file three &&
+ git read-tree --reset HEAD &&
+ git checkout-index -u one dir/two three &&
+ git checkout-index -u .gitattributes &&
+
+ if has_cr one
+ then
+ echo "Eh? one should not have CRLF"
+ false
+ else
+ : happy
+ fi &&
+ has_cr three || {
+ echo "Eh? three should still have CRLF"
+ false
+ }
+'
+
+test_expect_success 'checkout with existing .gitattributes' '
+
+ git config core.autocrlf true &&
+ git config --unset core.safecrlf &&
+ echo ".file2 -crlfQ" | q_to_cr >> .gitattributes &&
+ git add .gitattributes &&
+ git commit -m initial &&
+ echo ".file -crlfQ" | q_to_cr >> .gitattributes &&
+ echo "contents" > .file &&
+ git add .gitattributes .file &&
+ git commit -m second &&
+
+ git checkout master~1 &&
+ git checkout master &&
+ test "$(git diff-files --raw)" = ""
+
+'
+
+test_expect_success 'checkout when deleting .gitattributes' '
+
+ git rm .gitattributes &&
+ echo "contentsQ" | q_to_cr > .file2 &&
+ git add .file2 &&
+ git commit -m third
+
+ git checkout master~1 &&
+ git checkout master &&
+ has_cr .file2
+
+'
+
+test_expect_success 'invalid .gitattributes (must not crash)' '
+
+ echo "three +crlf" >>.gitattributes &&
+ git diff
+
+'
+# Some more tests here to add new autocrlf functionality.
+# We want to have a known state here, so start a bit from scratch
+
+test_expect_success 'setting up for new autocrlf tests' '
+ git config core.autocrlf false &&
+ git config core.safecrlf false &&
+ rm -rf .????* * &&
+ for w in I am all LF; do echo $w; done >alllf &&
+ for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >mixed &&
+ for w in I am all CRLF; do echo $w; done | append_cr >allcrlf &&
+ git add -A . &&
+ git commit -m "alllf, allcrlf and mixed only" &&
+ git tag -a -m "message" autocrlf-checkpoint
+'
+
+test_expect_success 'report no change after setting autocrlf' '
+ git config core.autocrlf true &&
+ touch * &&
+ git diff --exit-code
+'
+
+test_expect_success 'files are clean after checkout' '
+ rm * &&
+ git checkout -f &&
+ git diff --exit-code
+'
+
+cr_to_Q_no_NL () {
+ tr '\015' Q | tr -d '\012'
+}
+
+test_expect_success 'LF only file gets CRLF with autocrlf' '
+ test "$(cr_to_Q_no_NL < alllf)" = "IQamQallQLFQ"
+'
+
+test_expect_success 'Mixed file is still mixed with autocrlf' '
+ test "$(cr_to_Q_no_NL < mixed)" = "OhhereisCRLFQintext"
+'
+
+test_expect_success 'CRLF only file has CRLF with autocrlf' '
+ test "$(cr_to_Q_no_NL < allcrlf)" = "IQamQallQCRLFQ"
+'
+
+test_expect_success 'New CRLF file gets LF in repo' '
+ tr -d "\015" < alllf | append_cr > alllf2 &&
+ git add alllf2 &&
+ git commit -m "alllf2 added" &&
+ git config core.autocrlf false &&
+ rm * &&
+ git checkout -f &&
+ test_cmp alllf alllf2
+'
+
+test_done
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
new file mode 100755
index 0000000..828e35b
--- /dev/null
+++ b/t/t0021-conversion.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='blob conversion via gitattributes'
+
+. ./test-lib.sh
+
+cat <<EOF >rot13.sh
+#!$SHELL_PATH
+tr \
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
+ 'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
+EOF
+chmod +x rot13.sh
+
+test_expect_success setup '
+ git config filter.rot13.smudge ./rot13.sh &&
+ git config filter.rot13.clean ./rot13.sh &&
+
+ {
+ echo "*.t filter=rot13"
+ echo "*.i ident"
+ } >.gitattributes &&
+
+ {
+ echo a b c d e f g h i j k l m
+ echo n o p q r s t u v w x y z
+ echo '\''$Id$'\''
+ } >test &&
+ cat test >test.t &&
+ cat test >test.o &&
+ 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
+'
+
+script='s/^\$Id: \([0-9a-f]*\) \$/\1/p'
+
+test_expect_success check '
+
+ cmp test.o test &&
+ cmp test.o test.t &&
+
+ # ident should be stripped in the repository
+ git diff --raw --exit-code :test :test.i &&
+ id=$(git rev-parse --verify :test) &&
+ embedded=$(sed -ne "$script" test.i) &&
+ test "z$id" = "z$embedded" &&
+
+ git cat-file blob :test.t > test.r &&
+
+ ./rot13.sh < test.o > test.t &&
+ cmp test.r test.t
+'
+
+# If an expanded ident ever gets into the repository, we want to make sure that
+# it is collapsed before being expanded again on checkout
+test_expect_success expanded_in_repo '
+ {
+ echo "File with expanded keywords"
+ echo "\$Id\$"
+ echo "\$Id:\$"
+ echo "\$Id: 0000000000000000000000000000000000000000 \$"
+ echo "\$Id: NoSpaceAtEnd\$"
+ echo "\$Id:NoSpaceAtFront \$"
+ echo "\$Id:NoSpaceAtEitherEnd\$"
+ echo "\$Id: NoTerminatingSymbol"
+ echo "\$Id: Foreign Commit With Spaces \$"
+ echo "\$Id: NoTerminatingSymbolAtEOF"
+ } > expanded-keywords &&
+
+ {
+ echo "File with expanded keywords"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: fd0478f5f1486f3d5177d4c3f6eb2765e8fc56b9 \$"
+ echo "\$Id: NoTerminatingSymbol"
+ echo "\$Id: Foreign Commit With Spaces \$"
+ echo "\$Id: NoTerminatingSymbolAtEOF"
+ } > expected-output &&
+
+ git add expanded-keywords &&
+ git commit -m "File with keywords expanded" &&
+
+ echo "expanded-keywords ident" >> .gitattributes &&
+
+ rm -f expanded-keywords &&
+ git checkout -- expanded-keywords &&
+ cat expanded-keywords &&
+ cmp expanded-keywords expected-output
+'
+
+test_done
diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh
new file mode 100755
index 0000000..7af3fbc
--- /dev/null
+++ b/t/t0022-crlf-rename.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='ignore CR in CRLF sequence while computing similiarity'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ cat "$TEST_DIRECTORY"/t0022-crlf-rename.sh >sample &&
+ git add sample &&
+
+ test_tick &&
+ git commit -m Initial &&
+
+ append_cr <"$TEST_DIRECTORY"/t0022-crlf-rename.sh >elpmas &&
+ git add elpmas &&
+ rm -f sample &&
+
+ test_tick &&
+ git commit -a -m Second
+
+'
+
+test_expect_success 'diff -M' '
+
+ git diff-tree -M -r --name-status HEAD^ HEAD |
+ sed -e "s/R[0-9]*/RNUM/" >actual &&
+ echo "RNUM sample elpmas" >expect &&
+ test_cmp expect actual
+
+'
+
+test_done
diff --git a/t/t0023-crlf-am.sh b/t/t0023-crlf-am.sh
new file mode 100755
index 0000000..aaed725
--- /dev/null
+++ b/t/t0023-crlf-am.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description='Test am with auto.crlf'
+
+. ./test-lib.sh
+
+cat >patchfile <<\EOF
+From 38be10072e45dd6b08ce40851e3fca60a31a340b Mon Sep 17 00:00:00 2001
+From: Marius Storm-Olsen <x@y.com>
+Date: Thu, 23 Aug 2007 13:00:00 +0200
+Subject: test1
+
+---
+ foo | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ create mode 100644 foo
+
+diff --git a/foo b/foo
+new file mode 100644
+index 0000000000000000000000000000000000000000..5716ca5987cbf97d6bb54920bea6adde242d87e6
+--- /dev/null
++++ b/foo
+@@ -0,0 +1 @@
++bar
+EOF
+
+test_expect_success 'setup' '
+
+ git config core.autocrlf true &&
+ echo foo >bar &&
+ git add bar &&
+ test_tick &&
+ git commit -m initial
+
+'
+
+test_expect_success 'am' '
+
+ git am -3 <patchfile &&
+ git diff-files --name-status --exit-code
+
+'
+
+test_done
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
new file mode 100755
index 0000000..c7d0324
--- /dev/null
+++ b/t/t0024-crlf-archive.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='respect crlf in git archive'
+
+. ./test-lib.sh
+UNZIP=${UNZIP:-unzip}
+
+test_expect_success setup '
+
+ git config core.autocrlf true
+
+ printf "CRLF line ending\r\nAnd another\r\n" > sample &&
+ git add sample &&
+
+ test_tick &&
+ git commit -m Initial
+
+'
+
+test_expect_success 'tar archive' '
+
+ git archive --format=tar HEAD |
+ ( mkdir untarred && cd untarred && "$TAR" -xf - )
+
+ test_cmp sample untarred/sample
+
+'
+
+"$UNZIP" -v >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+ say "Skipping ZIP test, because unzip was not found"
+else
+ test_set_prereq UNZIP
+fi
+
+test_expect_success UNZIP 'zip archive' '
+
+ git archive --format=zip HEAD >test.zip &&
+
+ ( mkdir unzipped && cd unzipped && unzip ../test.zip ) &&
+
+ test_cmp sample unzipped/sample
+
+'
+
+test_done
diff --git a/t/t0025-crlf-auto.sh b/t/t0025-crlf-auto.sh
new file mode 100755
index 0000000..f5f67a6
--- /dev/null
+++ b/t/t0025-crlf-auto.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+test_description='CRLF conversion'
+
+. ./test-lib.sh
+
+has_cr() {
+ tr '\015' Q <"$1" | grep Q >/dev/null
+}
+
+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 &&
+ git add . &&
+
+ git commit -m initial &&
+
+ one=`git rev-parse HEAD:one` &&
+ two=`git rev-parse HEAD:two` &&
+ three=`git rev-parse HEAD:three` &&
+
+ echo happy.
+'
+
+test_expect_success 'default settings cause no changes' '
+
+ rm -f .gitattributes tmp one two three &&
+ 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" -a -z "$twodiff" -a -z "$threediff"
+'
+
+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 &&
+ 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"
+'
+
+test_expect_success 'text=true causes a CRLF file to be normalized' '
+
+ rm -f .gitattributes tmp one two three &&
+ echo "two 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"
+'
+
+test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' '
+
+ rm -f .gitattributes tmp one two three &&
+ git config core.autocrlf false &&
+ echo "one eol=crlf" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ onediff=`git diff one` &&
+ test -z "$onediff"
+'
+
+test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=input' '
+
+ rm -f .gitattributes tmp one two three &&
+ git config core.autocrlf input &&
+ echo "one eol=crlf" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ onediff=`git diff one` &&
+ test -z "$onediff"
+'
+
+test_expect_success 'eol=lf gives a normalized file LFs with autocrlf=true' '
+
+ rm -f .gitattributes tmp one two three &&
+ git config core.autocrlf true &&
+ echo "one eol=lf" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ ! has_cr one &&
+ onediff=`git diff one` &&
+ test -z "$onediff"
+'
+
+test_expect_success 'autocrlf=true does not normalize CRLF files' '
+
+ rm -f .gitattributes tmp one two three &&
+ 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" -a -z "$twodiff" -a -z "$threediff"
+'
+
+test_expect_success 'text=auto, autocrlf=true _does_ normalize CRLF files' '
+
+ rm -f .gitattributes tmp one two three &&
+ 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" -a -n "$twodiff" -a -z "$threediff"
+'
+
+test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
+
+ rm -f .gitattributes tmp one two three &&
+ 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"
+'
+
+test_expect_success 'eol=crlf _does_ normalize binary files' '
+
+ rm -f .gitattributes tmp one two three &&
+ echo "three eol=crlf" > .gitattributes &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr three &&
+ threediff=`git diff three` &&
+ test -z "$threediff"
+'
+
+test_done
diff --git a/t/t0026-eol-config.sh b/t/t0026-eol-config.sh
new file mode 100755
index 0000000..f37ac8f
--- /dev/null
+++ b/t/t0026-eol-config.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+test_description='CRLF conversion'
+
+. ./test-lib.sh
+
+has_cr() {
+ tr '\015' Q <"$1" | grep Q >/dev/null
+}
+
+test_expect_success setup '
+
+ git config core.autocrlf false &&
+
+ echo "one text" > .gitattributes
+
+ 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; done >two &&
+ git add . &&
+
+ git commit -m initial &&
+
+ one=`git rev-parse HEAD:one` &&
+ two=`git rev-parse HEAD:two` &&
+
+ echo happy.
+'
+
+test_expect_success 'eol=lf puts LFs in normalized file' '
+
+ rm -f .gitattributes tmp one two &&
+ git config core.eol lf &&
+ git read-tree --reset -u HEAD &&
+
+ ! has_cr one &&
+ ! has_cr two &&
+ onediff=`git diff one` &&
+ twodiff=`git diff two` &&
+ test -z "$onediff" -a -z "$twodiff"
+'
+
+test_expect_success 'eol=crlf puts CRLFs in normalized file' '
+
+ rm -f .gitattributes tmp one two &&
+ git config core.eol crlf &&
+ git read-tree --reset -u HEAD &&
+
+ has_cr one &&
+ ! has_cr two &&
+ onediff=`git diff one` &&
+ twodiff=`git diff two` &&
+ test -z "$onediff" -a -z "$twodiff"
+'
+
+test_expect_success 'autocrlf=true overrides eol=lf' '
+
+ rm -f .gitattributes tmp one two &&
+ git config core.eol lf &&
+ 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` &&
+ test -z "$onediff" -a -z "$twodiff"
+'
+
+test_expect_success 'autocrlf=true overrides unset eol' '
+
+ rm -f .gitattributes tmp one two &&
+ git config --unset-all core.eol &&
+ 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` &&
+ test -z "$onediff" -a -z "$twodiff"
+'
+
+test_done
diff --git a/t/t0030-stripspace.sh b/t/t0030-stripspace.sh
new file mode 100755
index 0000000..ccb0a3c
--- /dev/null
+++ b/t/t0030-stripspace.sh
@@ -0,0 +1,400 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carlos Rica
+#
+
+test_description='git stripspace'
+
+. ./test-lib.sh
+
+t40='A quick brown fox jumps over the lazy do'
+s40=' '
+sss="$s40$s40$s40$s40$s40$s40$s40$s40$s40$s40" # 400
+ttt="$t40$t40$t40$t40$t40$t40$t40$t40$t40$t40" # 400
+
+test_expect_success \
+ 'long lines without spaces should be unchanged' '
+ echo "$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt$ttt$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt$ttt$ttt$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'lines with spaces at the beginning should be unchanged' '
+ echo "$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
+
+ echo "$sss$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
+
+ echo "$sss$sss$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'lines with intermediate spaces should be unchanged' '
+ echo "$ttt$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt$sss$sss$ttt" >expect &&
+ git stripspace <expect >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'consecutive blank lines should be unified' '
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n$ttt$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n$ttt$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt\n\t\n \n\n \t\t\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'only consecutive blank lines should be completely removed' '
+ > expect &&
+
+ printf "\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$sss\n$sss\n$sss\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$sss$sss\n$sss\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "\n$sss\n$sss$sss\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$sss$sss$sss$sss\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "\n$sss$sss$sss$sss\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "\n\n$sss$sss$sss$sss\n" | git stripspace >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'consecutive blank lines at the beginning should be removed' '
+ printf "$ttt\n" > expect &&
+ printf "\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n" > expect &&
+ printf "\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt\n" > expect &&
+ printf "\n\n\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt$ttt\n" > expect &&
+ printf "\n\n\n$ttt$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt$ttt$ttt\n" > expect &&
+ printf "\n\n\n$ttt$ttt$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n" > expect &&
+
+ printf "$sss\n$sss\n$sss\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "\n$sss\n$sss$sss\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$sss$sss\n$sss\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$sss$sss$sss\n\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "\n$sss$sss$sss\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "\n\n$sss$sss$sss\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'consecutive blank lines at the end should be removed' '
+ printf "$ttt\n" > expect &&
+ printf "$ttt\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n" > expect &&
+ printf "$ttt\n\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt\n" > expect &&
+ printf "$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt$ttt$ttt\n" > expect &&
+ printf "$ttt$ttt$ttt$ttt\n\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n" > expect &&
+
+ printf "$ttt\n$sss\n$sss\n$sss\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n$sss\n$sss$sss\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n$sss$sss\n$sss\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n$sss$sss$sss\n\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n$sss$sss$sss\n\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n\n\n$sss$sss$sss\n" | git stripspace >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'text without newline at end should end with newline' '
+ test `printf "$ttt" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$ttt" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$ttt$ttt" | git stripspace | wc -l` -gt 0
+'
+
+# text plus spaces at the end:
+
+test_expect_success \
+ 'text plus spaces without newline at end should end with newline' '
+ test `printf "$ttt$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$ttt$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$sss$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$ttt$sss$sss" | git stripspace | wc -l` -gt 0 &&
+ test `printf "$ttt$sss$sss$sss" | git stripspace | wc -l` -gt 0
+'
+
+test_expect_success \
+ 'text plus spaces without newline at end should not show spaces' '
+ ! (printf "$ttt$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (printf "$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (printf "$ttt$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (printf "$ttt$sss$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (printf "$ttt$ttt$sss$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (printf "$ttt$sss$sss$sss" | git stripspace | grep " " >/dev/null)
+'
+
+test_expect_success \
+ 'text plus spaces without newline should show the correct lines' '
+ printf "$ttt\n" >expect &&
+ printf "$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n" >expect &&
+ printf "$ttt$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n" >expect &&
+ printf "$ttt$sss$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'text plus spaces at end should not show spaces' '
+ ! (echo "$ttt$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (echo "$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (echo "$ttt$ttt$ttt$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (echo "$ttt$sss$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (echo "$ttt$ttt$sss$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (echo "$ttt$sss$sss$sss" | git stripspace | grep " " >/dev/null)
+'
+
+test_expect_success \
+ 'text plus spaces at end should be cleaned and newline must remain' '
+ echo "$ttt" >expect &&
+ echo "$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt" >expect &&
+ echo "$ttt$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt" >expect &&
+ echo "$ttt$sss$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt$ttt" >expect &&
+ echo "$ttt$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt$ttt" >expect &&
+ echo "$ttt$ttt$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ echo "$ttt$ttt$ttt" >expect &&
+ echo "$ttt$ttt$ttt$sss" | git stripspace >actual &&
+ test_cmp expect actual
+'
+
+# spaces only:
+
+test_expect_success \
+ 'spaces with newline at end should be replaced with empty string' '
+ printf "" >expect &&
+
+ echo | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ echo "$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ echo "$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ echo "$sss$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ echo "$sss$sss$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'spaces without newline at end should not show spaces' '
+ ! (printf "" | git stripspace | grep " " >/dev/null) &&
+ ! (printf "$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (printf "$sss$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (printf "$sss$sss$sss" | git stripspace | grep " " >/dev/null) &&
+ ! (printf "$sss$sss$sss$sss" | git stripspace | grep " " >/dev/null)
+'
+
+test_expect_success \
+ 'spaces without newline at end should be replaced with empty string' '
+ printf "" >expect &&
+
+ printf "" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$sss$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$sss$sss$sss$sss" | git stripspace >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'consecutive text lines should be unchanged' '
+ printf "$ttt$ttt\n$ttt\n" >expect &&
+ printf "$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n$ttt$ttt\n$ttt\n" >expect &&
+ printf "$ttt\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" >expect &&
+ printf "$ttt\n$ttt\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" >expect &&
+ printf "$ttt\n$ttt\n\n$ttt$ttt\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" >expect &&
+ printf "$ttt$ttt\n\n$ttt\n$ttt$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual &&
+
+ printf "$ttt\n$ttt$ttt\n\n$ttt\n" >expect &&
+ printf "$ttt\n$ttt$ttt\n\n$ttt\n" | git stripspace >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'strip comments, too' '
+ test ! -z "$(echo "# comment" | git stripspace)" &&
+ test -z "$(echo "# comment" | git stripspace -s)"
+'
+
+test_done
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
new file mode 100755
index 0000000..2092450
--- /dev/null
+++ b/t/t0040-parse-options.sh
@@ -0,0 +1,340 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Schindelin
+#
+
+test_description='our own option parser'
+
+. ./test-lib.sh
+
+cat > expect << EOF
+usage: test-parse-options <options>
+
+ -b, --boolean get a boolean
+ -4, --or4 bitwise-or boolean with ...0100
+ --neg-or4 same as --no-or4
+
+ -i, --integer <n> get a integer
+ -j <n> get a integer, too
+ --set23 set integer to 23
+ -t <time> get timestamp of <time>
+ -L, --length <str> get length of <str>
+ -F, --file <FILE> set file to <FILE>
+
+String options
+ -s, --string <string>
+ get a string
+ --string2 <str> get another string
+ --st <st> get another string (pervert ordering)
+ -o <str> get another string
+ --default-string set string to default
+
+Magic arguments
+ --quux means --quux
+ -NUM set integer to NUM
+ + same as -b
+ --ambiguous positive ambiguity
+ --no-ambiguous negative ambiguity
+
+Standard options
+ --abbrev[=<n>] use <n> digits to display SHA-1s
+ -v, --verbose be verbose
+ -n, --dry-run dry run
+ -q, --quiet be quiet
+
+EOF
+
+test_expect_success 'test help' '
+ test_must_fail test-parse-options -h > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+mv expect expect.err
+
+cat > expect << EOF
+boolean: 2
+integer: 1729
+timestamp: 0
+string: 123
+abbrev: 7
+verbose: 2
+quiet: no
+dry run: yes
+file: prefix/my.file
+EOF
+
+test_expect_success 'short options' '
+ test-parse-options -s123 -b -i 1729 -b -vv -n -F my.file \
+ > output 2> output.err &&
+ test_cmp expect output &&
+ test ! -s output.err
+'
+
+cat > expect << EOF
+boolean: 2
+integer: 1729
+timestamp: 0
+string: 321
+abbrev: 10
+verbose: 2
+quiet: no
+dry run: no
+file: prefix/fi.le
+EOF
+
+test_expect_success 'long options' '
+ test-parse-options --boolean --integer 1729 --boolean --string2=321 \
+ --verbose --verbose --no-dry-run --abbrev=10 --file fi.le\
+ > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+test_expect_success 'missing required value' '
+ test-parse-options -s;
+ test $? = 129 &&
+ test-parse-options --string;
+ test $? = 129 &&
+ test-parse-options --file;
+ test $? = 129
+'
+
+cat > expect << EOF
+boolean: 1
+integer: 13
+timestamp: 0
+string: 123
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+arg 00: a1
+arg 01: b1
+arg 02: --boolean
+EOF
+
+test_expect_success 'intermingled arguments' '
+ test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \
+ > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+boolean: 0
+integer: 2
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'unambiguously abbreviated option' '
+ test-parse-options --int 2 --boolean --no-bo > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+test_expect_success 'unambiguously abbreviated option with "="' '
+ test-parse-options --int=2 > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+test_expect_success 'ambiguously abbreviated option' '
+ test-parse-options --strin 123;
+ test $? = 129
+'
+
+cat > expect << EOF
+boolean: 0
+integer: 0
+timestamp: 0
+string: 123
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'non ambiguous option (after two options it abbreviates)' '
+ test-parse-options --st 123 > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+cat > typo.err << EOF
+error: did you mean \`--boolean\` (with two dashes ?)
+EOF
+
+test_expect_success 'detect possible typos' '
+ test_must_fail test-parse-options -boolean > output 2> output.err &&
+ test ! -s output &&
+ test_cmp typo.err output.err
+'
+
+cat > expect <<EOF
+boolean: 0
+integer: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+arg 00: --quux
+EOF
+
+test_expect_success 'keep some options as arguments' '
+ test-parse-options --quux > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+boolean: 0
+integer: 0
+timestamp: 1
+string: default
+abbrev: 7
+verbose: 0
+quiet: yes
+dry run: no
+file: (not set)
+arg 00: foo
+EOF
+
+test_expect_success 'OPT_DATE() and OPT_SET_PTR() work' '
+ test-parse-options -t "1970-01-01 00:00:01 +0000" --default-string \
+ foo -q > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+Callback: "four", 0
+boolean: 5
+integer: 4
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '
+ test-parse-options --length=four -b -4 > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+Callback: "not set", 1
+EOF
+
+test_expect_success 'OPT_CALLBACK() and callback errors work' '
+ test_must_fail test-parse-options --no-length > output 2> output.err &&
+ test_cmp expect output &&
+ test_cmp expect.err output.err
+'
+
+cat > expect <<EOF
+boolean: 1
+integer: 23
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '
+ test-parse-options --set23 -bbbbb --no-or4 > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+test_expect_success 'OPT_NEGBIT() and OPT_SET_INT() work' '
+ test-parse-options --set23 -bbbbb --neg-or4 > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+boolean: 6
+integer: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'OPT_BIT() works' '
+ test-parse-options -bb --or4 > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+test_expect_success 'OPT_NEGBIT() works' '
+ test-parse-options -bb --no-neg-or4 > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+test_expect_success 'OPT_BOOLEAN() with PARSE_OPT_NODASH works' '
+ test-parse-options + + + + + + > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+boolean: 0
+integer: 12345
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'OPT_NUMBER_CALLBACK() works' '
+ test-parse-options -12345 > output 2> output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+boolean: 0
+integer: 0
+timestamp: 0
+string: (not set)
+abbrev: 7
+verbose: 0
+quiet: no
+dry run: no
+file: (not set)
+EOF
+
+test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
+ test-parse-options --no-ambig >output 2>output.err &&
+ test ! -s output.err &&
+ test_cmp expect output
+'
+
+test_done
diff --git a/t/t0050-filesystem.sh b/t/t0050-filesystem.sh
new file mode 100755
index 0000000..41df6bc
--- /dev/null
+++ b/t/t0050-filesystem.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+test_description='Various filesystem issues'
+
+. ./test-lib.sh
+
+auml=`printf '\xc3\xa4'`
+aumlcdiar=`printf '\x61\xcc\x88'`
+
+case_insensitive=
+unibad=
+no_symlinks=
+test_expect_success 'see what we expect' '
+
+ test_case=test_expect_success
+ test_unicode=test_expect_success
+ mkdir junk &&
+ echo good >junk/CamelCase &&
+ echo bad >junk/camelcase &&
+ if test "$(cat junk/CamelCase)" != good
+ then
+ test_case=test_expect_failure
+ case_insensitive=t
+ fi &&
+ rm -fr junk &&
+ mkdir junk &&
+ >junk/"$auml" &&
+ case "$(cd junk && echo *)" in
+ "$aumlcdiar")
+ test_unicode=test_expect_failure
+ unibad=t
+ ;;
+ *) ;;
+ esac &&
+ rm -fr junk &&
+ {
+ ln -s x y 2> /dev/null &&
+ test -h y 2> /dev/null ||
+ no_symlinks=1
+ rm -f y
+ }
+'
+
+test "$case_insensitive" &&
+ say "will test on a case insensitive filesystem"
+test "$unibad" &&
+ say "will test on a unicode corrupting filesystem"
+test "$no_symlinks" &&
+ say "will test on a filesystem lacking symbolic links"
+
+if test "$case_insensitive"
+then
+test_expect_success "detection of case insensitive filesystem during repo init" '
+
+ test $(git config --bool core.ignorecase) = true
+'
+else
+test_expect_success "detection of case insensitive filesystem during repo init" '
+
+ test_must_fail git config --bool core.ignorecase >/dev/null ||
+ test $(git config --bool core.ignorecase) = false
+'
+fi
+
+if test "$no_symlinks"
+then
+test_expect_success "detection of filesystem w/o symlink support during repo init" '
+
+ v=$(git config --bool core.symlinks) &&
+ test "$v" = false
+'
+else
+test_expect_success "detection of filesystem w/o symlink support during repo init" '
+
+ test_must_fail git config --bool core.symlinks ||
+ test "$(git config --bool core.symlinks)" = true
+'
+fi
+
+test_expect_success "setup case tests" '
+
+ git config core.ignorecase true &&
+ touch camelcase &&
+ git add camelcase &&
+ git commit -m "initial" &&
+ git tag initial &&
+ git checkout -b topic &&
+ git mv camelcase tmp &&
+ git mv tmp CamelCase &&
+ git commit -m "rename" &&
+ git checkout -f master
+
+'
+
+$test_case 'rename (case change)' '
+
+ git mv camelcase CamelCase &&
+ git commit -m "rename"
+
+'
+
+$test_case 'merge (case change)' '
+
+ rm -f CamelCase &&
+ rm -f camelcase &&
+ git reset --hard initial &&
+ git merge topic
+
+'
+
+
+
+test_expect_failure 'add (with different case)' '
+
+ git reset --hard initial &&
+ rm camelcase &&
+ echo 1 >CamelCase &&
+ git add CamelCase &&
+ camel=$(git ls-files | grep -i camelcase) &&
+ test $(echo "$camel" | wc -l) = 1 &&
+ test "z$(git cat-file blob :$camel)" = z1
+
+'
+
+test_expect_success "setup unicode normalization tests" '
+
+ test_create_repo unicode &&
+ cd unicode &&
+ touch "$aumlcdiar" &&
+ git add "$aumlcdiar" &&
+ git commit -m initial
+ git tag initial &&
+ git checkout -b topic &&
+ git mv $aumlcdiar tmp &&
+ git mv tmp "$auml" &&
+ git commit -m rename &&
+ git checkout -f master
+
+'
+
+$test_unicode 'rename (silent unicode normalization)' '
+
+ git mv "$aumlcdiar" "$auml" &&
+ git commit -m rename
+
+'
+
+$test_unicode 'merge (silent unicode normalization)' '
+
+ git reset --hard initial &&
+ git merge topic
+
+'
+
+test_done
diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh
new file mode 100755
index 0000000..0c6ff56
--- /dev/null
+++ b/t/t0055-beyond-symlinks.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='update-index and add refuse to add beyond symlinks'
+
+. ./test-lib.sh
+
+test_expect_success SYMLINKS setup '
+ >a &&
+ mkdir b &&
+ ln -s b c &&
+ >c/d &&
+ git update-index --add a b/d
+'
+
+test_expect_success SYMLINKS 'update-index --add beyond symlinks' '
+ test_must_fail git update-index --add c/d &&
+ ! ( git ls-files | grep c/d )
+'
+
+test_expect_success SYMLINKS 'add beyond symlinks' '
+ test_must_fail git add c/d &&
+ ! ( git ls-files | grep c/d )
+'
+
+test_done
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
new file mode 100755
index 0000000..53cf1f8
--- /dev/null
+++ b/t/t0060-path-utils.sh
@@ -0,0 +1,142 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 David Reiss
+#
+
+test_description='Test various path utilities'
+
+. ./test-lib.sh
+
+norm_path() {
+ test_expect_success $3 "normalize path: $1 => $2" \
+ "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
+}
+
+# On Windows, we are using MSYS's bash, which mangles the paths.
+# Absolute paths are anchored at the MSYS installation directory,
+# which means that the path / accounts for this many characters:
+rootoff=$(test-path-utils normalize_path_copy / | wc -c)
+# Account for the trailing LF:
+if test $rootoff = 2; then
+ rootoff= # we are on Unix
+else
+ rootoff=$(($rootoff-1))
+fi
+
+ancestor() {
+ # We do some math with the expected ancestor length.
+ expected=$3
+ if test -n "$rootoff" && test "x$expected" != x-1; then
+ expected=$(($expected+$rootoff))
+ fi
+ test_expect_success "longest ancestor: $1 $2 => $expected" \
+ "actual=\$(test-path-utils longest_ancestor_length '$1' '$2') &&
+ test \"\$actual\" = '$expected'"
+}
+
+# Absolute path tests must be skipped on Windows because due to path mangling
+# the test program never sees a POSIX-style absolute path
+case $(uname -s) in
+*MINGW*)
+ ;;
+*)
+ test_set_prereq POSIX
+ ;;
+esac
+
+norm_path "" ""
+norm_path . ""
+norm_path ./ ""
+norm_path ./. ""
+norm_path ./.. ++failed++
+norm_path ../. ++failed++
+norm_path ./../.// ++failed++
+norm_path dir/.. ""
+norm_path dir/sub/../.. ""
+norm_path dir/sub/../../.. ++failed++
+norm_path dir dir
+norm_path dir// dir/
+norm_path ./dir dir
+norm_path dir/. dir/
+norm_path dir///./ dir/
+norm_path dir//sub/.. dir/
+norm_path dir/sub/../ dir/
+norm_path dir/sub/../. dir/
+norm_path dir/s1/../s2/ dir/s2/
+norm_path d1/s1///s2/..//../s3/ d1/s3/
+norm_path d1/s1//../s2/../../d2 d2
+norm_path d1/.../d2 d1/.../d2
+norm_path d1/..././../d2 d1/d2
+
+norm_path / / POSIX
+norm_path // / POSIX
+norm_path /// / POSIX
+norm_path /. / POSIX
+norm_path /./ / POSIX
+norm_path /./.. ++failed++ POSIX
+norm_path /../. ++failed++ POSIX
+norm_path /./../.// ++failed++ POSIX
+norm_path /dir/.. / POSIX
+norm_path /dir/sub/../.. / POSIX
+norm_path /dir/sub/../../.. ++failed++ POSIX
+norm_path /dir /dir POSIX
+norm_path /dir// /dir/ POSIX
+norm_path /./dir /dir POSIX
+norm_path /dir/. /dir/ POSIX
+norm_path /dir///./ /dir/ POSIX
+norm_path /dir//sub/.. /dir/ POSIX
+norm_path /dir/sub/../ /dir/ POSIX
+norm_path //dir/sub/../. /dir/ POSIX
+norm_path /dir/s1/../s2/ /dir/s2/ POSIX
+norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX
+norm_path /d1/s1//../s2/../../d2 /d2 POSIX
+norm_path /d1/.../d2 /d1/.../d2 POSIX
+norm_path /d1/..././../d2 /d1/d2 POSIX
+
+ancestor / "" -1
+ancestor / / -1
+ancestor /foo "" -1
+ancestor /foo : -1
+ancestor /foo ::. -1
+ancestor /foo ::..:: -1
+ancestor /foo / 0
+ancestor /foo /fo -1
+ancestor /foo /foo -1
+ancestor /foo /foo/ -1
+ancestor /foo /bar -1
+ancestor /foo /bar/ -1
+ancestor /foo /foo/bar -1
+ancestor /foo /foo:/bar/ -1
+ancestor /foo /foo/:/bar/ -1
+ancestor /foo /foo::/bar/ -1
+ancestor /foo /:/foo:/bar/ 0
+ancestor /foo /foo:/:/bar/ 0
+ancestor /foo /:/bar/:/foo 0
+ancestor /foo/bar "" -1
+ancestor /foo/bar / 0
+ancestor /foo/bar /fo -1
+ancestor /foo/bar foo -1
+ancestor /foo/bar /foo 4
+ancestor /foo/bar /foo/ 4
+ancestor /foo/bar /foo/ba -1
+ancestor /foo/bar /:/fo 0
+ancestor /foo/bar /foo:/foo/ba 4
+ancestor /foo/bar /bar -1
+ancestor /foo/bar /bar/ -1
+ancestor /foo/bar /fo: -1
+ancestor /foo/bar :/fo -1
+ancestor /foo/bar /foo:/bar/ 4
+ancestor /foo/bar /:/foo:/bar/ 4
+ancestor /foo/bar /foo:/:/bar/ 4
+ancestor /foo/bar /:/bar/:/fo 0
+ancestor /foo/bar /:/bar/ 0
+ancestor /foo/bar .:/foo/. 4
+ancestor /foo/bar .:/foo/.:.: 4
+ancestor /foo/bar /foo/./:.:/bar 4
+ancestor /foo/bar .:/bar -1
+
+test_expect_success 'strip_path_suffix' '
+ test c:/msysgit = $(test-path-utils strip_path_suffix \
+ c:/msysgit/libexec//git-core libexec/git-core)
+'
+test_done
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
new file mode 100755
index 0000000..10b26e4
--- /dev/null
+++ b/t/t0061-run-command.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Ilari Liusvaara
+#
+
+test_description='Test run command'
+
+. ./test-lib.sh
+
+test_expect_success 'start_command reports ENOENT' '
+ test-run-command start-command-ENOENT ./does-not-exist
+'
+
+test_done
diff --git a/t/t0070-fundamental.sh b/t/t0070-fundamental.sh
new file mode 100755
index 0000000..680d7d6
--- /dev/null
+++ b/t/t0070-fundamental.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+test_description='check that the most basic functions work
+
+
+Verify wrappers and compatibility functions.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'character classes (isspace, isalpha etc.)' '
+ test-ctype
+'
+
+test_done
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
new file mode 100755
index 0000000..315b9b3
--- /dev/null
+++ b/t/t0100-previous.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='previous branch syntax @{-n}'
+
+. ./test-lib.sh
+
+test_expect_success 'branch -d @{-1}' '
+ test_commit A &&
+ git checkout -b junk &&
+ git checkout - &&
+ test "$(git symbolic-ref HEAD)" = refs/heads/master &&
+ git branch -d @{-1} &&
+ test_must_fail git rev-parse --verify refs/heads/junk
+'
+
+test_expect_success 'branch -d @{-12} when there is not enough switches yet' '
+ git reflog expire --expire=now &&
+ git checkout -b junk2 &&
+ git checkout - &&
+ test "$(git symbolic-ref HEAD)" = refs/heads/master &&
+ test_must_fail git branch -d @{-12} &&
+ git rev-parse --verify refs/heads/master
+'
+
+test_expect_success 'merge @{-1}' '
+ git checkout A &&
+ test_commit B &&
+ git checkout A &&
+ test_commit C &&
+ git branch -f master B &&
+ git branch -f other &&
+ git checkout other &&
+ git checkout master &&
+ git merge @{-1} &&
+ git cat-file commit HEAD | grep "Merge branch '\''other'\''"
+'
+
+test_expect_success 'merge @{-1} when there is not enough switches yet' '
+ git reflog expire --expire=now &&
+ git checkout -f master &&
+ git reset --hard B &&
+ git branch -f other C &&
+ git checkout other &&
+ git checkout master &&
+ test_must_fail git merge @{-12}
+'
+
+test_done
+
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
new file mode 100755
index 0000000..a1998b5
--- /dev/null
+++ b/t/t0101-at-syntax.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='various @{whatever} syntax tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit one &&
+ test_commit two
+'
+
+check_at() {
+ echo "$2" >expect &&
+ git log -1 --format=%s "$1" >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success '@{0} shows current' '
+ check_at @{0} two
+'
+
+test_expect_success '@{1} shows old' '
+ check_at @{1} one
+'
+
+test_expect_success '@{now} shows current' '
+ check_at @{now} two
+'
+
+test_expect_success '@{2001-09-17} (before the first commit) shows old' '
+ check_at @{2001-09-17} one
+'
+
+test_expect_success 'silly approxidates work' '
+ check_at @{3.hot.dogs.on.2001-09-17} one
+'
+
+test_expect_success 'notice misspelled upstream' '
+ test_must_fail git log -1 --format=%s @{usptream}
+'
+
+test_expect_success 'complain about total nonsense' '
+ test_must_fail git log -1 --format=%s @{utter.bogosity}
+'
+
+test_done
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
new file mode 100755
index 0000000..4f17172
--- /dev/null
+++ b/t/t1000-read-tree-m-3way.sh
@@ -0,0 +1,528 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Three way merge with read-tree -m
+
+This test tries three-way merge with read-tree -m
+
+There is one ancestor (called O for Original) and two branches A
+and B derived from it. We want to do a 3-way merge between A and
+B, using O as the common ancestor.
+
+ merge A O B
+
+Decisions are made by comparing contents of O, A and B pathname
+by pathname. The result is determined by the following guiding
+principle:
+
+ - If only A does something to it and B does not touch it, take
+ whatever A does.
+
+ - If only B does something to it and A does not touch it, take
+ whatever B does.
+
+ - If both A and B does something but in the same way, take
+ whatever they do.
+
+ - If A and B does something but different things, we need a
+ 3-way merge:
+
+ - We cannot do anything about the following cases:
+
+ * O does not have it. A and B both must be adding to the
+ same path independently.
+
+ * A deletes it. B must be modifying.
+
+ - Otherwise, A and B are modifying. Run 3-way merge.
+
+First, the case matrix.
+
+ - Vertical axis is for A'\''s actions.
+ - Horizontal axis is for B'\''s actions.
+
+.----------------------------------------------------------------.
+| A B | No Action | Delete | Modify | Add |
+|------------+------------+------------+------------+------------|
+| No Action | | | | |
+| | select O | delete | select B | select B |
+| | | | | |
+|------------+------------+------------+------------+------------|
+| Delete | | | ********** | can |
+| | delete | delete | merge | not |
+| | | | | happen |
+|------------+------------+------------+------------+------------|
+| Modify | | ********** | ?????????? | can |
+| | select A | merge | select A=B | not |
+| | | | merge | happen |
+|------------+------------+------------+------------+------------|
+| Add | | can | can | ?????????? |
+| | select A | not | not | select A=B |
+| | | happen | happen | merge |
+.----------------------------------------------------------------.
+
+In addition:
+
+ SS: a special case of MM, where A and B makes the same modification.
+ LL: a special case of AA, where A and B creates the same file.
+ TT: a special case of MM, where A and B makes mergeable changes.
+ DF: a special case, where A makes a directory and B makes a file.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
+
+################################################################
+# Trivial "majority when 3 stages exist" merge plus #2ALT, #3ALT
+# and #5ALT trivial merges.
+
+cat >expected <<\EOF
+100644 X 2 AA
+100644 X 3 AA
+100644 X 0 AN
+100644 X 1 DD
+100644 X 3 DF
+100644 X 2 DF/DF
+100644 X 1 DM
+100644 X 3 DM
+100644 X 1 DN
+100644 X 3 DN
+100644 X 0 LL
+100644 X 1 MD
+100644 X 2 MD
+100644 X 1 MM
+100644 X 2 MM
+100644 X 3 MM
+100644 X 0 MN
+100644 X 0 NA
+100644 X 1 ND
+100644 X 2 ND
+100644 X 0 NM
+100644 X 0 NN
+100644 X 0 SS
+100644 X 1 TT
+100644 X 2 TT
+100644 X 3 TT
+100644 X 2 Z/AA
+100644 X 3 Z/AA
+100644 X 0 Z/AN
+100644 X 1 Z/DD
+100644 X 1 Z/DM
+100644 X 3 Z/DM
+100644 X 1 Z/DN
+100644 X 3 Z/DN
+100644 X 1 Z/MD
+100644 X 2 Z/MD
+100644 X 1 Z/MM
+100644 X 2 Z/MM
+100644 X 3 Z/MM
+100644 X 0 Z/MN
+100644 X 0 Z/NA
+100644 X 1 Z/ND
+100644 X 2 Z/ND
+100644 X 0 Z/NM
+100644 X 0 Z/NN
+EOF
+
+check_result () {
+ 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 &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+# This starts out with the first head, which is the normal
+# patch submitter behaviour.
+test_expect_success \
+ '3-way merge with git read-tree -m, match H' \
+ "rm -fr [NDMALTS][NDMALTSF] Z &&
+ rm .git/index &&
+ git read-tree $tree_A &&
+ git checkout-index -f -u -a &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+: <<\END_OF_CASE_TABLE
+
+We have so far tested only empty index and clean-and-matching-A index
+case which are trivial. Make sure index requirements are also
+checked.
+
+"git read-tree -m O A B"
+
+ O A B result index requirements
+-------------------------------------------------------------------
+ 1 missing missing missing - must not exist.
+ ------------------------------------------------------------------
+ 2 missing missing exists take B* must match B, if exists.
+ ------------------------------------------------------------------
+ 3 missing exists missing take A* must match A, if exists.
+ ------------------------------------------------------------------
+ 4 missing exists A!=B no merge must match A and be
+ up-to-date, if exists.
+ ------------------------------------------------------------------
+ 5 missing exists A==B take A must match A, if exists.
+ ------------------------------------------------------------------
+ 6 exists missing missing remove must not exist.
+ ------------------------------------------------------------------
+ 7 exists missing O!=B no merge must not exist.
+ ------------------------------------------------------------------
+ 8 exists missing O==B remove must not exist.
+ ------------------------------------------------------------------
+ 9 exists O!=A missing no merge must match A and be
+ up-to-date, if exists.
+ ------------------------------------------------------------------
+ 10 exists O==A missing no merge must match A
+ ------------------------------------------------------------------
+ 11 exists O!=A O!=B no merge must match A and be
+ A!=B up-to-date, if exists.
+ ------------------------------------------------------------------
+ 12 exists O!=A O!=B take A must match A, if exists.
+ A==B
+ ------------------------------------------------------------------
+ 13 exists O!=A O==B take A must match A, if exists.
+ ------------------------------------------------------------------
+ 14 exists O==A O!=B take B if exists, must either (1)
+ match A and be up-to-date,
+ or (2) match B.
+ ------------------------------------------------------------------
+ 15 exists O==A O==B take B must match A if exists.
+ ------------------------------------------------------------------
+ 16 exists O==A O==B barf must match A if exists.
+ *multi* in one in another
+-------------------------------------------------------------------
+
+Note: we need to be careful in case 2 and 3. The tree A may contain
+DF (file) when tree B require DF to be a directory by having DF/DF
+(file).
+
+END_OF_CASE_TABLE
+
+test_expect_success '1 - must not have an entry not in A.' "
+ rm -f .git/index XX &&
+ echo XX >XX &&
+ git update-index --add XX &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+test_expect_success \
+ '2 - must match B in !O && !A && B case.' \
+ "rm -f .git/index NA &&
+ cp .orig-B/NA NA &&
+ git update-index --add NA &&
+ git read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '2 - matching B alone is OK in !O && !A && B case.' \
+ "rm -f .git/index NA &&
+ cp .orig-B/NA NA &&
+ git update-index --add NA &&
+ echo extra >>NA &&
+ git read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_success \
+ '3 - must match A in !O && A && !B case.' \
+ "rm -f .git/index AN &&
+ cp .orig-A/AN AN &&
+ git update-index --add AN &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '3 - matching A alone is OK in !O && A && !B case.' \
+ "rm -f .git/index AN &&
+ cp .orig-A/AN AN &&
+ git update-index --add AN &&
+ echo extra >>AN &&
+ git read-tree -m $tree_O $tree_A $tree_B"
+
+test_expect_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 &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+test_expect_success \
+ '4 - must match and be up-to-date in !O && A && B && A!=B case.' \
+ "rm -f .git/index AA &&
+ cp .orig-A/AA AA &&
+ git update-index --add AA &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_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 &&
+ test_must_fail git read-tree -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 &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+test_expect_success \
+ '5 - must match in !O && A && B && A==B case.' \
+ "rm -f .git/index LL &&
+ cp .orig-A/LL LL &&
+ git update-index --add LL &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '5 - must match in !O && A && B && A==B case.' \
+ "rm -f .git/index LL &&
+ cp .orig-A/LL LL &&
+ git update-index --add LL &&
+ echo extra >>LL &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_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 &&
+ test_must_fail git read-tree -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 &&
+ test_must_fail git read-tree -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 &&
+ test_must_fail git read-tree -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 &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+test_expect_success \
+ '9 - must match and be up-to-date in O && A && !B && O!=A case' \
+ "rm -f .git/index MD &&
+ cp .orig-A/MD MD &&
+ git update-index --add MD &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_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 &&
+ test_must_fail git read-tree -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 &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+test_expect_success \
+ '10 - must match and be up-to-date in O && A && !B && O==A case' \
+ "rm -f .git/index ND &&
+ cp .orig-A/ND ND &&
+ git update-index --add ND &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_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 &&
+ test_must_fail git read-tree -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 &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+test_expect_success \
+ '11 - must match and be up-to-date in O && A && B && O!=A && O!=B && A!=B case' \
+ "rm -f .git/index MM &&
+ cp .orig-A/MM MM &&
+ git update-index --add MM &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_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 &&
+ test_must_fail git read-tree -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 &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+test_expect_success \
+ '12 - must match A in O && A && B && O!=A && A==B case' \
+ "rm -f .git/index SS &&
+ cp .orig-A/SS SS &&
+ git update-index --add SS &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '12 - must match A in O && A && B && O!=A && A==B case' \
+ "rm -f .git/index SS &&
+ cp .orig-A/SS SS &&
+ git update-index --add SS &&
+ echo extra >>SS &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_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 &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+test_expect_success \
+ '13 - must match A in O && A && B && O!=A && O==B case' \
+ "rm -f .git/index MN &&
+ cp .orig-A/MN MN &&
+ git update-index --add MN &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '13 - must match A in O && A && B && O!=A && O==B case' \
+ "rm -f .git/index MN &&
+ cp .orig-A/MN MN &&
+ git update-index --add MN &&
+ echo extra >>MN &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '14 - must match and be up-to-date in O && A && B && O==A && O!=B case' \
+ "rm -f .git/index NM &&
+ cp .orig-A/NM NM &&
+ git update-index --add NM &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '14 - may match B in O && A && B && O==A && O!=B case' \
+ "rm -f .git/index NM &&
+ cp .orig-B/NM NM &&
+ git update-index --add NM &&
+ echo extra >>NM &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_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 &&
+ test_must_fail git read-tree -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 &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+test_expect_success \
+ '15 - must match A in O && A && B && O==A && O==B case' \
+ "rm -f .git/index NN &&
+ cp .orig-A/NN NN &&
+ git update-index --add NN &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_success \
+ '15 - must match A in O && A && B && O==A && O==B case' \
+ "rm -f .git/index NN &&
+ cp .orig-A/NN NN &&
+ git update-index --add NN &&
+ echo extra >>NN &&
+ git read-tree -m $tree_O $tree_A $tree_B &&
+ check_result"
+
+test_expect_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 &&
+ test_must_fail git read-tree -m $tree_O $tree_A $tree_B
+"
+
+# #16
+test_expect_success \
+ '16 - A matches in one and B matches in another.' \
+ 'rm -f .git/index F16 &&
+ echo F16 >F16 &&
+ git update-index --add F16 &&
+ tree0=`git write-tree` &&
+ echo E16 >F16 &&
+ git update-index F16 &&
+ tree1=`git write-tree` &&
+ git read-tree -m $tree0 $tree1 $tree1 $tree0 &&
+ git ls-files --stage'
+
+test_done
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
new file mode 100755
index 0000000..0c562bb
--- /dev/null
+++ b/t/t1001-read-tree-m-2way.sh
@@ -0,0 +1,409 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Two way merge with read-tree -m $H $M
+
+This test tries two-way merge (aka fast-forward with carry forward).
+
+There is the head (called H) and another commit (called M), which is
+simply ahead of H. The index and the work tree contains a state that
+is derived from H, but may also have local changes. This test checks
+all the combinations described in the two-tree merge "carry forward"
+rules, found in <Documentation/git read-tree.txt>.
+
+In the test, these paths are used:
+ bozbar - in H, stays in M, modified from bozbar to gnusto
+ frotz - not in H added in M
+ nitfol - in H, stays in M unmodified
+ rezrov - in H, deleted in M
+ yomin - not in H nor M
+'
+. ./test-lib.sh
+
+read_tree_twoway () {
+ git read-tree -m "$1" "$2" && git ls-files --stage
+}
+
+compare_change () {
+ sed -n >current \
+ -e '/^--- /d; /^+++ /d; /^@@ /d;' \
+ -e 's/^\([-+][0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /p' \
+ "$1"
+ test_cmp expected current
+}
+
+check_cache_at () {
+ clean_if_empty=`git diff-files -- "$1"`
+ case "$clean_if_empty" in
+ '') echo "$1: clean" ;;
+ ?*) echo "$1: dirty" ;;
+ esac
+ case "$2,$clean_if_empty" in
+ clean,) : ;;
+ clean,?*) false ;;
+ dirty,) false ;;
+ dirty,?*) : ;;
+ esac
+}
+
+cat >bozbar-old <<\EOF
+This is a sample file used in two-way fast-forward merge
+tests. Its second line ends with a magic word bozbar
+which will be modified by the merged head to gnusto.
+It has some extra lines so that external tools can
+successfully merge independent changes made to later
+lines (such as this one), avoiding line conflicts.
+EOF
+
+sed -e 's/bozbar/gnusto (earlier bozbar)/' bozbar-old >bozbar-new
+
+test_expect_success \
+ setup \
+ 'echo frotz >frotz &&
+ echo nitfol >nitfol &&
+ cat bozbar-old >bozbar &&
+ echo rezrov >rezrov &&
+ echo yomin >yomin &&
+ git update-index --add nitfol bozbar rezrov &&
+ treeH=`git write-tree` &&
+ echo treeH $treeH &&
+ git ls-tree $treeH &&
+
+ cat bozbar-new >bozbar &&
+ git update-index --add frotz bozbar --force-remove rezrov &&
+ git ls-files --stage >M.out &&
+ treeM=`git write-tree` &&
+ echo treeM $treeM &&
+ git ls-tree $treeM &&
+ git diff-tree $treeH $treeM'
+
+test_expect_success \
+ '1, 2, 3 - no carry forward' \
+ 'rm -f .git/index &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >1-3.out &&
+ 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 &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ git update-index --add yomin &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >4.out || return 1
+ 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 &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo yomin >yomin &&
+ git update-index --add yomin &&
+ echo yomin yomin >yomin &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >5.out || return 1
+ 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 &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ git update-index --add frotz &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >6.out &&
+ 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 &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo frotz >frotz &&
+ git update-index --add frotz &&
+ echo frotz frotz >frotz &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >7.out &&
+ test_cmp M.out 7.out &&
+ check_cache_at frotz dirty'
+
+test_expect_success \
+ '8 - conflicting addition.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo frotz frotz >frotz &&
+ git update-index --add frotz &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '9 - conflicting addition.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo frotz frotz >frotz &&
+ git update-index --add frotz &&
+ echo frotz >frotz &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '10 - path removed.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo rezrov >rezrov &&
+ git update-index --add rezrov &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >10.out &&
+ test_cmp M.out 10.out'
+
+test_expect_success \
+ '11 - dirty path removed.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo rezrov >rezrov &&
+ git update-index --add rezrov &&
+ echo rezrov rezrov >rezrov &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '12 - unmatching local changes being removed.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo rezrov rezrov >rezrov &&
+ git update-index --add rezrov &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '13 - unmatching local changes being removed.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo rezrov rezrov >rezrov &&
+ git update-index --add rezrov &&
+ echo rezrov >rezrov &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+cat >expected <<EOF
+-100644 X 0 nitfol
++100644 X 0 nitfol
+EOF
+
+test_expect_success \
+ '14 - unchanged in two heads.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo nitfol nitfol >nitfol &&
+ git update-index --add nitfol &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >14.out || return 1
+ 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 &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo nitfol nitfol >nitfol &&
+ git update-index --add nitfol &&
+ echo nitfol nitfol nitfol >nitfol &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >15.out || return 1
+ 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 &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo bozbar bozbar >bozbar &&
+ git update-index --add bozbar &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '17 - conflicting local change.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ echo bozbar bozbar >bozbar &&
+ git update-index --add bozbar &&
+ echo bozbar bozbar bozbar >bozbar &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '18 - local change already having a good result.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ cat bozbar-new >bozbar &&
+ git update-index --add bozbar &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >18.out &&
+ 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 &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ cat bozbar-new >bozbar &&
+ git update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >19.out &&
+ 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 &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ cat bozbar-old >bozbar &&
+ git update-index --add bozbar &&
+ read_tree_twoway $treeH $treeM &&
+ git ls-files --stage >20.out &&
+ test_cmp M.out 20.out &&
+ check_cache_at bozbar dirty'
+
+test_expect_success \
+ '21 - no local change, dirty cache.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ cat bozbar-old >bozbar &&
+ git update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+# This fails with straight two-way fast-forward.
+test_expect_success \
+ '22 - local change cache updated.' \
+ 'rm -f .git/index &&
+ git read-tree $treeH &&
+ git checkout-index -u -f -q -a &&
+ sed -e "s/such as/SUCH AS/" bozbar-old >bozbar &&
+ git update-index --add bozbar &&
+ if read_tree_twoway $treeH $treeM; then false; else :; fi'
+
+# Also make sure we did not break DF vs DF/DF case.
+test_expect_success \
+ 'DF vs DF/DF case setup.' \
+ 'rm -f .git/index &&
+ echo DF >DF &&
+ git update-index --add DF &&
+ treeDF=`git write-tree` &&
+ echo treeDF $treeDF &&
+ git ls-tree $treeDF &&
+
+ rm -f DF &&
+ mkdir DF &&
+ echo DF/DF >DF/DF &&
+ git update-index --add --remove DF DF/DF &&
+ treeDFDF=`git write-tree` &&
+ echo treeDFDF $treeDFDF &&
+ git ls-tree $treeDFDF &&
+ git ls-files --stage >DFDF.out'
+
+test_expect_success \
+ 'DF vs DF/DF case test.' \
+ 'rm -f .git/index &&
+ rm -fr DF &&
+ echo DF >DF &&
+ git update-index --add DF &&
+ read_tree_twoway $treeDF $treeDFDF &&
+ git ls-files --stage >DFDFcheck.out &&
+ 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.' \
+ '! git read-tree -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.' \
+ 'git read-tree -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 &&
+ echo >file-b &&
+ git add file-a file-b &&
+ git commit -a -m "test for correct modified tree"
+ git branch initial-mod &&
+ echo b >file-b &&
+ git commit -a -m "B" &&
+ echo a >file-a &&
+ git add file-a &&
+ git ls-tree $(git write-tree) file-a >expect &&
+ git read-tree -m HEAD initial-mod &&
+ git ls-tree $(git write-tree) file-a >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
new file mode 100755
index 0000000..0241329
--- /dev/null
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -0,0 +1,344 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Two way merge with read-tree -m -u $H $M
+
+This is identical to t1001, but uses -u to update the work tree as well.
+
+'
+. ./test-lib.sh
+
+compare_change () {
+ sed >current \
+ -e '1{/^diff --git /d;}' \
+ -e '2{/^index /d;}' \
+ -e '/^--- /d; /^+++ /d; /^@@ /d;' \
+ -e 's/^\(.[0-7][0-7][0-7][0-7][0-7][0-7]\) '"$_x40"' /\1 X /' "$1"
+ test_cmp expected current
+}
+
+check_cache_at () {
+ clean_if_empty=`git diff-files -- "$1"`
+ case "$clean_if_empty" in
+ '') echo "$1: clean" ;;
+ ?*) echo "$1: dirty" ;;
+ esac
+ case "$2,$clean_if_empty" in
+ clean,) : ;;
+ clean,?*) false ;;
+ dirty,) false ;;
+ dirty,?*) : ;;
+ esac
+}
+
+test_expect_success \
+ setup \
+ 'echo frotz >frotz &&
+ echo nitfol >nitfol &&
+ echo bozbar >bozbar &&
+ echo rezrov >rezrov &&
+ git update-index --add nitfol bozbar rezrov &&
+ treeH=`git write-tree` &&
+ echo treeH $treeH &&
+ git ls-tree $treeH &&
+
+ echo gnusto >bozbar &&
+ git update-index --add frotz bozbar --force-remove rezrov &&
+ git ls-files --stage >M.out &&
+ treeM=`git write-tree` &&
+ echo treeM $treeM &&
+ git ls-tree $treeM &&
+ sum bozbar frotz nitfol >M.sum &&
+ git diff-tree $treeH $treeM'
+
+test_expect_success \
+ '1, 2, 3 - no carry forward' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >1-3.out &&
+ cmp M.out 1-3.out &&
+ sum bozbar frotz nitfol >actual3.sum &&
+ cmp M.sum actual3.sum &&
+ check_cache_at bozbar clean &&
+ check_cache_at frotz clean &&
+ check_cache_at nitfol clean'
+
+test_expect_success \
+ '4 - carry forward local addition.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo "+100644 X 0 yomin" >expected &&
+ echo yomin >yomin &&
+ git update-index --add yomin &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >4.out || return 1
+ git diff -U0 --no-index M.out 4.out >4diff.out
+ compare_change 4diff.out expected &&
+ check_cache_at yomin clean &&
+ sum bozbar frotz nitfol >actual4.sum &&
+ cmp M.sum actual4.sum &&
+ echo yomin >yomin1 &&
+ diff yomin yomin1 &&
+ rm -f yomin1'
+
+test_expect_success \
+ '5 - carry forward local addition.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ git read-tree -m -u $treeH &&
+ echo yomin >yomin &&
+ git update-index --add yomin &&
+ echo yomin yomin >yomin &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >5.out || return 1
+ git diff -U0 --no-index M.out 5.out >5diff.out
+ compare_change 5diff.out expected &&
+ check_cache_at yomin dirty &&
+ sum bozbar frotz nitfol >actual5.sum &&
+ cmp M.sum actual5.sum &&
+ : dirty index should have prevented -u from checking it out. &&
+ echo yomin yomin >yomin1 &&
+ diff yomin yomin1 &&
+ rm -f yomin1'
+
+test_expect_success \
+ '6 - local addition already has the same.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo frotz >frotz &&
+ git update-index --add frotz &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >6.out &&
+ test_cmp M.out 6.out &&
+ check_cache_at frotz clean &&
+ sum bozbar frotz nitfol >actual3.sum &&
+ cmp M.sum actual3.sum &&
+ echo frotz >frotz1 &&
+ diff frotz frotz1 &&
+ rm -f frotz1'
+
+test_expect_success \
+ '7 - local addition already has the same.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo frotz >frotz &&
+ git update-index --add frotz &&
+ echo frotz frotz >frotz &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >7.out &&
+ test_cmp M.out 7.out &&
+ check_cache_at frotz dirty &&
+ sum bozbar frotz nitfol >actual7.sum &&
+ if cmp M.sum actual7.sum; then false; else :; fi &&
+ : dirty index should have prevented -u from checking it out. &&
+ echo frotz frotz >frotz1 &&
+ diff frotz frotz1 &&
+ rm -f frotz1'
+
+test_expect_success \
+ '8 - conflicting addition.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo frotz frotz >frotz &&
+ git update-index --add frotz &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '9 - conflicting addition.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo frotz frotz >frotz &&
+ git update-index --add frotz &&
+ echo frotz >frotz &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '10 - path removed.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo rezrov >rezrov &&
+ git update-index --add rezrov &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >10.out &&
+ cmp M.out 10.out &&
+ sum bozbar frotz nitfol >actual10.sum &&
+ cmp M.sum actual10.sum'
+
+test_expect_success \
+ '11 - dirty path removed.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo rezrov >rezrov &&
+ git update-index --add rezrov &&
+ echo rezrov rezrov >rezrov &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '12 - unmatching local changes being removed.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo rezrov rezrov >rezrov &&
+ git update-index --add rezrov &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '13 - unmatching local changes being removed.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo rezrov rezrov >rezrov &&
+ git update-index --add rezrov &&
+ echo rezrov >rezrov &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+cat >expected <<EOF
+-100644 X 0 nitfol
++100644 X 0 nitfol
+EOF
+
+test_expect_success \
+ '14 - unchanged in two heads.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo nitfol nitfol >nitfol &&
+ git update-index --add nitfol &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >14.out || return 1
+ git diff -U0 --no-index M.out 14.out >14diff.out
+ compare_change 14diff.out expected &&
+ sum bozbar frotz >actual14.sum &&
+ grep -v nitfol M.sum > expected14.sum &&
+ cmp expected14.sum actual14.sum &&
+ sum bozbar frotz nitfol >actual14a.sum &&
+ if cmp M.sum actual14a.sum; then false; else :; fi &&
+ check_cache_at nitfol clean &&
+ echo nitfol nitfol >nitfol1 &&
+ diff nitfol nitfol1 &&
+ rm -f nitfol1'
+
+test_expect_success \
+ '15 - unchanged in two heads.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo nitfol nitfol >nitfol &&
+ git update-index --add nitfol &&
+ echo nitfol nitfol nitfol >nitfol &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >15.out || return 1
+ git diff -U0 --no-index M.out 15.out >15diff.out
+ compare_change 15diff.out expected &&
+ check_cache_at nitfol dirty &&
+ sum bozbar frotz >actual15.sum &&
+ grep -v nitfol M.sum > expected15.sum &&
+ cmp expected15.sum actual15.sum &&
+ sum bozbar frotz nitfol >actual15a.sum &&
+ if cmp M.sum actual15a.sum; then false; else :; fi &&
+ echo nitfol nitfol nitfol >nitfol1 &&
+ diff nitfol nitfol1 &&
+ rm -f nitfol1'
+
+test_expect_success \
+ '16 - conflicting local change.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo bozbar bozbar >bozbar &&
+ git update-index --add bozbar &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '17 - conflicting local change.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo bozbar bozbar >bozbar &&
+ git update-index --add bozbar &&
+ echo bozbar bozbar bozbar >bozbar &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+test_expect_success \
+ '18 - local change already having a good result.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo gnusto >bozbar &&
+ git update-index --add bozbar &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >18.out &&
+ test_cmp M.out 18.out &&
+ check_cache_at bozbar clean &&
+ sum bozbar frotz nitfol >actual18.sum &&
+ cmp M.sum actual18.sum'
+
+test_expect_success \
+ '19 - local change already having a good result, further modified.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo gnusto >bozbar &&
+ git update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >19.out &&
+ test_cmp M.out 19.out &&
+ check_cache_at bozbar dirty &&
+ sum frotz nitfol >actual19.sum &&
+ grep -v bozbar M.sum > expected19.sum &&
+ cmp expected19.sum actual19.sum &&
+ sum bozbar frotz nitfol >actual19a.sum &&
+ if cmp M.sum actual19a.sum; then false; else :; fi &&
+ echo gnusto gnusto >bozbar1 &&
+ diff bozbar bozbar1 &&
+ rm -f bozbar1'
+
+test_expect_success \
+ '20 - no local change, use new tree.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo bozbar >bozbar &&
+ git update-index --add bozbar &&
+ git read-tree -m -u $treeH $treeM &&
+ git ls-files --stage >20.out &&
+ test_cmp M.out 20.out &&
+ check_cache_at bozbar clean &&
+ sum bozbar frotz nitfol >actual20.sum &&
+ cmp M.sum actual20.sum'
+
+test_expect_success \
+ '21 - no local change, dirty cache.' \
+ 'rm -f .git/index nitfol bozbar rezrov frotz &&
+ git read-tree --reset -u $treeH &&
+ echo bozbar >bozbar &&
+ git update-index --add bozbar &&
+ echo gnusto gnusto >bozbar &&
+ if git read-tree -m -u $treeH $treeM; then false; else :; fi'
+
+# Also make sure we did not break DF vs DF/DF case.
+test_expect_success \
+ 'DF vs DF/DF case setup.' \
+ 'rm -f .git/index
+ echo DF >DF &&
+ git update-index --add DF &&
+ treeDF=`git write-tree` &&
+ echo treeDF $treeDF &&
+ git ls-tree $treeDF &&
+
+ rm -f DF &&
+ mkdir DF &&
+ echo DF/DF >DF/DF &&
+ git update-index --add --remove DF DF/DF &&
+ treeDFDF=`git write-tree` &&
+ echo treeDFDF $treeDFDF &&
+ git ls-tree $treeDFDF &&
+ git ls-files --stage >DFDF.out'
+
+test_expect_success \
+ 'DF vs DF/DF case test.' \
+ 'rm -f .git/index &&
+ rm -fr DF &&
+ echo DF >DF &&
+ git update-index --add DF &&
+ git read-tree -m -u $treeDF $treeDFDF &&
+ git ls-files --stage >DFDFcheck.out &&
+ test_cmp DFDF.out DFDFcheck.out &&
+ check_cache_at DF/DF clean'
+
+test_done
diff --git a/t/t1003-read-tree-prefix.sh b/t/t1003-read-tree-prefix.sh
new file mode 100755
index 0000000..8c6d67e
--- /dev/null
+++ b/t/t1003-read-tree-prefix.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git read-tree --prefix test.
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo hello >one &&
+ git update-index --add one &&
+ tree=`git write-tree` &&
+ echo tree is $tree
+'
+
+echo 'one
+two/one' >expect
+
+test_expect_success 'read-tree --prefix' '
+ git read-tree --prefix=two/ $tree &&
+ git ls-files >actual &&
+ cmp expect actual
+'
+
+test_done
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
new file mode 100755
index 0000000..f19b4a2
--- /dev/null
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -0,0 +1,241 @@
+#!/bin/sh
+
+test_description='read-tree -m -u checks working tree files'
+
+. ./test-lib.sh
+
+# two-tree test
+
+test_expect_success 'two-way setup' '
+
+ mkdir subdir &&
+ echo >file1 file one &&
+ echo >file2 file two &&
+ echo >subdir/file1 file one in subdirectory &&
+ echo >subdir/file2 file two in subdirectory &&
+ git update-index --add file1 file2 subdir/file1 subdir/file2 &&
+ git commit -m initial &&
+
+ git branch side &&
+ git tag -f branch-point &&
+
+ echo file2 is not tracked on the master anymore &&
+ rm -f file2 subdir/file2 &&
+ git update-index --remove file2 subdir/file2 &&
+ git commit -a -m "master removes file2 and subdir/file2"
+'
+
+test_expect_success 'two-way not clobbering' '
+
+ echo >file2 master creates untracked file2 &&
+ echo >subdir/file2 master creates untracked subdir/file2 &&
+ if err=`git read-tree -m -u master side 2>&1`
+ then
+ echo should have complained
+ false
+ else
+ echo "happy to see $err"
+ fi
+'
+
+echo file2 >.gitignore
+
+test_expect_success 'two-way with incorrect --exclude-per-directory (1)' '
+
+ if err=`git read-tree -m --exclude-per-directory=.gitignore master side 2>&1`
+ then
+ echo should have complained
+ false
+ else
+ echo "happy to see $err"
+ fi
+'
+
+test_expect_success 'two-way with incorrect --exclude-per-directory (2)' '
+
+ if err=`git read-tree -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore master side 2>&1`
+ then
+ echo should have complained
+ false
+ else
+ echo "happy to see $err"
+ fi
+'
+
+test_expect_success 'two-way clobbering a ignored file' '
+
+ git read-tree -m -u --exclude-per-directory=.gitignore master side
+'
+
+rm -f .gitignore
+
+# three-tree test
+
+test_expect_success 'three-way not complaining on an untracked path in both' '
+
+ rm -f file2 subdir/file2 &&
+ git checkout side &&
+ echo >file3 file three &&
+ echo >subdir/file3 file three &&
+ git update-index --add file3 subdir/file3 &&
+ git commit -a -m "side adds file3 and removes file2" &&
+
+ git checkout master &&
+ echo >file2 file two is untracked on the master side &&
+ echo >subdir/file2 file two is untracked on the master side &&
+
+ git read-tree -m -u branch-point master side
+'
+
+test_expect_success 'three-way not clobbering a working tree file' '
+
+ git reset --hard &&
+ rm -f file2 subdir/file2 file3 subdir/file3 &&
+ git checkout master &&
+ echo >file3 file three created in master, untracked &&
+ echo >subdir/file3 file three created in master, untracked &&
+ if err=`git read-tree -m -u branch-point master side 2>&1`
+ then
+ echo should have complained
+ false
+ else
+ echo "happy to see $err"
+ fi
+'
+
+echo >.gitignore file3
+
+test_expect_success 'three-way not complaining on an untracked file' '
+
+ git reset --hard &&
+ rm -f file2 subdir/file2 file3 subdir/file3 &&
+ git checkout master &&
+ echo >file3 file three created in master, untracked &&
+ echo >subdir/file3 file three created in master, untracked &&
+
+ git read-tree -m -u --exclude-per-directory=.gitignore branch-point master side
+'
+
+test_expect_success '3-way not overwriting local changes (setup)' '
+
+ git reset --hard &&
+ git checkout -b side-a branch-point &&
+ echo >>file1 "new line to be kept in the merge result" &&
+ git commit -a -m "side-a changes file1" &&
+ git checkout -b side-b branch-point &&
+ echo >>file2 "new line to be kept in the merge result" &&
+ git commit -a -m "side-b changes file2" &&
+ git checkout side-a
+
+'
+
+test_expect_success '3-way not overwriting local changes (our side)' '
+
+ # At this point, file1 from side-a should be kept as side-b
+ # did not touch it.
+
+ git reset --hard &&
+
+ echo >>file1 "local changes" &&
+ git read-tree -m -u branch-point side-a side-b &&
+ grep "new line to be kept" file1 &&
+ grep "local changes" file1
+
+'
+
+test_expect_success '3-way not overwriting local changes (their side)' '
+
+ # At this point, file2 from side-b should be taken as side-a
+ # did not touch it.
+
+ git reset --hard &&
+
+ echo >>file2 "local changes" &&
+ test_must_fail git read-tree -m -u branch-point side-a side-b &&
+ ! grep "new line to be kept" file2 &&
+ grep "local changes" file2
+
+'
+
+test_expect_success SYMLINKS 'funny symlink in work tree' '
+
+ git reset --hard &&
+ git checkout -b sym-b side-b &&
+ mkdir -p a &&
+ >a/b &&
+ git add a/b &&
+ git commit -m "side adds a/b" &&
+
+ rm -fr a &&
+ git checkout -b sym-a side-a &&
+ mkdir -p a &&
+ ln -s ../b a/b &&
+ git add a/b &&
+ git commit -m "we add a/b" &&
+
+ git read-tree -m -u sym-a sym-a sym-b
+
+'
+
+test_expect_success SYMLINKS 'funny symlink in work tree, un-unlink-able' '
+
+ rm -fr a b &&
+ git reset --hard &&
+
+ git checkout sym-a &&
+ chmod a-w a &&
+ test_must_fail git read-tree -m -u sym-a sym-a sym-b
+
+'
+
+# clean-up from the above test
+chmod a+w a 2>/dev/null
+rm -fr a b
+
+test_expect_success 'D/F setup' '
+
+ git reset --hard &&
+
+ git checkout side-a &&
+ rm -f subdir/file2 &&
+ mkdir subdir/file2 &&
+ echo qfwfq >subdir/file2/another &&
+ git add subdir/file2/another &&
+ test_tick &&
+ git commit -m "side-a changes file2 to directory"
+
+'
+
+test_expect_success 'D/F' '
+
+ git checkout side-b &&
+ git read-tree -m -u branch-point side-b side-a &&
+ git ls-files -u >actual &&
+ (
+ a=$(git rev-parse branch-point:subdir/file2)
+ b=$(git rev-parse side-a:subdir/file2/another)
+ echo "100644 $a 1 subdir/file2"
+ echo "100644 $a 2 subdir/file2"
+ echo "100644 $b 3 subdir/file2/another"
+ ) >expect &&
+ test_cmp actual expect
+
+'
+
+test_expect_success 'D/F resolve' '
+
+ git reset --hard &&
+ git checkout side-b &&
+ git merge-resolve branch-point -- side-b side-a
+
+'
+
+test_expect_success 'D/F recursive' '
+
+ git reset --hard &&
+ git checkout side-b &&
+ git merge-recursive branch-point -- side-b side-a
+
+'
+
+test_done
diff --git a/t/t1005-read-tree-reset.sh b/t/t1005-read-tree-reset.sh
new file mode 100755
index 0000000..8499116
--- /dev/null
+++ b/t/t1005-read-tree-reset.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='read-tree -u --reset'
+
+. ./test-lib.sh
+
+# two-tree test
+
+test_expect_success 'setup' '
+ git init &&
+ mkdir df &&
+ echo content >df/file &&
+ git add df/file &&
+ git commit -m one &&
+ git ls-files >expect &&
+ rm -rf df &&
+ echo content >df &&
+ git add df &&
+ echo content >new &&
+ git add new &&
+ git commit -m two
+'
+
+test_expect_success 'reset should work' '
+ git read-tree -u --reset HEAD^ &&
+ git ls-files >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reset should remove remnants from a failed merge' '
+ git read-tree --reset -u HEAD &&
+ git ls-files -s >expect &&
+ sha1=$(git rev-parse :new) &&
+ (
+ echo "100644 $sha1 1 old"
+ echo "100644 $sha1 3 old"
+ ) | git update-index --index-info &&
+ >old &&
+ git ls-files -s &&
+ git read-tree --reset -u HEAD &&
+ git ls-files -s >actual &&
+ ! test -f old
+'
+
+test_expect_success 'Porcelain reset should remove remnants too' '
+ git read-tree --reset -u HEAD &&
+ git ls-files -s >expect &&
+ sha1=$(git rev-parse :new) &&
+ (
+ echo "100644 $sha1 1 old"
+ echo "100644 $sha1 3 old"
+ ) | git update-index --index-info &&
+ >old &&
+ git ls-files -s &&
+ git reset --hard &&
+ git ls-files -s >actual &&
+ ! test -f old
+'
+
+test_expect_success 'Porcelain checkout -f should remove remnants too' '
+ git read-tree --reset -u HEAD &&
+ git ls-files -s >expect &&
+ sha1=$(git rev-parse :new) &&
+ (
+ echo "100644 $sha1 1 old"
+ echo "100644 $sha1 3 old"
+ ) | git update-index --index-info &&
+ >old &&
+ git ls-files -s &&
+ git checkout -f &&
+ git ls-files -s >actual &&
+ ! test -f old
+'
+
+test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
+ git read-tree --reset -u HEAD &&
+ git ls-files -s >expect &&
+ sha1=$(git rev-parse :new) &&
+ (
+ echo "100644 $sha1 1 old"
+ echo "100644 $sha1 3 old"
+ ) | git update-index --index-info &&
+ >old &&
+ git ls-files -s &&
+ git checkout -f HEAD &&
+ git ls-files -s >actual &&
+ ! test -f old
+'
+
+test_done
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
new file mode 100755
index 0000000..d8b7f2f
--- /dev/null
+++ b/t/t1006-cat-file.sh
@@ -0,0 +1,244 @@
+#!/bin/sh
+
+test_description='git cat-file'
+
+. ./test-lib.sh
+
+echo_without_newline () {
+ printf '%s' "$*"
+}
+
+strlen () {
+ echo_without_newline "$1" | wc -c | sed -e 's/^ *//'
+}
+
+maybe_remove_timestamp () {
+ if test -z "$2"; then
+ echo_without_newline "$1"
+ else
+ echo_without_newline "$(printf '%s\n' "$1" | sed -e 's/ [0-9][0-9]* [-+][0-9][0-9][0-9][0-9]$//')"
+ fi
+}
+
+run_tests () {
+ type=$1
+ sha1=$2
+ size=$3
+ content=$4
+ pretty_content=$5
+ no_ts=$6
+
+ batch_output="$sha1 $type $size
+$content"
+
+ test_expect_success "$type exists" '
+ git cat-file -e $sha1
+ '
+
+ test_expect_success "Type of $type is correct" '
+ test $type = "$(git cat-file -t $sha1)"
+ '
+
+ test_expect_success "Size of $type is correct" '
+ test $size = "$(git cat-file -s $sha1)"
+ '
+
+ test -z "$content" ||
+ test_expect_success "Content of $type is correct" '
+ expect="$(maybe_remove_timestamp "$content" $no_ts)"
+ actual="$(maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts)"
+
+ if test "z$expect" = "z$actual"
+ then
+ : happy
+ else
+ echo "Oops: expected $expect"
+ echo "but got $actual"
+ false
+ fi
+ '
+
+ test_expect_success "Pretty content of $type is correct" '
+ expect="$(maybe_remove_timestamp "$pretty_content" $no_ts)"
+ actual="$(maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts)"
+ if test "z$expect" = "z$actual"
+ then
+ : happy
+ else
+ echo "Oops: expected $expect"
+ echo "but got $actual"
+ false
+ fi
+ '
+
+ test -z "$content" ||
+ test_expect_success "--batch output of $type is correct" '
+ expect="$(maybe_remove_timestamp "$batch_output" $no_ts)"
+ actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts)"
+ if test "z$expect" = "z$actual"
+ then
+ : happy
+ else
+ echo "Oops: expected $expect"
+ echo "but got $actual"
+ false
+ fi
+ '
+
+ test_expect_success "--batch-check output of $type is correct" '
+ expect="$sha1 $type $size"
+ actual="$(echo_without_newline $sha1 | git cat-file --batch-check)"
+ if test "z$expect" = "z$actual"
+ then
+ : happy
+ else
+ echo "Oops: expected $expect"
+ echo "but got $actual"
+ false
+ fi
+ '
+}
+
+hello_content="Hello World"
+hello_size=$(strlen "$hello_content")
+hello_sha1=$(echo_without_newline "$hello_content" | git hash-object --stdin)
+
+test_expect_success "setup" '
+ echo_without_newline "$hello_content" > hello &&
+ git update-index --add hello
+'
+
+run_tests 'blob' $hello_sha1 $hello_size "$hello_content" "$hello_content"
+
+tree_sha1=$(git write-tree)
+tree_size=33
+tree_pretty_content="100644 blob $hello_sha1 hello"
+
+run_tests 'tree' $tree_sha1 $tree_size "" "$tree_pretty_content"
+
+commit_message="Intial commit"
+commit_sha1=$(echo_without_newline "$commit_message" | git commit-tree $tree_sha1)
+commit_size=176
+commit_content="tree $tree_sha1
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 0000000000 +0000
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 0000000000 +0000
+
+$commit_message"
+
+run_tests 'commit' $commit_sha1 $commit_size "$commit_content" "$commit_content" 1
+
+tag_header_without_timestamp="object $hello_sha1
+type blob
+tag hellotag
+tagger $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+tag_description="This is a tag"
+tag_content="$tag_header_without_timestamp 0000000000 +0000
+
+$tag_description"
+tag_pretty_content="$tag_header_without_timestamp Thu Jan 1 00:00:00 1970 +0000
+
+$tag_description"
+
+tag_sha1=$(echo_without_newline "$tag_content" | git mktag)
+tag_size=$(strlen "$tag_content")
+
+run_tests 'tag' $tag_sha1 $tag_size "$tag_content" "$tag_pretty_content" 1
+
+test_expect_success \
+ "Reach a blob from a tag pointing to it" \
+ "test '$hello_content' = \"\$(git cat-file blob $tag_sha1)\""
+
+for batch in batch batch-check
+do
+ for opt in t s e p
+ do
+ test_expect_success "Passing -$opt with --$batch fails" '
+ test_must_fail git cat-file --$batch -$opt $hello_sha1
+ '
+
+ test_expect_success "Passing --$batch with -$opt fails" '
+ test_must_fail git cat-file -$opt --$batch $hello_sha1
+ '
+ done
+
+ test_expect_success "Passing <type> with --$batch fails" '
+ test_must_fail git cat-file --$batch blob $hello_sha1
+ '
+
+ test_expect_success "Passing --$batch with <type> fails" '
+ test_must_fail git cat-file blob --$batch $hello_sha1
+ '
+
+ test_expect_success "Passing sha1 with --$batch fails" '
+ test_must_fail git cat-file --$batch $hello_sha1
+ '
+done
+
+test_expect_success "--batch-check for a non-existent named object" '
+ test "foobar42 missing
+foobar84 missing" = \
+ "$( ( echo foobar42; echo_without_newline foobar84; ) | git cat-file --batch-check)"
+'
+
+test_expect_success "--batch-check for a non-existent hash" '
+ test "0000000000000000000000000000000000000042 missing
+0000000000000000000000000000000000000084 missing" = \
+ "$( ( echo 0000000000000000000000000000000000000042;
+ echo_without_newline 0000000000000000000000000000000000000084; ) \
+ | git cat-file --batch-check)"
+'
+
+test_expect_success "--batch for an existent and a non-existent hash" '
+ test "$tag_sha1 tag $tag_size
+$tag_content
+0000000000000000000000000000000000000000 missing" = \
+ "$( ( echo $tag_sha1;
+ echo_without_newline 0000000000000000000000000000000000000000; ) \
+ | git cat-file --batch)"
+'
+
+test_expect_success "--batch-check for an emtpy line" '
+ test " missing" = "$(echo | git cat-file --batch-check)"
+'
+
+batch_input="$hello_sha1
+$commit_sha1
+$tag_sha1
+deadbeef
+
+"
+
+batch_output="$hello_sha1 blob $hello_size
+$hello_content
+$commit_sha1 commit $commit_size
+$commit_content
+$tag_sha1 tag $tag_size
+$tag_content
+deadbeef missing
+ missing"
+
+test_expect_success '--batch with multiple sha1s gives correct format' '
+ test "$(maybe_remove_timestamp "$batch_output" 1)" = "$(maybe_remove_timestamp "$(echo_without_newline "$batch_input" | git cat-file --batch)" 1)"
+'
+
+batch_check_input="$hello_sha1
+$tree_sha1
+$commit_sha1
+$tag_sha1
+deadbeef
+
+"
+
+batch_check_output="$hello_sha1 blob $hello_size
+$tree_sha1 tree $tree_size
+$commit_sha1 commit $commit_size
+$tag_sha1 tag $tag_size
+deadbeef missing
+ missing"
+
+test_expect_success "--batch-check with multiple sha1s gives correct format" '
+ test "$batch_check_output" = \
+ "$(echo_without_newline "$batch_check_input" | git cat-file --batch-check)"
+'
+
+test_done
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
new file mode 100755
index 0000000..dd32432
--- /dev/null
+++ b/t/t1007-hash-object.sh
@@ -0,0 +1,191 @@
+#!/bin/sh
+
+test_description="git hash-object"
+
+. ./test-lib.sh
+
+echo_without_newline() {
+ printf '%s' "$*"
+}
+
+test_blob_does_not_exist() {
+ test_expect_success 'blob does not exist in database' "
+ test_must_fail git cat-file blob $1
+ "
+}
+
+test_blob_exists() {
+ test_expect_success 'blob exists in database' "
+ git cat-file blob $1
+ "
+}
+
+hello_content="Hello World"
+hello_sha1=5e1c309dae7f45e0f39b1bf3ac3cd9db12e7d689
+
+example_content="This is an example"
+example_sha1=ddd3f836d3e3fbb7ae289aa9ae83536f76956399
+
+setup_repo() {
+ echo_without_newline "$hello_content" > hello
+ echo_without_newline "$example_content" > example
+}
+
+test_repo=test
+push_repo() {
+ test_create_repo $test_repo
+ cd $test_repo
+
+ setup_repo
+}
+
+pop_repo() {
+ cd ..
+ rm -rf $test_repo
+}
+
+setup_repo
+
+# Argument checking
+
+test_expect_success "multiple '--stdin's are rejected" '
+ echo example | test_must_fail git hash-object --stdin --stdin
+'
+
+test_expect_success "Can't use --stdin and --stdin-paths together" '
+ echo example | test_must_fail git hash-object --stdin --stdin-paths &&
+ echo example | test_must_fail git hash-object --stdin-paths --stdin
+'
+
+test_expect_success "Can't pass filenames as arguments with --stdin-paths" '
+ echo example | test_must_fail git hash-object --stdin-paths hello
+'
+
+test_expect_success "Can't use --path with --stdin-paths" '
+ echo example | test_must_fail git hash-object --stdin-paths --path=foo
+'
+
+test_expect_success "Can't use --path with --no-filters" '
+ test_must_fail git hash-object --no-filters --path=foo
+'
+
+# Behavior
+
+push_repo
+
+test_expect_success 'hash a file' '
+ test $hello_sha1 = $(git hash-object hello)
+'
+
+test_blob_does_not_exist $hello_sha1
+
+test_expect_success 'hash from stdin' '
+ test $example_sha1 = $(git hash-object --stdin < example)
+'
+
+test_blob_does_not_exist $example_sha1
+
+test_expect_success 'hash a file and write to database' '
+ test $hello_sha1 = $(git hash-object -w hello)
+'
+
+test_blob_exists $hello_sha1
+
+test_expect_success 'git hash-object --stdin file1 <file0 first operates on file0, then file1' '
+ echo foo > file1 &&
+ obname0=$(echo bar | git hash-object --stdin) &&
+ obname1=$(git hash-object file1) &&
+ obname0new=$(echo bar | git hash-object --stdin file1 | sed -n -e 1p) &&
+ obname1new=$(echo bar | git hash-object --stdin file1 | sed -n -e 2p) &&
+ test "$obname0" = "$obname0new" &&
+ test "$obname1" = "$obname1new"
+'
+
+test_expect_success 'check that appropriate filter is invoke when --path is used' '
+ 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" &&
+ path1_sha=$(git hash-object --path=file1 file0) &&
+ path0_sha=$(git hash-object --path=file0 file1) &&
+ test "$file0_sha" = "$path0_sha" &&
+ test "$file1_sha" = "$path1_sha" &&
+ 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_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_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
+'
+
+pop_repo
+
+for args in "-w --stdin" "--stdin -w"; do
+ push_repo
+
+ test_expect_success "hash from stdin and write to database ($args)" '
+ test $example_sha1 = $(git hash-object $args < example)
+ '
+
+ test_blob_exists $example_sha1
+
+ pop_repo
+done
+
+filenames="hello
+example"
+
+sha1s="$hello_sha1
+$example_sha1"
+
+test_expect_success "hash two files with names on stdin" '
+ test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object --stdin-paths)"
+'
+
+for args in "-w --stdin-paths" "--stdin-paths -w"; do
+ push_repo
+
+ test_expect_success "hash two files with names on stdin and write to database ($args)" '
+ test "$sha1s" = "$(echo_without_newline "$filenames" | git hash-object $args)"
+ '
+
+ test_blob_exists $hello_sha1
+ test_blob_exists $example_sha1
+
+ pop_repo
+done
+
+test_done
diff --git a/t/t1008-read-tree-overlay.sh b/t/t1008-read-tree-overlay.sh
new file mode 100755
index 0000000..f9e0028
--- /dev/null
+++ b/t/t1008-read-tree-overlay.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='test multi-tree read-tree without merging'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo one >a &&
+ git add a &&
+ git commit -m initial &&
+ git tag initial &&
+ echo two >b &&
+ git add b &&
+ git commit -m second &&
+ git checkout -b side initial &&
+ echo three >a &&
+ mkdir b &&
+ echo four >b/c &&
+ git add b/c &&
+ git commit -m third
+'
+
+test_expect_success 'multi-read' '
+ git read-tree initial master side &&
+ (echo a; echo b/c) >expect &&
+ git ls-files >actual &&
+ test_cmp expect actual
+'
+
+test_done
+
diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh
new file mode 100755
index 0000000..59b3aa4
--- /dev/null
+++ b/t/t1009-read-tree-new-index.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='test read-tree into a fresh index file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo one >a &&
+ git add a &&
+ git commit -m initial
+'
+
+test_expect_success 'non-existent index file' '
+ rm -f new-index &&
+ GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_expect_success 'empty index file' '
+ rm -f new-index &&
+ > new-index &&
+ GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_done
+
diff --git a/t/t1010-mktree.sh b/t/t1010-mktree.sh
new file mode 100755
index 0000000..b946f87
--- /dev/null
+++ b/t/t1010-mktree.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git mktree'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ for d in a a. a0
+ do
+ mkdir "$d" && echo "$d/one" >"$d/one" &&
+ git add "$d"
+ done &&
+ echo zero >one &&
+ git update-index --add --info-only one &&
+ git write-tree --missing-ok >tree.missing &&
+ git ls-tree $(cat tree.missing) >top.missing &&
+ git ls-tree -r $(cat tree.missing) >all.missing &&
+ echo one >one &&
+ git add one &&
+ git write-tree >tree &&
+ git ls-tree $(cat tree) >top &&
+ git ls-tree -r $(cat tree) >all &&
+ test_tick &&
+ git commit -q -m one &&
+ H=$(git rev-parse HEAD) &&
+ git update-index --add --cacheinfo 160000 $H sub &&
+ test_tick &&
+ git commit -q -m two &&
+ git rev-parse HEAD^{tree} >tree.withsub &&
+ git ls-tree HEAD >top.withsub &&
+ git ls-tree -r HEAD >all.withsub
+'
+
+test_expect_success 'ls-tree piped to mktree (1)' '
+ git mktree <top >actual &&
+ test_cmp tree actual
+'
+
+test_expect_success 'ls-tree piped to mktree (2)' '
+ git mktree <top.withsub >actual &&
+ test_cmp tree.withsub actual
+'
+
+test_expect_success 'ls-tree output in wrong order given to mktree (1)' '
+ perl -e "print reverse <>" <top |
+ git mktree >actual &&
+ test_cmp tree actual
+'
+
+test_expect_success 'ls-tree output in wrong order given to mktree (2)' '
+ perl -e "print reverse <>" <top.withsub |
+ git mktree >actual &&
+ test_cmp tree.withsub actual
+'
+
+test_expect_success 'allow missing object with --missing' '
+ git mktree --missing <top.missing >actual &&
+ test_cmp tree.missing actual
+'
+
+test_expect_success 'mktree refuses to read ls-tree -r output (1)' '
+ test_must_fail git mktree <all >actual
+'
+
+test_expect_success 'mktree refuses to read ls-tree -r output (2)' '
+ test_must_fail git mktree <all.withsub >actual
+'
+
+test_done
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
new file mode 100755
index 0000000..62246db
--- /dev/null
+++ b/t/t1011-read-tree-sparse-checkout.sh
@@ -0,0 +1,150 @@
+#!/bin/sh
+
+test_description='sparse checkout tests'
+
+. ./test-lib.sh
+
+cat >expected <<EOF
+100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0 init.t
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 sub/added
+EOF
+test_expect_success 'setup' '
+ test_commit init &&
+ echo modified >> init.t &&
+ mkdir sub &&
+ touch sub/added &&
+ git add init.t sub/added &&
+ git commit -m "modified and added" &&
+ git tag top &&
+ git rm sub/added &&
+ git commit -m removed &&
+ git tag removed &&
+ git checkout top &&
+ git ls-files --stage > result &&
+ test_cmp expected result
+'
+
+cat >expected.swt <<EOF
+H init.t
+H sub/added
+EOF
+test_expect_success 'read-tree without .git/info/sparse-checkout' '
+ git read-tree -m -u HEAD &&
+ git ls-files --stage > result &&
+ test_cmp expected result &&
+ git ls-files -t > result &&
+ test_cmp expected.swt result
+'
+
+test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
+ echo > .git/info/sparse-checkout
+ git read-tree -m -u HEAD &&
+ git ls-files -t > result &&
+ test_cmp expected.swt result &&
+ test -f init.t &&
+ test -f sub/added
+'
+
+test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '
+ git config core.sparsecheckout true &&
+ echo > .git/info/sparse-checkout &&
+ git read-tree --no-sparse-checkout -m -u HEAD &&
+ git ls-files -t > result &&
+ test_cmp expected.swt result &&
+ test -f init.t &&
+ test -f sub/added
+'
+
+test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
+ git config core.sparsecheckout true &&
+ echo > .git/info/sparse-checkout &&
+ test_must_fail git read-tree -m -u HEAD &&
+ git ls-files --stage > result &&
+ test_cmp expected result &&
+ git ls-files -t > result &&
+ test_cmp expected.swt result &&
+ test -f init.t &&
+ test -f sub/added
+'
+
+cat >expected.swt <<EOF
+S init.t
+H sub/added
+EOF
+test_expect_success 'match directories with trailing slash' '
+ echo sub/ > .git/info/sparse-checkout &&
+ git read-tree -m -u HEAD &&
+ git ls-files -t > result &&
+ test_cmp expected.swt result &&
+ test ! -f init.t &&
+ test -f sub/added
+'
+
+cat >expected.swt <<EOF
+H init.t
+H sub/added
+EOF
+test_expect_failure 'match directories without trailing slash' '
+ echo init.t > .git/info/sparse-checkout &&
+ echo sub >> .git/info/sparse-checkout &&
+ git read-tree -m -u HEAD &&
+ git ls-files -t > result &&
+ test_cmp expected.swt result &&
+ test ! -f init.t &&
+ test -f sub/added
+'
+
+cat >expected.swt <<EOF
+H init.t
+S sub/added
+EOF
+test_expect_success 'checkout area changes' '
+ echo init.t > .git/info/sparse-checkout &&
+ git read-tree -m -u HEAD &&
+ git ls-files -t > result &&
+ test_cmp expected.swt result &&
+ test -f init.t &&
+ test ! -f sub/added
+'
+
+test_expect_success 'read-tree updates worktree, absent case' '
+ echo sub/added > .git/info/sparse-checkout &&
+ git checkout -f top &&
+ git read-tree -m -u HEAD^ &&
+ test ! -f init.t
+'
+
+test_expect_success 'read-tree updates worktree, dirty case' '
+ echo sub/added > .git/info/sparse-checkout &&
+ git checkout -f top &&
+ echo dirty > init.t &&
+ git read-tree -m -u HEAD^ &&
+ grep -q dirty init.t &&
+ rm init.t
+'
+
+test_expect_success 'read-tree removes worktree, dirty case' '
+ echo init.t > .git/info/sparse-checkout &&
+ git checkout -f top &&
+ echo dirty > added &&
+ git read-tree -m -u HEAD^ &&
+ grep -q dirty added
+'
+
+test_expect_success 'read-tree adds to worktree, absent case' '
+ echo init.t > .git/info/sparse-checkout &&
+ git checkout -f removed &&
+ git read-tree -u -m HEAD^ &&
+ test ! -f sub/added
+'
+
+test_expect_success 'read-tree adds to worktree, dirty case' '
+ echo init.t > .git/info/sparse-checkout &&
+ git checkout -f removed &&
+ mkdir sub &&
+ echo dirty > sub/added &&
+ git read-tree -u -m HEAD^ &&
+ grep -q dirty sub/added
+'
+
+test_done
diff --git a/t/t1012-read-tree-df.sh b/t/t1012-read-tree-df.sh
new file mode 100755
index 0000000..9811d46
--- /dev/null
+++ b/t/t1012-read-tree-df.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='read-tree D/F conflict corner cases'
+
+. ./test-lib.sh
+
+maketree () {
+ (
+ rm -f .git/index .git/index.lock &&
+ git clean -d -f -f -q -x &&
+ name="$1" &&
+ shift &&
+ for it
+ do
+ path=$(expr "$it" : '\([^:]*\)') &&
+ mkdir -p $(dirname "$path") &&
+ echo "$it" >"$path" &&
+ git update-index --add "$path" || exit
+ done &&
+ git tag "$name" $(git write-tree)
+ )
+}
+
+settree () {
+ rm -f .git/index .git/index.lock &&
+ git clean -d -f -f -q -x &&
+ git read-tree "$1" &&
+ git checkout-index -f -q -u -a &&
+ git update-index --refresh
+}
+
+checkindex () {
+ git ls-files -s |
+ sed "s|^[0-7][0-7]* $_x40 \([0-3]\) |\1 |" >current &&
+ cat >expect &&
+ test_cmp expect current
+}
+
+test_expect_success setup '
+ maketree O-000 a/b-2/c/d a/b/c/d a/x &&
+ maketree A-000 a/b-2/c/d a/b/c/d a/x &&
+ maketree A-001 a/b-2/c/d a/b/c/d a/b/c/e a/x &&
+ maketree B-000 a/b-2/c/d a/b a/x &&
+
+ maketree O-010 t-0 t/1 t/2 t=3 &&
+ maketree A-010 t-0 t t=3 &&
+ maketree B-010 t/1: t=3: &&
+
+ maketree O-020 ds/dma/ioat.c ds/dma/ioat_dca.c &&
+ maketree A-020 ds/dma/ioat/Makefile ds/dma/ioat/registers.h &&
+ :
+'
+
+test_expect_success '3-way (1)' '
+ settree A-000 &&
+ git read-tree -m -u O-000 A-000 B-000 &&
+ checkindex <<-EOF
+ 3 a/b
+ 0 a/b-2/c/d
+ 1 a/b/c/d
+ 2 a/b/c/d
+ 0 a/x
+ EOF
+'
+
+test_expect_success '3-way (2)' '
+ settree A-001 &&
+ git read-tree -m -u O-000 A-001 B-000 &&
+ checkindex <<-EOF
+ 3 a/b
+ 0 a/b-2/c/d
+ 1 a/b/c/d
+ 2 a/b/c/d
+ 2 a/b/c/e
+ 0 a/x
+ EOF
+'
+
+test_expect_success '3-way (3)' '
+ settree A-010 &&
+ git read-tree -m -u O-010 A-010 B-010 &&
+ checkindex <<-EOF
+ 2 t
+ 1 t-0
+ 2 t-0
+ 1 t/1
+ 3 t/1
+ 1 t/2
+ 0 t=3
+ EOF
+'
+
+test_expect_success '2-way (1)' '
+ settree O-020 &&
+ git read-tree -m -u O-020 A-020 &&
+ checkindex <<-EOF
+ 0 ds/dma/ioat/Makefile
+ 0 ds/dma/ioat/registers.h
+ EOF
+'
+
+test_done
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
new file mode 100755
index 0000000..5687499
--- /dev/null
+++ b/t/t1020-subdirectory.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='Try various core-level commands in subdirectory.
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ long="a b c d e f g h i j k l m n o p q r s t u v w x y z" &&
+ for c in $long; do echo $c; done >one &&
+ mkdir dir &&
+ for c in x y z $long a b c; do echo $c; done >dir/two &&
+ cp one original.one &&
+ cp dir/two original.two
+'
+HERE=`pwd`
+LF='
+'
+
+test_expect_success 'update-index and ls-files' '
+ cd "$HERE" &&
+ git update-index --add one &&
+ case "`git ls-files`" in
+ one) echo pass one ;;
+ *) echo bad one; exit 1 ;;
+ esac &&
+ cd dir &&
+ git update-index --add two &&
+ case "`git ls-files`" in
+ two) echo pass two ;;
+ *) echo bad two; exit 1 ;;
+ esac &&
+ cd .. &&
+ case "`git ls-files`" in
+ dir/two"$LF"one) echo pass both ;;
+ *) echo bad; exit 1 ;;
+ esac
+'
+
+test_expect_success 'cat-file' '
+ cd "$HERE" &&
+ two=`git ls-files -s dir/two` &&
+ two=`expr "$two" : "[0-7]* \\([0-9a-f]*\\)"` &&
+ echo "$two" &&
+ git cat-file -p "$two" >actual &&
+ cmp dir/two actual &&
+ cd dir &&
+ git cat-file -p "$two" >actual &&
+ cmp two actual
+'
+rm -f actual dir/actual
+
+test_expect_success 'diff-files' '
+ cd "$HERE" &&
+ echo a >>one &&
+ echo d >>dir/two &&
+ case "`git diff-files --name-only`" in
+ dir/two"$LF"one) echo pass top ;;
+ *) echo bad top; exit 1 ;;
+ esac &&
+ # diff should not omit leading paths
+ cd dir &&
+ case "`git diff-files --name-only`" in
+ dir/two"$LF"one) echo pass subdir ;;
+ *) echo bad subdir; exit 1 ;;
+ esac &&
+ case "`git diff-files --name-only .`" in
+ dir/two) echo pass subdir limited ;;
+ *) echo bad subdir limited; exit 1 ;;
+ esac
+'
+
+test_expect_success 'write-tree' '
+ cd "$HERE" &&
+ top=`git write-tree` &&
+ echo $top &&
+ cd dir &&
+ sub=`git write-tree` &&
+ echo $sub &&
+ test "z$top" = "z$sub"
+'
+
+test_expect_success 'checkout-index' '
+ cd "$HERE" &&
+ git checkout-index -f -u one &&
+ cmp one original.one &&
+ cd dir &&
+ git checkout-index -f -u two &&
+ cmp two ../original.two
+'
+
+test_expect_success 'read-tree' '
+ cd "$HERE" &&
+ rm -f one dir/two &&
+ tree=`git write-tree` &&
+ git read-tree --reset -u "$tree" &&
+ cmp one original.one &&
+ cmp dir/two original.two &&
+ cd dir &&
+ rm -f two &&
+ git read-tree --reset -u "$tree" &&
+ cmp two ../original.two &&
+ cmp ../one ../original.one
+'
+
+test_expect_success 'no file/rev ambiguity check inside .git' '
+ cd "$HERE" &&
+ git commit -a -m 1 &&
+ cd "$HERE"/.git &&
+ git show -s HEAD
+'
+
+test_expect_success 'no file/rev ambiguity check inside a bare repo' '
+ cd "$HERE" &&
+ git clone -s --bare .git foo.git &&
+ cd foo.git && GIT_DIR=. git show -s HEAD
+'
+
+# This still does not work as it should...
+: test_expect_success 'no file/rev ambiguity check inside a bare repo' '
+ cd "$HERE" &&
+ git clone -s --bare .git foo.git &&
+ cd foo.git && git show -s HEAD
+'
+
+test_expect_success SYMLINKS 'detection should not be fooled by a symlink' '
+ cd "$HERE" &&
+ rm -fr foo.git &&
+ git clone -s .git another &&
+ ln -s another yetanother &&
+ cd yetanother/.git &&
+ git show -s HEAD
+'
+
+test_done
diff --git a/t/t1100-commit-tree-options.sh b/t/t1100-commit-tree-options.sh
new file mode 100755
index 0000000..c4414ff
--- /dev/null
+++ b/t/t1100-commit-tree-options.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# Copyright (C) 2005 Rene Scharfe
+#
+
+test_description='git commit-tree options test
+
+This test checks that git commit-tree can create a specific commit
+object by defining all environment variables that it understands.
+'
+
+. ./test-lib.sh
+
+cat >expected <<EOF
+tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
+author Author Name <author@email> 1117148400 +0000
+committer Committer Name <committer@email> 1117150200 +0000
+
+comment text
+EOF
+
+test_expect_success \
+ 'test preparation: write empty tree' \
+ 'git write-tree >treeid'
+
+test_expect_success \
+ 'construct commit' \
+ 'echo comment text |
+ GIT_AUTHOR_NAME="Author Name" \
+ GIT_AUTHOR_EMAIL="author@email" \
+ GIT_AUTHOR_DATE="2005-05-26 23:00" \
+ GIT_COMMITTER_NAME="Committer Name" \
+ GIT_COMMITTER_EMAIL="committer@email" \
+ GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ TZ=GMT git commit-tree `cat treeid` >commitid 2>/dev/null'
+
+test_expect_success \
+ 'read commit' \
+ 'git cat-file commit `cat commitid` >commit'
+
+test_expect_success \
+ 'compare commit' \
+ 'test_cmp expected commit'
+
+test_done
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
new file mode 100755
index 0000000..ab55eda
--- /dev/null
+++ b/t/t1200-tutorial.sh
@@ -0,0 +1,265 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='A simple turial in the form of a test case'
+
+. ./test-lib.sh
+
+test_expect_success 'blob' '
+ echo "Hello World" > hello &&
+ echo "Silly example" > example &&
+
+ git update-index --add hello example &&
+
+ test blob = "$(git cat-file -t 557db03)"
+'
+
+test_expect_success 'blob 557db03' '
+ test "Hello World" = "$(git cat-file blob 557db03)"
+'
+
+echo "It's a new day for git" >>hello
+cat > diff.expect << EOF
+diff --git a/hello b/hello
+index 557db03..263414f 100644
+--- a/hello
++++ b/hello
+@@ -1 +1,2 @@
+ Hello World
++It's a new day for git
+EOF
+
+test_expect_success 'git diff-files -p' '
+ git diff-files -p > diff.output &&
+ test_cmp diff.expect diff.output
+'
+
+test_expect_success 'git diff' '
+ git diff > diff.output &&
+ test_cmp diff.expect diff.output
+'
+
+test_expect_success 'tree' '
+ tree=$(git write-tree 2>/dev/null)
+ test 8988da15d077d4829fc51d8544c097def6644dbb = $tree
+'
+
+test_expect_success 'git diff-index -p HEAD' '
+ test_tick &&
+ tree=$(git write-tree) &&
+ commit=$(echo "Initial commit" | git commit-tree $tree) &&
+ git update-ref HEAD $commit &&
+ git diff-index -p HEAD > diff.output &&
+ test_cmp diff.expect diff.output
+'
+
+test_expect_success 'git diff HEAD' '
+ git diff HEAD > diff.output &&
+ test_cmp diff.expect diff.output
+'
+
+cat > whatchanged.expect << EOF
+commit VARIABLE
+Author: VARIABLE
+Date: VARIABLE
+
+ Initial commit
+
+diff --git a/example b/example
+new file mode 100644
+index 0000000..f24c74a
+--- /dev/null
++++ b/example
+@@ -0,0 +1 @@
++Silly example
+diff --git a/hello b/hello
+new file mode 100644
+index 0000000..557db03
+--- /dev/null
++++ b/hello
+@@ -0,0 +1 @@
++Hello World
+EOF
+
+test_expect_success 'git whatchanged -p --root' '
+ git whatchanged -p --root |
+ sed -e "1s/^\(.\{7\}\).\{40\}/\1VARIABLE/" \
+ -e "2,3s/^\(.\{8\}\).*$/\1VARIABLE/" \
+ > whatchanged.output &&
+ test_cmp whatchanged.expect whatchanged.output
+'
+
+test_expect_success 'git tag my-first-tag' '
+ git tag my-first-tag &&
+ test_cmp .git/refs/heads/master .git/refs/tags/my-first-tag
+'
+
+test_expect_success 'git checkout -b mybranch' '
+ git checkout -b mybranch &&
+ test_cmp .git/refs/heads/master .git/refs/heads/mybranch
+'
+
+cat > branch.expect <<EOF
+ master
+* mybranch
+EOF
+
+test_expect_success 'git branch' '
+ git branch > branch.output &&
+ test_cmp branch.expect branch.output
+'
+
+test_expect_success 'git resolve now fails' '
+ git checkout mybranch &&
+ echo "Work, work, work" >>hello &&
+ test_tick &&
+ git commit -m "Some work." -i hello &&
+
+ git checkout master &&
+
+ echo "Play, play, play" >>hello &&
+ echo "Lots of fun" >>example &&
+ test_tick &&
+ git commit -m "Some fun." -i hello example &&
+
+ test_must_fail git merge -m "Merge work in mybranch" mybranch
+'
+
+cat > hello << EOF
+Hello World
+It's a new day for git
+Play, play, play
+Work, work, work
+EOF
+
+cat > show-branch.expect << EOF
+* [master] Merge work in mybranch
+ ! [mybranch] Some work.
+--
+- [master] Merge work in mybranch
+*+ [mybranch] Some work.
+* [master^] Some fun.
+EOF
+
+test_expect_success 'git show-branch' '
+ test_tick &&
+ git commit -m "Merge work in mybranch" -i hello &&
+ git show-branch --topo-order --more=1 master mybranch \
+ > show-branch.output &&
+ test_cmp show-branch.expect show-branch.output
+'
+
+cat > resolve.expect << EOF
+Updating VARIABLE..VARIABLE
+FASTFORWARD (no commit created; -m option ignored)
+ example | 1 +
+ hello | 1 +
+ 2 files changed, 2 insertions(+), 0 deletions(-)
+EOF
+
+test_expect_success 'git resolve' '
+ git checkout mybranch &&
+ git merge -m "Merge upstream changes." master |
+ sed -e "1s/[0-9a-f]\{7\}/VARIABLE/g" \
+ -e "s/^Fast[- ]forward /FASTFORWARD /" >resolve.output &&
+ test_cmp resolve.expect resolve.output
+'
+
+cat > show-branch2.expect << EOF
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
+--
+-- [master] Merge work in mybranch
+EOF
+
+test_expect_success 'git show-branch (part 2)' '
+ git show-branch --topo-order master mybranch > show-branch2.output &&
+ test_cmp show-branch2.expect show-branch2.output
+'
+
+cat > show-branch3.expect << EOF
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
+--
+-- [master] Merge work in mybranch
++* [master^2] Some work.
++* [master^] Some fun.
+EOF
+
+test_expect_success 'git show-branch (part 3)' '
+ git show-branch --topo-order --more=2 master mybranch \
+ > show-branch3.output &&
+ test_cmp show-branch3.expect show-branch3.output
+'
+
+test_expect_success 'rewind to "Some fun." and "Some work."' '
+ git checkout mybranch &&
+ git reset --hard master^2 &&
+ git checkout master &&
+ git reset --hard master^
+'
+
+cat > show-branch4.expect << EOF
+* [master] Some fun.
+ ! [mybranch] Some work.
+--
+* [master] Some fun.
+ + [mybranch] Some work.
+*+ [master^] Initial commit
+EOF
+
+test_expect_success 'git show-branch (part 4)' '
+ git show-branch --topo-order > show-branch4.output &&
+ test_cmp show-branch4.expect show-branch4.output
+'
+
+test_expect_success 'manual merge' '
+ mb=$(git merge-base HEAD mybranch) &&
+ git name-rev --name-only --tags $mb > name-rev.output &&
+ test "my-first-tag" = $(cat name-rev.output) &&
+
+ git read-tree -m -u $mb HEAD mybranch
+'
+
+cat > ls-files.expect << EOF
+100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1 hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2 hello
+100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
+EOF
+
+test_expect_success 'git ls-files --stage' '
+ git ls-files --stage > ls-files.output &&
+ test_cmp ls-files.expect ls-files.output
+'
+
+cat > ls-files-unmerged.expect << EOF
+100644 557db03de997c86a4a028e1ebd3a1ceb225be238 1 hello
+100644 ba42a2a96e3027f3333e13ede4ccf4498c3ae942 2 hello
+100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
+EOF
+
+test_expect_success 'git ls-files --unmerged' '
+ git ls-files --unmerged > ls-files-unmerged.output &&
+ test_cmp ls-files-unmerged.expect ls-files-unmerged.output
+'
+
+test_expect_success 'git-merge-index' '
+ test_must_fail git merge-index git-merge-one-file hello
+'
+
+test_expect_success 'git ls-files --stage (part 2)' '
+ git ls-files --stage > ls-files.output2 &&
+ test_cmp ls-files.expect ls-files.output2
+'
+
+test_expect_success 'git repack' 'git repack'
+test_expect_success 'git prune-packed' 'git prune-packed'
+test_expect_success '-> only packed objects' '
+ git prune && # Remove conflict marked blobs
+ test $(find .git/objects/[0-9a-f][0-9a-f] -type f -print 2>/dev/null | wc -l) = 0
+'
+
+test_done
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
new file mode 100755
index 0000000..074f2f2
--- /dev/null
+++ b/t/t1300-repo-config.sh
@@ -0,0 +1,857 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test git config in different settings'
+
+. ./test-lib.sh
+
+test -f .git/config && rm .git/config
+
+git config core.penguin "little blue"
+
+cat > expect << EOF
+[core]
+ penguin = little blue
+EOF
+
+test_expect_success 'initial' 'cmp .git/config expect'
+
+git config Core.Movie BadPhysics
+
+cat > expect << EOF
+[core]
+ penguin = little blue
+ Movie = BadPhysics
+EOF
+
+test_expect_success 'mixed case' 'cmp .git/config expect'
+
+git config Cores.WhatEver Second
+
+cat > expect << EOF
+[core]
+ penguin = little blue
+ Movie = BadPhysics
+[Cores]
+ WhatEver = Second
+EOF
+
+test_expect_success 'similar section' 'cmp .git/config expect'
+
+git config CORE.UPPERCASE true
+
+cat > expect << EOF
+[core]
+ penguin = little blue
+ Movie = BadPhysics
+ UPPERCASE = true
+[Cores]
+ WhatEver = Second
+EOF
+
+test_expect_success 'similar section' 'cmp .git/config expect'
+
+test_expect_success 'replace with non-match' \
+ 'git config core.penguin kingpin !blue'
+
+test_expect_success 'replace with non-match (actually matching)' \
+ 'git config core.penguin "very blue" !kingpin'
+
+cat > expect << EOF
+[core]
+ penguin = very blue
+ Movie = BadPhysics
+ UPPERCASE = true
+ penguin = kingpin
+[Cores]
+ WhatEver = Second
+EOF
+
+test_expect_success 'non-match result' 'cmp .git/config expect'
+
+cat > .git/config <<\EOF
+[alpha]
+bar = foo
+[beta]
+baz = multiple \
+lines
+EOF
+
+test_expect_success 'unset with cont. lines' \
+ 'git config --unset beta.baz'
+
+cat > expect <<\EOF
+[alpha]
+bar = foo
+[beta]
+EOF
+
+test_expect_success 'unset with cont. lines is correct' 'cmp .git/config expect'
+
+cat > .git/config << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha ="beta" # last silly comment
+haha = hello
+ haha = bello
+[nextSection] noNewline = ouch
+EOF
+
+cp .git/config .git/config2
+
+test_expect_success 'multiple unset' \
+ 'git config --unset-all beta.haha'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'multiple unset is correct' 'cmp .git/config expect'
+
+cp .git/config2 .git/config
+
+test_expect_success '--replace-all missing value' '
+ test_must_fail git config --replace-all beta.haha &&
+ test_cmp .git/config2 .git/config
+'
+
+rm .git/config2
+
+test_expect_success '--replace-all' \
+ 'git config --replace-all beta.haha gamma'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha = gamma
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'all replaced' 'cmp .git/config expect'
+
+git config beta.haha alpha
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha = alpha
+[nextSection] noNewline = ouch
+EOF
+
+test_expect_success 'really mean test' 'cmp .git/config expect'
+
+git config nextsection.nonewline wow
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+ haha = alpha
+[nextSection]
+ nonewline = wow
+EOF
+
+test_expect_success 'really really mean test' 'cmp .git/config expect'
+
+test_expect_success 'get value' 'test alpha = $(git config beta.haha)'
+git config --unset beta.haha
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ nonewline = wow
+EOF
+
+test_expect_success 'unset' 'cmp .git/config expect'
+
+git config nextsection.NoNewLine "wow2 for me" "for me$"
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ nonewline = wow
+ NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar' 'cmp .git/config expect'
+
+test_expect_success 'non-match' \
+ 'git config --get nextsection.nonewline !for'
+
+test_expect_success 'non-match value' \
+ 'test wow = $(git config --get nextsection.nonewline !for)'
+
+test_expect_success 'ambiguous get' '
+ test_must_fail git config --get nextsection.nonewline
+'
+
+test_expect_success 'get multivar' \
+ 'git config --get-all nextsection.nonewline'
+
+git config nextsection.nonewline "wow3" "wow$"
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ nonewline = wow3
+ NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar replace' 'cmp .git/config expect'
+
+test_expect_success 'ambiguous value' '
+ test_must_fail git config nextsection.nonewline
+'
+
+test_expect_success 'ambiguous unset' '
+ test_must_fail git config --unset nextsection.nonewline
+'
+
+test_expect_success 'invalid unset' '
+ test_must_fail git config --unset somesection.nonewline
+'
+
+git config --unset nextsection.nonewline "wow3$"
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ NoNewLine = wow2 for me
+EOF
+
+test_expect_success 'multivar unset' 'cmp .git/config expect'
+
+test_expect_success 'invalid key' 'test_must_fail git config inval.2key blabla'
+
+test_expect_success 'correct key' 'git config 123456.a123 987'
+
+test_expect_success 'hierarchical section' \
+ 'git config Version.1.2.3eX.Alpha beta'
+
+cat > expect << EOF
+[beta] ; silly comment # another comment
+noIndent= sillyValue ; 'nother silly comment
+
+# empty line
+ ; comment
+[nextSection]
+ NoNewLine = wow2 for me
+[123456]
+ a123 = 987
+[Version "1.2.3eX"]
+ Alpha = beta
+EOF
+
+test_expect_success 'hierarchical section value' 'cmp .git/config expect'
+
+cat > expect << EOF
+beta.noindent=sillyValue
+nextsection.nonewline=wow2 for me
+123456.a123=987
+version.1.2.3eX.alpha=beta
+EOF
+
+test_expect_success 'working --list' \
+ 'git config --list > output && cmp output expect'
+
+cat > expect << EOF
+beta.noindent sillyValue
+nextsection.nonewline wow2 for me
+EOF
+
+test_expect_success '--get-regexp' \
+ 'git config --get-regexp in > output && cmp output expect'
+
+git config --add nextsection.nonewline "wow4 for you"
+
+cat > expect << EOF
+wow2 for me
+wow4 for you
+EOF
+
+test_expect_success '--add' \
+ 'git config --get-all nextsection.nonewline > output && cmp output expect'
+
+cat > .git/config << EOF
+[novalue]
+ variable
+[emptyvalue]
+ variable =
+EOF
+
+test_expect_success 'get variable with no value' \
+ 'git config --get novalue.variable ^$'
+
+test_expect_success 'get variable with empty value' \
+ 'git config --get emptyvalue.variable ^$'
+
+echo novalue.variable > expect
+
+test_expect_success 'get-regexp variable with no value' \
+ 'git config --get-regexp novalue > output &&
+ cmp output expect'
+
+echo 'emptyvalue.variable ' > expect
+
+test_expect_success 'get-regexp variable with empty value' \
+ 'git config --get-regexp emptyvalue > output &&
+ cmp output expect'
+
+echo true > expect
+
+test_expect_success 'get bool variable with no value' \
+ 'git config --bool novalue.variable > output &&
+ cmp output expect'
+
+echo false > expect
+
+test_expect_success 'get bool variable with empty value' \
+ 'git config --bool emptyvalue.variable > output &&
+ cmp output expect'
+
+test_expect_success 'no arguments, but no crash' '
+ test_must_fail git config >output 2>&1 &&
+ grep usage output
+'
+
+cat > .git/config << EOF
+[a.b]
+ c = d
+EOF
+
+git config a.x y
+
+cat > expect << EOF
+[a.b]
+ c = d
+[a]
+ x = y
+EOF
+
+test_expect_success 'new section is partial match of another' 'cmp .git/config expect'
+
+git config b.x y
+git config a.b c
+
+cat > expect << EOF
+[a.b]
+ c = d
+[a]
+ x = y
+ b = c
+[b]
+ x = y
+EOF
+
+test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
+
+test_expect_success 'alternative GIT_CONFIG (non-existing file should fail)' \
+ 'test_must_fail git config --file non-existing-config -l'
+
+cat > other-config << EOF
+[ein]
+ bahn = strasse
+EOF
+
+cat > expect << EOF
+ein.bahn=strasse
+EOF
+
+GIT_CONFIG=other-config git config -l > output
+
+test_expect_success 'alternative GIT_CONFIG' 'cmp output expect'
+
+test_expect_success 'alternative GIT_CONFIG (--file)' \
+ 'git config --file other-config -l > output && cmp output expect'
+
+test_expect_success 'refer config from subdirectory' '
+ mkdir x &&
+ (
+ cd x &&
+ echo strasse >expect
+ git config --get --file ../other-config ein.bahn >actual &&
+ test_cmp expect actual
+ )
+
+'
+
+GIT_CONFIG=other-config git config anwohner.park ausweis
+
+cat > expect << EOF
+[ein]
+ bahn = strasse
+[anwohner]
+ park = ausweis
+EOF
+
+test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect'
+
+cat > .git/config << EOF
+# Hallo
+ #Bello
+[branch "eins"]
+ x = 1
+[branch.eins]
+ y = 1
+ [branch "1 234 blabl/a"]
+weird
+EOF
+
+test_expect_success "rename section" \
+ "git config --rename-section branch.eins branch.zwei"
+
+cat > expect << EOF
+# Hallo
+ #Bello
+[branch "zwei"]
+ x = 1
+[branch "zwei"]
+ y = 1
+ [branch "1 234 blabl/a"]
+weird
+EOF
+
+test_expect_success "rename succeeded" "test_cmp expect .git/config"
+
+test_expect_success "rename non-existing section" '
+ test_must_fail git config --rename-section \
+ branch."world domination" branch.drei
+'
+
+test_expect_success "rename succeeded" "test_cmp expect .git/config"
+
+test_expect_success "rename another section" \
+ 'git config --rename-section branch."1 234 blabl/a" branch.drei'
+
+cat > expect << EOF
+# Hallo
+ #Bello
+[branch "zwei"]
+ x = 1
+[branch "zwei"]
+ y = 1
+[branch "drei"]
+weird
+EOF
+
+test_expect_success "rename succeeded" "test_cmp expect .git/config"
+
+cat >> .git/config << EOF
+[branch "vier"] z = 1
+EOF
+
+test_expect_success "rename a section with a var on the same line" \
+ 'git config --rename-section branch.vier branch.zwei'
+
+cat > expect << EOF
+# Hallo
+ #Bello
+[branch "zwei"]
+ x = 1
+[branch "zwei"]
+ y = 1
+[branch "drei"]
+weird
+[branch "zwei"]
+ z = 1
+EOF
+
+test_expect_success "rename succeeded" "test_cmp expect .git/config"
+
+cat >> .git/config << EOF
+ [branch "zwei"] a = 1 [branch "vier"]
+EOF
+
+test_expect_success "remove section" "git config --remove-section branch.zwei"
+
+cat > expect << EOF
+# Hallo
+ #Bello
+[branch "drei"]
+weird
+EOF
+
+test_expect_success "section was removed properly" \
+ "test_cmp expect .git/config"
+
+rm .git/config
+
+cat > expect << EOF
+[gitcvs]
+ enabled = true
+ dbname = %Ggitcvs2.%a.%m.sqlite
+[gitcvs "ext"]
+ dbname = %Ggitcvs1.%a.%m.sqlite
+EOF
+
+test_expect_success 'section ending' '
+
+ git config gitcvs.enabled true &&
+ git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+ git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+ cmp .git/config expect
+
+'
+
+test_expect_success numbers '
+
+ git config kilo.gram 1k &&
+ git config mega.ton 1m &&
+ k=$(git config --int --get kilo.gram) &&
+ test z1024 = "z$k" &&
+ m=$(git config --int --get mega.ton) &&
+ test z1048576 = "z$m"
+'
+
+cat > expect <<EOF
+fatal: bad config value for 'aninvalid.unit' in .git/config
+EOF
+
+test_expect_success 'invalid unit' '
+
+ git config aninvalid.unit "1auto" &&
+ s=$(git config aninvalid.unit) &&
+ test "z1auto" = "z$s" &&
+ if git config --int --get aninvalid.unit 2>actual
+ then
+ echo config should have failed
+ false
+ fi &&
+ cmp actual expect
+'
+
+cat > expect << EOF
+true
+false
+true
+false
+true
+false
+true
+false
+EOF
+
+test_expect_success bool '
+
+ git config bool.true1 01 &&
+ git config bool.true2 -1 &&
+ git config bool.true3 YeS &&
+ git config bool.true4 true &&
+ git config bool.false1 000 &&
+ git config bool.false2 "" &&
+ git config bool.false3 nO &&
+ git config bool.false4 FALSE &&
+ rm -f result &&
+ for i in 1 2 3 4
+ do
+ git config --bool --get bool.true$i >>result
+ git config --bool --get bool.false$i >>result
+ done &&
+ cmp expect result'
+
+test_expect_success 'invalid bool (--get)' '
+
+ git config bool.nobool foobar &&
+ test_must_fail git config --bool --get bool.nobool'
+
+test_expect_success 'invalid bool (set)' '
+
+ test_must_fail git config --bool bool.nobool foobar'
+
+rm .git/config
+
+cat > expect <<\EOF
+[bool]
+ true1 = true
+ true2 = true
+ true3 = true
+ true4 = true
+ false1 = false
+ false2 = false
+ false3 = false
+ false4 = false
+EOF
+
+test_expect_success 'set --bool' '
+
+ git config --bool bool.true1 01 &&
+ git config --bool bool.true2 -1 &&
+ git config --bool bool.true3 YeS &&
+ git config --bool bool.true4 true &&
+ git config --bool bool.false1 000 &&
+ git config --bool bool.false2 "" &&
+ git config --bool bool.false3 nO &&
+ git config --bool bool.false4 FALSE &&
+ cmp expect .git/config'
+
+rm .git/config
+
+cat > expect <<\EOF
+[int]
+ val1 = 1
+ val2 = -1
+ val3 = 5242880
+EOF
+
+test_expect_success 'set --int' '
+
+ git config --int int.val1 01 &&
+ git config --int int.val2 -1 &&
+ git config --int int.val3 5m &&
+ cmp expect .git/config'
+
+rm .git/config
+
+cat >expect <<\EOF
+[bool]
+ true1 = true
+ true2 = true
+ false1 = false
+ false2 = false
+[int]
+ int1 = 0
+ int2 = 1
+ int3 = -1
+EOF
+
+test_expect_success 'get --bool-or-int' '
+ (
+ echo "[bool]"
+ echo true1
+ echo true2 = true
+ echo false = false
+ echo "[int]"
+ echo int1 = 0
+ echo int2 = 1
+ echo int3 = -1
+ ) >>.git/config &&
+ test $(git config --bool-or-int bool.true1) = true &&
+ test $(git config --bool-or-int bool.true2) = true &&
+ test $(git config --bool-or-int bool.false) = false &&
+ test $(git config --bool-or-int int.int1) = 0 &&
+ test $(git config --bool-or-int int.int2) = 1 &&
+ test $(git config --bool-or-int int.int3) = -1
+
+'
+
+rm .git/config
+cat >expect <<\EOF
+[bool]
+ true1 = true
+ false1 = false
+ true2 = true
+ false2 = false
+[int]
+ int1 = 0
+ int2 = 1
+ int3 = -1
+EOF
+
+test_expect_success 'set --bool-or-int' '
+ git config --bool-or-int bool.true1 true &&
+ git config --bool-or-int bool.false1 false &&
+ git config --bool-or-int bool.true2 yes &&
+ git config --bool-or-int bool.false2 no &&
+ git config --bool-or-int int.int1 0 &&
+ git config --bool-or-int int.int2 1 &&
+ git config --bool-or-int int.int3 -1 &&
+ test_cmp expect .git/config
+'
+
+rm .git/config
+
+cat >expect <<\EOF
+[path]
+ home = ~/
+ normal = /dev/null
+ trailingtilde = foo~
+EOF
+
+test_expect_success 'set --path' '
+ git config --path path.home "~/" &&
+ git config --path path.normal "/dev/null" &&
+ git config --path path.trailingtilde "foo~" &&
+ test_cmp expect .git/config'
+
+if test "${HOME+set}"
+then
+ test_set_prereq HOMEVAR
+fi
+
+cat >expect <<EOF
+$HOME/
+/dev/null
+foo~
+EOF
+
+test_expect_success HOMEVAR 'get --path' '
+ git config --get --path path.home > result &&
+ git config --get --path path.normal >> result &&
+ git config --get --path path.trailingtilde >> result &&
+ test_cmp expect result
+'
+
+cat >expect <<\EOF
+/dev/null
+foo~
+EOF
+
+test_expect_success 'get --path copes with unset $HOME' '
+ (
+ unset HOME;
+ test_must_fail git config --get --path path.home \
+ >result 2>msg &&
+ git config --get --path path.normal >>result &&
+ git config --get --path path.trailingtilde >>result
+ ) &&
+ grep "[Ff]ailed to expand.*~/" msg &&
+ test_cmp expect result
+'
+
+rm .git/config
+
+git config quote.leading " test"
+git config quote.ending "test "
+git config quote.semicolon "test;test"
+git config quote.hash "test#test"
+
+cat > expect << EOF
+[quote]
+ leading = " test"
+ ending = "test "
+ semicolon = "test;test"
+ hash = "test#test"
+EOF
+
+test_expect_success 'quoting' 'cmp .git/config expect'
+
+test_expect_success 'key with newline' '
+ test_must_fail git config "key.with
+newline" 123'
+
+test_expect_success 'value with newline' 'git config key.sub value.with\\\
+newline'
+
+cat > .git/config <<\EOF
+[section]
+ ; comment \
+ continued = cont\
+inued
+ noncont = not continued ; \
+ quotecont = "cont;\
+inued"
+EOF
+
+cat > expect <<\EOF
+section.continued=continued
+section.noncont=not continued
+section.quotecont=cont;inued
+EOF
+
+git config --list > result
+
+test_expect_success 'value continued on next line' 'cmp result expect'
+
+cat > .git/config <<\EOF
+[section "sub=section"]
+ val1 = foo=bar
+ val2 = foo\nbar
+ val3 = \n\n
+ val4 =
+ val5
+EOF
+
+cat > expect <<\EOF
+section.sub=section.val1
+foo=barQsection.sub=section.val2
+foo
+barQsection.sub=section.val3
+
+
+Qsection.sub=section.val4
+Qsection.sub=section.val5Q
+EOF
+
+git config --null --list | perl -pe 'y/\000/Q/' > result
+echo >>result
+
+test_expect_success '--null --list' 'cmp result expect'
+
+git config --null --get-regexp 'val[0-9]' | perl -pe 'y/\000/Q/' > result
+echo >>result
+
+test_expect_success '--null --get-regexp' 'cmp result expect'
+
+test_expect_success 'inner whitespace kept verbatim' '
+ git config section.val "foo bar" &&
+ test "z$(git config section.val)" = "zfoo bar"
+'
+
+test_expect_success SYMLINKS 'symlinked configuration' '
+
+ ln -s notyet myconfig &&
+ GIT_CONFIG=myconfig git config test.frotz nitfol &&
+ test -h myconfig &&
+ test -f notyet &&
+ test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol &&
+ GIT_CONFIG=myconfig git config test.xyzzy rezrov &&
+ test -h myconfig &&
+ test -f notyet &&
+ test "z$(GIT_CONFIG=notyet git config test.frotz)" = znitfol &&
+ test "z$(GIT_CONFIG=notyet git config test.xyzzy)" = zrezrov
+
+'
+
+test_expect_success 'check split_cmdline return' "
+ git config alias.split-cmdline-fix 'echo \"' &&
+ test_must_fail git split-cmdline-fix &&
+ echo foo > foo &&
+ git add foo &&
+ git commit -m 'initial commit' &&
+ git config branch.master.mergeoptions 'echo \"' &&
+ test_must_fail git merge master
+ "
+
+test_expect_success 'git -c "key=value" support' '
+ test "z$(git -c name=value config name)" = zvalue &&
+ test "z$(git -c core.name=value config core.name)" = zvalue &&
+ test "z$(git -c CamelCase=value config camelcase)" = zvalue &&
+ test "z$(git -c flag config --bool flag)" = ztrue &&
+ test_must_fail git -c core.name=value config name
+'
+
+test_done
diff --git a/t/t1301-shared-repo.sh b/t/t1301-shared-repo.sh
new file mode 100755
index 0000000..de42d21
--- /dev/null
+++ b/t/t1301-shared-repo.sh
@@ -0,0 +1,170 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Schindelin
+#
+
+test_description='Test shared repository initialization'
+
+. ./test-lib.sh
+
+# Remove a default ACL from the test dir if possible.
+setfacl -k . 2>/dev/null
+
+# User must have read permissions to the repo -> failure on --shared=0400
+test_expect_success 'shared = 0400 (faulty permission u-w)' '
+ mkdir sub && (
+ cd sub && git init --shared=0400
+ )
+ ret="$?"
+ rm -rf sub
+ test $ret != "0"
+'
+
+modebits () {
+ ls -l "$1" | sed -e 's|^\(..........\).*|\1|'
+}
+
+for u in 002 022
+do
+ test_expect_success POSIXPERM "shared=1 does not clear bits preset by umask $u" '
+ mkdir sub && (
+ cd sub &&
+ umask $u &&
+ git init --shared=1 &&
+ test 1 = "$(git config core.sharedrepository)"
+ ) &&
+ actual=$(ls -l sub/.git/HEAD)
+ case "$actual" in
+ -rw-rw-r--*)
+ : happy
+ ;;
+ *)
+ echo Oops, .git/HEAD is not 0664 but $actual
+ false
+ ;;
+ esac
+ '
+ rm -rf sub
+done
+
+test_expect_success 'shared=all' '
+ mkdir sub &&
+ cd sub &&
+ git init --shared=all &&
+ test 2 = $(git config core.sharedrepository)
+'
+
+test_expect_success POSIXPERM 'update-server-info honors core.sharedRepository' '
+ : > a1 &&
+ git add a1 &&
+ test_tick &&
+ git commit -m a1 &&
+ umask 0277 &&
+ git update-server-info &&
+ actual="$(ls -l .git/info/refs)" &&
+ case "$actual" in
+ -r--r--r--*)
+ : happy
+ ;;
+ *)
+ echo Oops, .git/info/refs is not 0444
+ false
+ ;;
+ esac
+'
+
+for u in 0660:rw-rw---- \
+ 0640:rw-r----- \
+ 0600:rw------- \
+ 0666:rw-rw-rw- \
+ 0664:rw-rw-r--
+do
+ x=$(expr "$u" : ".*:\([rw-]*\)") &&
+ y=$(echo "$x" | sed -e "s/w/-/g") &&
+ u=$(expr "$u" : "\([0-7]*\)") &&
+ git config core.sharedrepository "$u" &&
+ umask 0277 &&
+
+ test_expect_success POSIXPERM "shared = $u ($y) ro" '
+
+ rm -f .git/info/refs &&
+ git update-server-info &&
+ actual="$(modebits .git/info/refs)" &&
+ test "x$actual" = "x-$y" || {
+ ls -lt .git/info
+ false
+ }
+ '
+
+ umask 077 &&
+ test_expect_success POSIXPERM "shared = $u ($x) rw" '
+
+ rm -f .git/info/refs &&
+ git update-server-info &&
+ actual="$(modebits .git/info/refs)" &&
+ test "x$actual" = "x-$x" || {
+ ls -lt .git/info
+ false
+ }
+
+ '
+
+done
+
+test_expect_success POSIXPERM 'git reflog expire honors core.sharedRepository' '
+ git config core.sharedRepository group &&
+ git reflog expire --all &&
+ actual="$(ls -l .git/logs/refs/heads/master)" &&
+ case "$actual" in
+ -rw-rw-*)
+ : happy
+ ;;
+ *)
+ echo Ooops, .git/logs/refs/heads/master is not 0662 [$actual]
+ false
+ ;;
+ esac
+'
+
+test_expect_success POSIXPERM 'forced modes' '
+ mkdir -p templates/hooks &&
+ echo update-server-info >templates/hooks/post-update &&
+ chmod +x templates/hooks/post-update &&
+ echo : >random-file &&
+ mkdir new &&
+ (
+ cd new &&
+ umask 002 &&
+ git init --shared=0660 --template=../templates &&
+ >frotz &&
+ git add frotz &&
+ git commit -a -m initial &&
+ git repack
+ ) &&
+ # List repository files meant to be protected; note that
+ # COMMIT_EDITMSG does not matter---0mode is not about a
+ # repository with a work tree.
+ find new/.git -type f -name COMMIT_EDITMSG -prune -o -print |
+ xargs ls -ld >actual &&
+
+ # Everything must be unaccessible to others
+ test -z "$(sed -e "/^.......---/d" actual)" &&
+
+ # All directories must have either 2770 or 770
+ test -z "$(sed -n -e "/^drwxrw[sx]---/d" -e "/^d/p" actual)" &&
+
+ # post-update hook must be 0770
+ test -z "$(sed -n -e "/post-update/{
+ /^-rwxrwx---/d
+ p
+ }" actual)" &&
+
+ # All files inside objects must be accessible by us
+ test -z "$(sed -n -e "/objects\//{
+ /^d/d
+ /^-r.-r.----/d
+ p
+ }" actual)"
+'
+
+test_done
diff --git a/t/t1302-repo-version.sh b/t/t1302-repo-version.sh
new file mode 100755
index 0000000..8d305b4
--- /dev/null
+++ b/t/t1302-repo-version.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Nguyá»…n Thái Ngá»c Duy
+#
+
+test_description='Test repository version check'
+
+. ./test-lib.sh
+
+cat >test.patch <<EOF
+diff --git a/test.txt b/test.txt
+new file mode 100644
+--- /dev/null
++++ b/test.txt
+@@ -0,0 +1 @@
++123
+EOF
+
+test_create_repo "test"
+test_create_repo "test2"
+
+GIT_CONFIG=test2/.git/config git config core.repositoryformatversion 99 || exit 1
+
+test_expect_success 'gitdir selection on normal repos' '
+ (test "$(git config core.repositoryformatversion)" = 0 &&
+ cd test &&
+ test "$(git config core.repositoryformatversion)" = 0)'
+
+# Make sure it would stop at test2, not trash
+test_expect_success 'gitdir selection on unsupported repo' '
+ (cd test2 &&
+ test "$(git config core.repositoryformatversion)" = 99)'
+
+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)'
+
+test_expect_success 'gitdir required mode on normal repos' '
+ (git apply --check --index test.patch &&
+ cd test && git apply --check --index ../test.patch)'
+
+test_expect_success 'gitdir required mode on unsupported repo' '
+ (cd test2 && test_must_fail git apply --check --index ../test.patch)
+'
+
+test_done
diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh
new file mode 100755
index 0000000..080117c
--- /dev/null
+++ b/t/t1303-wacky-config.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='Test wacky input to git config'
+. ./test-lib.sh
+
+setup() {
+ (printf "[section]\n" &&
+ printf " key = foo") >.git/config
+}
+
+check() {
+ echo "$2" >expected
+ git config --get "$1" >actual 2>&1
+ test_cmp actual expected
+}
+
+test_expect_success 'modify same key' '
+ setup &&
+ git config section.key bar &&
+ check section.key bar
+'
+
+test_expect_success 'add key in same section' '
+ setup &&
+ git config section.other bar &&
+ check section.key foo &&
+ check section.other bar
+'
+
+test_expect_success 'add key in different section' '
+ setup &&
+ git config section2.key bar &&
+ check section.key foo &&
+ check section2.key bar
+'
+
+SECTION="test.q\"s\\sq'sp e.key"
+test_expect_success 'make sure git config escapes section names properly' '
+ git config "$SECTION" bar &&
+ check "$SECTION" bar
+'
+
+LONG_VALUE=$(printf "x%01021dx a" 7)
+test_expect_success 'do not crash on special long config line' '
+ setup &&
+ git config section.key "$LONG_VALUE" &&
+ check section.key "fatal: bad config file line 2 in .git/config"
+'
+
+test_done
diff --git a/t/t1304-default-acl.sh b/t/t1304-default-acl.sh
new file mode 100755
index 0000000..97ab02a
--- /dev/null
+++ b/t/t1304-default-acl.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Matthieu Moy
+#
+
+test_description='Test repository with default ACL'
+
+# Create the test repo with restrictive umask
+# => this must come before . ./test-lib.sh
+umask 077
+
+. ./test-lib.sh
+
+# We need an arbitrary other user give permission to using ACLs. root
+# is a good candidate: exists on all unices, and it has permission
+# anyway, so we don't create a security hole running the testsuite.
+
+setfacl_out="$(setfacl -m u:root:rwx . 2>&1)"
+setfacl_ret=$?
+
+if [ $setfacl_ret != 0 ]; then
+ skip_all="Skipping ACL tests: unable to use setfacl (output: '$setfacl_out'; return code: '$setfacl_ret')"
+ test_done
+fi
+
+check_perms_and_acl () {
+ test -r "$1" &&
+ getfacl "$1" > actual &&
+ grep -q "user:root:rwx" actual &&
+ grep -q "user:${LOGNAME}:rwx" actual &&
+ egrep "mask::?r--" actual > /dev/null 2>&1 &&
+ grep -q "group::---" actual || false
+}
+
+dirs_to_set="./ .git/ .git/objects/ .git/objects/pack/"
+
+test_expect_success 'Setup test repo' '
+ setfacl -m d:u::rwx,d:g::---,d:o:---,d:m:rwx $dirs_to_set &&
+ setfacl -m m:rwx $dirs_to_set &&
+ setfacl -m u:root:rwx $dirs_to_set &&
+ setfacl -m d:u:"$LOGNAME":rwx $dirs_to_set &&
+ setfacl -m d:u:root:rwx $dirs_to_set &&
+
+ touch file.txt &&
+ git add file.txt &&
+ git commit -m "init"
+'
+
+test_expect_success 'Objects creation does not break ACLs with restrictive umask' '
+ # SHA1 for empty blob
+ check_perms_and_acl .git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
+'
+
+test_expect_success 'git gc does not break ACLs with restrictive umask' '
+ git gc &&
+ check_perms_and_acl .git/objects/pack/*.pack
+'
+
+test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
new file mode 100755
index 0000000..54ba3df
--- /dev/null
+++ b/t/t1400-update-ref.sh
@@ -0,0 +1,288 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='Test git update-ref and basic ref logging'
+. ./test-lib.sh
+
+Z=0000000000000000000000000000000000000000
+
+test_expect_success setup '
+
+ 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
+ done
+
+'
+
+m=refs/heads/master
+n_dir=refs/heads/gu
+n=$n_dir/fixes
+
+test_expect_success \
+ "create $m" \
+ "git update-ref $m $A &&
+ test $A"' = $(cat .git/'"$m"')'
+test_expect_success \
+ "create $m" \
+ "git update-ref $m $B $A &&
+ test $B"' = $(cat .git/'"$m"')'
+test_expect_success "fail to delete $m with stale ref" '
+ test_must_fail git update-ref -d $m $A &&
+ test $B = "$(cat .git/$m)"
+'
+test_expect_success "delete $m" '
+ git update-ref -d $m $B &&
+ ! test -f .git/$m
+'
+rm -f .git/$m
+
+test_expect_success "delete $m without oldvalue verification" "
+ git update-ref $m $A &&
+ test $A = \$(cat .git/$m) &&
+ git update-ref -d $m &&
+ ! test -f .git/$m
+"
+rm -f .git/$m
+
+test_expect_success \
+ "fail to create $n" \
+ "touch .git/$n_dir
+ git update-ref $n $A >out 2>err"'
+ test $? != 0'
+rm -f .git/$n_dir out err
+
+test_expect_success \
+ "create $m (by HEAD)" \
+ "git update-ref HEAD $A &&
+ test $A"' = $(cat .git/'"$m"')'
+test_expect_success \
+ "create $m (by HEAD)" \
+ "git update-ref HEAD $B $A &&
+ test $B"' = $(cat .git/'"$m"')'
+test_expect_success "fail to delete $m (by HEAD) with stale ref" '
+ test_must_fail git update-ref -d HEAD $A &&
+ test $B = $(cat .git/$m)
+'
+test_expect_success "delete $m (by HEAD)" '
+ git update-ref -d HEAD $B &&
+ ! test -f .git/$m
+'
+rm -f .git/$m
+
+cp -f .git/HEAD .git/HEAD.orig
+test_expect_success "delete symref without dereference" '
+ git update-ref --no-deref -d HEAD &&
+ ! test -f .git/HEAD
+'
+cp -f .git/HEAD.orig .git/HEAD
+
+test_expect_success "delete symref without dereference when the referred ref is packed" '
+ echo foo >foo.c &&
+ git add foo.c &&
+ git commit -m foo &&
+ git pack-refs --all &&
+ git update-ref --no-deref -d HEAD &&
+ ! test -f .git/HEAD
+'
+cp -f .git/HEAD.orig .git/HEAD
+git update-ref -d $m
+
+test_expect_success '(not) create HEAD with old sha1' "
+ test_must_fail git update-ref HEAD $A $B
+"
+test_expect_success "(not) prior created .git/$m" "
+ ! test -f .git/$m
+"
+rm -f .git/$m
+
+test_expect_success \
+ "create HEAD" \
+ "git update-ref HEAD $A"
+test_expect_success '(not) change HEAD with wrong SHA1' "
+ test_must_fail git update-ref HEAD $B $Z
+"
+test_expect_success "(not) changed .git/$m" "
+ ! test $B"' = $(cat .git/'"$m"')
+'
+rm -f .git/$m
+
+: a repository with working tree always has reflog these days...
+: >.git/logs/refs/heads/master
+test_expect_success \
+ "create $m (logged by touch)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ git update-ref HEAD '"$A"' -m "Initial Creation" &&
+ test '"$A"' = $(cat .git/'"$m"')'
+test_expect_success \
+ "update $m (logged by touch)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:31" \
+ git update-ref HEAD'" $B $A "'-m "Switch" &&
+ test '"$B"' = $(cat .git/'"$m"')'
+test_expect_success \
+ "set $m (logged by touch)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:41" \
+ git update-ref HEAD'" $A &&
+ test $A"' = $(cat .git/'"$m"')'
+
+cat >expect <<EOF
+$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch
+$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
+EOF
+test_expect_success \
+ "verifying $m's log" \
+ "test_cmp expect .git/logs/$m"
+rm -rf .git/$m .git/logs expect
+
+test_expect_success \
+ 'enable core.logAllRefUpdates' \
+ 'git config core.logAllRefUpdates true &&
+ test true = $(git config --bool --get core.logAllRefUpdates)'
+
+test_expect_success \
+ "create $m (logged by config)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:32" \
+ git update-ref HEAD'" $A "'-m "Initial Creation" &&
+ test '"$A"' = $(cat .git/'"$m"')'
+test_expect_success \
+ "update $m (logged by config)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:33" \
+ git update-ref HEAD'" $B $A "'-m "Switch" &&
+ test '"$B"' = $(cat .git/'"$m"')'
+test_expect_success \
+ "set $m (logged by config)" \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:43" \
+ git update-ref HEAD '"$A &&
+ test $A"' = $(cat .git/'"$m"')'
+
+cat >expect <<EOF
+$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 +0000 Initial Creation
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000 Switch
+$B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
+EOF
+test_expect_success \
+ "verifying $m's log" \
+ 'test_cmp expect .git/logs/$m'
+rm -f .git/$m .git/logs/$m expect
+
+git update-ref $m $D
+cat >.git/logs/$m <<EOF
+0000000000000000000000000000000000000000 $C $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 -0500
+$C $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150350 -0500
+$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 -0500
+$F $Z $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150680 -0500
+$Z $E $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 -0500
+EOF
+
+ed="Thu, 26 May 2005 18:32:00 -0500"
+gd="Thu, 26 May 2005 18:33:00 -0500"
+ld="Thu, 26 May 2005 18:43:00 -0500"
+test_expect_success \
+ 'Query "master@{May 25 2005}" (before history)' \
+ 'rm -f o e
+ git rev-parse --verify "master@{May 25 2005}" >o 2>e &&
+ test '"$C"' = $(cat o) &&
+ test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
+test_expect_success \
+ "Query master@{2005-05-25} (before history)" \
+ 'rm -f o e
+ git rev-parse --verify master@{2005-05-25} >o 2>e &&
+ test '"$C"' = $(cat o) &&
+ echo test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \
+ 'rm -f o e
+ git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
+ test '"$C"' = $(cat o) &&
+ test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
+ 'rm -f o e
+ git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
+ test '"$C"' = $(cat o) &&
+ test "" = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{May 26 2005 23:32:30}" (first non-creation change)' \
+ 'rm -f o e
+ git rev-parse --verify "master@{May 26 2005 23:32:30}" >o 2>e &&
+ test '"$A"' = $(cat o) &&
+ test "" = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
+ 'rm -f o e
+ git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
+ test '"$B"' = $(cat o) &&
+ test "warning: Log .git/logs/'"$m has gap after $gd"'." = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
+ 'rm -f o e
+ git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
+ test '"$Z"' = $(cat o) &&
+ test "" = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
+ 'rm -f o e
+ git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
+ test '"$E"' = $(cat o) &&
+ test "" = "$(cat e)"'
+test_expect_success \
+ 'Query "master@{2005-05-28}" (past end of history)' \
+ 'rm -f o e
+ git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
+ test '"$D"' = $(cat o) &&
+ test "warning: Log .git/logs/'"$m unexpectedly ended on $ld"'." = "$(cat e)"'
+
+
+rm -f .git/$m .git/logs/$m expect
+
+test_expect_success \
+ 'creating initial files' \
+ 'echo TEST >F &&
+ git add F &&
+ GIT_AUTHOR_DATE="2005-05-26 23:30" \
+ GIT_COMMITTER_DATE="2005-05-26 23:30" git commit -m add -a &&
+ h_TEST=$(git rev-parse --verify HEAD)
+ echo The other day this did not work. >M &&
+ echo And then Bob told me how to fix it. >>M &&
+ echo OTHER >F &&
+ GIT_AUTHOR_DATE="2005-05-26 23:41" \
+ GIT_COMMITTER_DATE="2005-05-26 23:41" git commit -F M -a &&
+ h_OTHER=$(git rev-parse --verify HEAD) &&
+ GIT_AUTHOR_DATE="2005-05-26 23:44" \
+ GIT_COMMITTER_DATE="2005-05-26 23:44" git commit --amend &&
+ h_FIXED=$(git rev-parse --verify HEAD) &&
+ echo Merged initial commit and a later commit. >M &&
+ echo $h_TEST >.git/MERGE_HEAD &&
+ GIT_AUTHOR_DATE="2005-05-26 23:45" \
+ GIT_COMMITTER_DATE="2005-05-26 23:45" git commit -F M &&
+ h_MERGED=$(git rev-parse --verify HEAD) &&
+ rm -f M'
+
+cat >expect <<EOF
+$Z $h_TEST $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 commit (initial): add
+$h_TEST $h_OTHER $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000 commit: The other day this did not work.
+$h_OTHER $h_FIXED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151040 +0000 commit (amend): The other day this did not work.
+$h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 commit (merge): Merged initial commit and a later commit.
+EOF
+test_expect_success \
+ 'git commit logged updates' \
+ "test_cmp expect .git/logs/$m"
+unset h_TEST h_OTHER h_FIXED h_MERGED
+
+test_expect_success \
+ 'git cat-file blob master:F (expect OTHER)' \
+ 'test OTHER = $(git cat-file blob master:F)'
+test_expect_success \
+ 'git cat-file blob master@{2005-05-26 23:30}:F (expect TEST)' \
+ 'test TEST = $(git cat-file blob "master@{2005-05-26 23:30}:F")'
+test_expect_success \
+ 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' \
+ 'test OTHER = $(git cat-file blob "master@{2005-05-26 23:42}:F")'
+
+test_done
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
new file mode 100755
index 0000000..7fa5f5b
--- /dev/null
+++ b/t/t1401-symbolic-ref.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='basic symbolic-ref tests'
+. ./test-lib.sh
+
+# If the tests munging HEAD fail, they can break detection of
+# the git repo, meaning that further tests will operate on
+# the surrounding git repo instead of the trash directory.
+reset_to_sane() {
+ echo ref: refs/heads/foo >.git/HEAD
+}
+
+test_expect_success 'symbolic-ref writes HEAD' '
+ git symbolic-ref HEAD refs/heads/foo &&
+ echo ref: refs/heads/foo >expect &&
+ test_cmp expect .git/HEAD
+'
+
+test_expect_success 'symbolic-ref reads HEAD' '
+ echo refs/heads/foo >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
+ test_must_fail git symbolic-ref HEAD foo
+'
+reset_to_sane
+
+test_expect_success 'symbolic-ref refuses bare sha1' '
+ echo content >file && git add file && git commit -m one
+ test_must_fail git symbolic-ref HEAD `git rev-parse HEAD`
+'
+reset_to_sane
+
+test_done
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
new file mode 100755
index 0000000..eb45afb
--- /dev/null
+++ b/t/t1402-check-ref-format.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+test_description='Test git check-ref-format'
+
+. ./test-lib.sh
+
+valid_ref() {
+ test_expect_success "ref name '$1' is valid" \
+ "git check-ref-format '$1'"
+}
+invalid_ref() {
+ test_expect_success "ref name '$1' is not valid" \
+ "test_must_fail git check-ref-format '$1'"
+}
+
+valid_ref 'heads/foo'
+invalid_ref 'foo'
+valid_ref 'foo/bar/baz'
+valid_ref 'refs///heads/foo'
+invalid_ref 'heads/foo/'
+invalid_ref './foo'
+invalid_ref '.refs/foo'
+invalid_ref 'heads/foo..bar'
+invalid_ref 'heads/foo?bar'
+valid_ref 'foo./bar'
+invalid_ref 'heads/foo.lock'
+valid_ref 'heads/foo@bar'
+invalid_ref 'heads/v@{ation'
+invalid_ref 'heads/foo\bar'
+
+test_expect_success "check-ref-format --branch @{-1}" '
+ T=$(git write-tree) &&
+ sha1=$(echo A | git commit-tree $T) &&
+ git update-ref refs/heads/master $sha1 &&
+ git update-ref refs/remotes/origin/master $sha1
+ git checkout master &&
+ git checkout origin/master &&
+ git checkout master &&
+ refname=$(git check-ref-format --branch @{-1}) &&
+ test "$refname" = "$sha1" &&
+ refname2=$(git check-ref-format --branch @{-2}) &&
+ test "$refname2" = master'
+
+valid_ref_normalized() {
+ test_expect_success "ref name '$1' simplifies to '$2'" "
+ refname=\$(git check-ref-format --print '$1') &&
+ test \"\$refname\" = '$2'"
+}
+invalid_ref_normalized() {
+ test_expect_success "check-ref-format --print rejects '$1'" "
+ test_must_fail git check-ref-format --print '$1'"
+}
+
+valid_ref_normalized 'heads/foo' 'heads/foo'
+valid_ref_normalized 'refs///heads/foo' 'refs/heads/foo'
+invalid_ref_normalized 'foo'
+invalid_ref_normalized 'heads/foo/../bar'
+invalid_ref_normalized 'heads/./foo'
+invalid_ref_normalized 'heads\foo'
+
+test_done
diff --git a/t/t1410-reflog.sh b/t/t1410-reflog.sh
new file mode 100755
index 0000000..25046c4
--- /dev/null
+++ b/t/t1410-reflog.sh
@@ -0,0 +1,258 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Junio C Hamano
+#
+
+test_description='Test prune and reflog expiration'
+. ./test-lib.sh
+
+check_have () {
+ gaah= &&
+ for N in "$@"
+ do
+ eval "o=\$$N" && git cat-file -t $o || {
+ echo Gaah $N
+ gaah=$N
+ break
+ }
+ done &&
+ test -z "$gaah"
+}
+
+check_fsck () {
+ output=$(git fsck --full)
+ case "$1" in
+ '')
+ test -z "$output" ;;
+ *)
+ echo "$output" | grep "$1" ;;
+ esac
+}
+
+corrupt () {
+ aa=${1%??????????????????????????????????????} zz=${1#??}
+ mv .git/objects/$aa/$zz .git/$aa$zz
+}
+
+recover () {
+ aa=${1%??????????????????????????????????????} zz=${1#??}
+ mkdir -p .git/objects/$aa
+ mv .git/$aa$zz .git/objects/$aa/$zz
+}
+
+check_dont_have () {
+ gaah= &&
+ for N in "$@"
+ do
+ eval "o=\$$N"
+ git cat-file -t $o && {
+ echo Gaah $N
+ gaah=$N
+ break
+ }
+ done
+ test -z "$gaah"
+}
+
+test_expect_success setup '
+ mkdir -p A/B &&
+ echo rat >C &&
+ echo ox >A/D &&
+ echo tiger >A/B/E &&
+ git add . &&
+
+ test_tick && git commit -m rabbit &&
+ H=`git rev-parse --verify HEAD` &&
+ A=`git rev-parse --verify HEAD:A` &&
+ B=`git rev-parse --verify HEAD:A/B` &&
+ C=`git rev-parse --verify HEAD:C` &&
+ D=`git rev-parse --verify HEAD:A/D` &&
+ E=`git rev-parse --verify HEAD:A/B/E` &&
+ check_fsck &&
+
+ test_chmod +x C &&
+ git add C &&
+ test_tick && git commit -m dragon &&
+ L=`git rev-parse --verify HEAD` &&
+ check_fsck &&
+
+ rm -f C A/B/E &&
+ echo snake >F &&
+ echo horse >A/G &&
+ git add F A/G &&
+ test_tick && git commit -a -m sheep &&
+ F=`git rev-parse --verify HEAD:F` &&
+ G=`git rev-parse --verify HEAD:A/G` &&
+ I=`git rev-parse --verify HEAD:A` &&
+ J=`git rev-parse --verify HEAD` &&
+ check_fsck &&
+
+ rm -f A/G &&
+ test_tick && git commit -a -m monkey &&
+ K=`git rev-parse --verify HEAD` &&
+ check_fsck &&
+
+ check_have A B C D E F G H I J K L &&
+
+ git prune &&
+
+ check_have A B C D E F G H I J K L &&
+
+ check_fsck &&
+
+ loglen=$(wc -l <.git/logs/refs/heads/master) &&
+ test $loglen = 4
+'
+
+test_expect_success rewind '
+ test_tick && git reset --hard HEAD~2 &&
+ test -f C &&
+ test -f A/B/E &&
+ ! test -f F &&
+ ! test -f A/G &&
+
+ check_have A B C D E F G H I J K L &&
+
+ git prune &&
+
+ check_have A B C D E F G H I J K L &&
+
+ loglen=$(wc -l <.git/logs/refs/heads/master) &&
+ test $loglen = 5
+'
+
+test_expect_success 'corrupt and check' '
+
+ corrupt $F &&
+ check_fsck "missing blob $F"
+
+'
+
+test_expect_success 'reflog expire --dry-run should not touch reflog' '
+
+ git reflog expire --dry-run \
+ --expire=$(($test_tick - 10000)) \
+ --expire-unreachable=$(($test_tick - 10000)) \
+ --stale-fix \
+ --all &&
+
+ loglen=$(wc -l <.git/logs/refs/heads/master) &&
+ test $loglen = 5 &&
+
+ check_fsck "missing blob $F"
+'
+
+test_expect_success 'reflog expire' '
+
+ git reflog expire --verbose \
+ --expire=$(($test_tick - 10000)) \
+ --expire-unreachable=$(($test_tick - 10000)) \
+ --stale-fix \
+ --all &&
+
+ loglen=$(wc -l <.git/logs/refs/heads/master) &&
+ test $loglen = 2 &&
+
+ check_fsck "dangling commit $K"
+'
+
+test_expect_success 'prune and fsck' '
+
+ git prune &&
+ check_fsck &&
+
+ check_have A B C D E H L &&
+ check_dont_have F G I J K
+
+'
+
+test_expect_success 'recover and check' '
+
+ recover $F &&
+ check_fsck "dangling blob $F"
+
+'
+
+test_expect_success 'delete' '
+ echo 1 > C &&
+ test_tick &&
+ git commit -m rat C &&
+
+ echo 2 > C &&
+ test_tick &&
+ git commit -m ox C &&
+
+ echo 3 > C &&
+ test_tick &&
+ git commit -m tiger C &&
+
+ HEAD_entry_count=$(git reflog | wc -l)
+ master_entry_count=$(git reflog show master | wc -l)
+
+ test $HEAD_entry_count = 5 &&
+ test $master_entry_count = 5 &&
+
+
+ git reflog delete master@{1} &&
+ git reflog show master > output &&
+ test $(($master_entry_count - 1)) = $(wc -l < output) &&
+ test $HEAD_entry_count = $(git reflog | wc -l) &&
+ ! grep ox < output &&
+
+ master_entry_count=$(wc -l < output)
+
+ git reflog delete HEAD@{1} &&
+ test $(($HEAD_entry_count -1)) = $(git reflog | wc -l) &&
+ test $master_entry_count = $(git reflog show master | wc -l) &&
+
+ HEAD_entry_count=$(git reflog | wc -l)
+
+ git reflog delete master@{07.04.2005.15:15:00.-0700} &&
+ git reflog show master > output &&
+ test $(($master_entry_count - 1)) = $(wc -l < output) &&
+ ! grep dragon < output
+
+'
+
+test_expect_success 'rewind2' '
+
+ test_tick && git reset --hard HEAD~2 &&
+ loglen=$(wc -l <.git/logs/refs/heads/master) &&
+ test $loglen = 4
+
+'
+
+test_expect_success '--expire=never' '
+
+ git reflog expire --verbose \
+ --expire=never \
+ --expire-unreachable=never \
+ --all &&
+ loglen=$(wc -l <.git/logs/refs/heads/master) &&
+ test $loglen = 4
+
+'
+
+test_expect_success 'gc.reflogexpire=never' '
+
+ git config gc.reflogexpire never &&
+ git config gc.reflogexpireunreachable never &&
+ git reflog expire --verbose --all &&
+ loglen=$(wc -l <.git/logs/refs/heads/master) &&
+ test $loglen = 4
+'
+
+test_expect_success 'gc.reflogexpire=false' '
+
+ git config gc.reflogexpire false &&
+ git config gc.reflogexpireunreachable false &&
+ git reflog expire --verbose --all &&
+ loglen=$(wc -l <.git/logs/refs/heads/master) &&
+ test $loglen = 4 &&
+
+ git config --unset gc.reflogexpire &&
+ git config --unset gc.reflogexpireunreachable
+
+'
+
+test_done
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
new file mode 100755
index 0000000..ba25ff3
--- /dev/null
+++ b/t/t1411-reflog-show.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+
+test_description='Test reflog display routines'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo content >file &&
+ git add file &&
+ test_tick &&
+ git commit -m one
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{0} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'log -g shows reflog headers' '
+ git log -g -1 >tmp &&
+ grep ^Reflog <tmp >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{0}: commit (initial): one
+EOF
+test_expect_success 'oneline reflog format' '
+ git log -g -1 --oneline >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{Thu Apr 7 15:13:13 2005 -0700} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (multiline)' '
+ git log -g -1 HEAD@{now} >tmp &&
+ grep ^Reflog <tmp >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{Thu Apr 7 15:13:13 2005 -0700}: commit (initial): one
+EOF
+test_expect_success 'using @{now} syntax shows reflog date (oneline)' '
+ git log -g -1 --oneline HEAD@{now} >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+Reflog: HEAD@{1112911993 -0700} (C O Mitter <committer@example.com>)
+Reflog message: commit (initial): one
+EOF
+test_expect_success 'using --date= shows reflog date (multiline)' '
+ git log -g -1 --date=raw >tmp &&
+ grep ^Reflog <tmp >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+e46513e HEAD@{1112911993 -0700}: commit (initial): one
+EOF
+test_expect_success 'using --date= shows reflog date (oneline)' '
+ git log -g -1 --oneline --date=raw >actual &&
+ test_cmp expect actual
+'
+
+: >expect
+test_expect_success 'empty reflog file' '
+ git branch empty &&
+ : >.git/logs/refs/heads/empty &&
+
+ git log -g empty >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1420-lost-found.sh b/t/t1420-lost-found.sh
new file mode 100755
index 0000000..dc9e402
--- /dev/null
+++ b/t/t1420-lost-found.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test fsck --lost-found'
+. ./test-lib.sh
+
+test_expect_success setup '
+ git config core.logAllRefUpdates 0 &&
+ : > file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m initial &&
+ echo 1 > file1 &&
+ echo 2 > file2 &&
+ git add file1 file2 &&
+ test_tick &&
+ git commit -m second &&
+ echo 3 > file3 &&
+ git add file3
+'
+
+test_expect_success 'lost and found something' '
+ git rev-parse HEAD > lost-commit &&
+ git rev-parse :file3 > lost-other &&
+ test_tick &&
+ git reset --hard HEAD^ &&
+ git fsck --lost-found &&
+ test 2 = $(ls .git/lost-found/*/* | wc -l) &&
+ test -f .git/lost-found/commit/$(cat lost-commit) &&
+ test -f .git/lost-found/other/$(cat lost-other)
+'
+
+test_done
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
new file mode 100755
index 0000000..759cf12
--- /dev/null
+++ b/t/t1450-fsck.sh
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+test_description='git fsck random collection of tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ git config i18n.commitencoding ISO-8859-1 &&
+ test_commit A fileA one &&
+ git config --unset i18n.commitencoding &&
+ git checkout HEAD^0 &&
+ test_commit B fileB two &&
+ git tag -d A B &&
+ git reflog expire --expire=now --all
+'
+
+test_expect_success 'HEAD is part of refs' '
+ test 0 = $(git fsck | wc -l)
+'
+
+test_expect_success 'loose objects borrowed from alternate are not missing' '
+ mkdir another &&
+ (
+ cd another &&
+ git init &&
+ echo ../../../.git/objects >.git/objects/info/alternates &&
+ test_commit C fileC one &&
+ git fsck >out &&
+ ! grep "missing blob" out
+ )
+'
+
+test_expect_success 'valid objects appear valid' '
+ { git fsck 2>out; true; } &&
+ ! grep error out &&
+ ! grep fatal out
+'
+
+# Corruption tests follow. Make sure to remove all traces of the
+# specific corruption you test afterwards, lest a later test trip over
+# it.
+
+test_expect_success 'object with bad sha1' '
+ sha=$(echo blob | git hash-object -w --stdin) &&
+ echo $sha &&
+ old=$(echo $sha | sed "s+^..+&/+") &&
+ new=$(dirname $old)/ffffffffffffffffffffffffffffffffffffff &&
+ sha="$(dirname $new)$(basename $new)"
+ mv .git/objects/$old .git/objects/$new &&
+ git update-index --add --cacheinfo 100644 $sha foo &&
+ tree=$(git write-tree) &&
+ cmt=$(echo bogus | git commit-tree $tree) &&
+ git update-ref refs/heads/bogus $cmt &&
+ (git fsck 2>out; true) &&
+ grep "$sha.*corrupt" out &&
+ rm -f .git/objects/$new &&
+ git update-ref -d refs/heads/bogus &&
+ git read-tree -u --reset HEAD
+'
+
+test_expect_success 'branch pointing to non-commit' '
+ git rev-parse HEAD^{tree} > .git/refs/heads/invalid &&
+ git fsck 2>out &&
+ grep "not a commit" out &&
+ git update-ref -d refs/heads/invalid
+'
+
+new=nothing
+test_expect_success 'email without @ is okay' '
+ git cat-file commit HEAD >basis &&
+ sed "s/@/AT/" basis >okay &&
+ new=$(git hash-object -t commit -w --stdin <okay) &&
+ echo "$new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ git fsck 2>out &&
+ cat out &&
+ ! grep "error in commit $new" out
+'
+git update-ref -d refs/heads/bogus
+rm -f ".git/objects/$new"
+
+new=nothing
+test_expect_success 'email with embedded > is not okay' '
+ git cat-file commit HEAD >basis &&
+ sed "s/@[a-z]/&>/" basis >bad-email &&
+ new=$(git hash-object -t commit -w --stdin <bad-email) &&
+ echo "$new" &&
+ git update-ref refs/heads/bogus "$new" &&
+ git fsck 2>out &&
+ cat out &&
+ grep "error in commit $new" out
+'
+git update-ref -d refs/heads/bogus
+rm -f ".git/objects/$new"
+
+cat > invalid-tag <<EOF
+object ffffffffffffffffffffffffffffffffffffffff
+type commit
+tag invalid
+tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+This is an invalid tag.
+EOF
+
+test_expect_success 'tag pointing to nonexistent' '
+ tag=$(git hash-object -t tag -w --stdin < invalid-tag) &&
+ echo $tag > .git/refs/tags/invalid &&
+ test_must_fail git fsck --tags >out &&
+ cat out &&
+ grep "broken link" out &&
+ rm .git/refs/tags/invalid
+'
+
+cat > wrong-tag <<EOF
+object $(echo blob | git hash-object -w --stdin)
+type commit
+tag wrong
+tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+This is an invalid tag.
+EOF
+
+test_expect_success 'tag pointing to something else than its type' '
+ tag=$(git hash-object -t tag -w --stdin < wrong-tag) &&
+ echo $tag > .git/refs/tags/wrong &&
+ test_must_fail git fsck --tags 2>out &&
+ cat out &&
+ grep "error in tag.*broken links" out &&
+ rm .git/refs/tags/wrong
+'
+
+
+
+test_done
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
new file mode 100755
index 0000000..48ee077
--- /dev/null
+++ b/t/t1500-rev-parse.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='test git rev-parse'
+. ./test-lib.sh
+
+test_rev_parse() {
+ name=$1
+ shift
+
+ test_expect_success "$name: is-bare-repository" \
+ "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: is-inside-git-dir" \
+ "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: is-inside-work-tree" \
+ "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: prefix" \
+ "test '$1' = \"\$(git rev-parse --show-prefix)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: git-dir" \
+ "test '$1' = \"\$(git rev-parse --git-dir)\""
+ shift
+ [ $# -eq 0 ] && return
+}
+
+# label is-bare is-inside-git is-inside-work prefix git-dir
+
+ROOT=$(pwd)
+
+test_rev_parse toplevel false false true '' .git
+
+cd .git || exit 1
+test_rev_parse .git/ false true false '' .
+cd objects || exit 1
+test_rev_parse .git/objects/ false true false '' "$ROOT/.git"
+cd ../.. || exit 1
+
+mkdir -p sub/dir || exit 1
+cd sub/dir || exit 1
+test_rev_parse subdirectory false false true sub/dir/ "$ROOT/.git"
+cd ../.. || exit 1
+
+git config core.bare true
+test_rev_parse 'core.bare = true' true false false
+
+git config --unset core.bare
+test_rev_parse 'core.bare undefined' false false true
+
+mkdir work || exit 1
+cd work || exit 1
+GIT_DIR=../.git
+GIT_CONFIG="$(pwd)"/../.git/config
+export GIT_DIR GIT_CONFIG
+
+git config core.bare false
+test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
+
+git config core.bare true
+test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false ''
+
+git config --unset core.bare
+test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
+
+mv ../.git ../repo.git || exit 1
+GIT_DIR=../repo.git
+GIT_CONFIG="$(pwd)"/../repo.git/config
+
+git config core.bare false
+test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''
+
+git config core.bare true
+test_rev_parse 'GIT_DIR=../repo.git, core.bare = true' true false false ''
+
+git config --unset core.bare
+test_rev_parse 'GIT_DIR=../repo.git, core.bare undefined' false false true ''
+
+test_done
diff --git a/t/t1501-worktree.sh b/t/t1501-worktree.sh
new file mode 100755
index 0000000..bd8b607
--- /dev/null
+++ b/t/t1501-worktree.sh
@@ -0,0 +1,206 @@
+#!/bin/sh
+
+test_description='test separate work tree'
+. ./test-lib.sh
+
+test_rev_parse() {
+ name=$1
+ shift
+
+ test_expect_success "$name: is-bare-repository" \
+ "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: is-inside-git-dir" \
+ "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: is-inside-work-tree" \
+ "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
+ shift
+ [ $# -eq 0 ] && return
+
+ test_expect_success "$name: prefix" \
+ "test '$1' = \"\$(git rev-parse --show-prefix)\""
+ shift
+ [ $# -eq 0 ] && return
+}
+
+EMPTY_TREE=$(git write-tree)
+mkdir -p work/sub/dir || exit 1
+mkdir -p work2 || exit 1
+mv .git repo.git || exit 1
+
+say "core.worktree = relative path"
+GIT_DIR=repo.git
+GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+export GIT_DIR GIT_CONFIG
+unset GIT_WORK_TREE
+git config core.worktree ../work
+test_rev_parse 'outside' false false false
+cd work || exit 1
+GIT_DIR=../repo.git
+GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+test_rev_parse 'inside' false false true ''
+cd sub/dir || exit 1
+GIT_DIR=../../../repo.git
+GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+say "core.worktree = absolute path"
+GIT_DIR=$(pwd)/repo.git
+GIT_CONFIG=$GIT_DIR/config
+git config core.worktree "$(pwd)/work"
+test_rev_parse 'outside' false false false
+cd work2
+test_rev_parse 'outside2' false false false
+cd ../work || exit 1
+test_rev_parse 'inside' false false true ''
+cd sub/dir || exit 1
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+say "GIT_WORK_TREE=relative path (override core.worktree)"
+GIT_DIR=$(pwd)/repo.git
+GIT_CONFIG=$GIT_DIR/config
+git config core.worktree non-existent
+GIT_WORK_TREE=work
+export GIT_WORK_TREE
+test_rev_parse 'outside' false false false
+cd work2
+test_rev_parse 'outside' false false false
+cd ../work || exit 1
+GIT_WORK_TREE=.
+test_rev_parse 'inside' false false true ''
+cd sub/dir || exit 1
+GIT_WORK_TREE=../..
+test_rev_parse 'subdirectory' false false true sub/dir/
+cd ../../.. || exit 1
+
+mv work repo.git/work
+mv work2 repo.git/work2
+
+say "GIT_WORK_TREE=absolute path, work tree below git dir"
+GIT_DIR=$(pwd)/repo.git
+GIT_CONFIG=$GIT_DIR/config
+GIT_WORK_TREE=$(pwd)/repo.git/work
+test_rev_parse 'outside' false false false
+cd repo.git || exit 1
+test_rev_parse 'in repo.git' false true false
+cd objects || exit 1
+test_rev_parse 'in repo.git/objects' false true false
+cd ../work2 || exit 1
+test_rev_parse 'in repo.git/work2' false true false
+cd ../work || exit 1
+test_rev_parse 'in repo.git/work' false true true ''
+cd sub/dir || exit 1
+test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/
+cd ../../../.. || exit 1
+
+test_expect_success 'repo finds its work tree' '
+ (cd repo.git &&
+ : > work/sub/dir/untracked &&
+ test sub/dir/untracked = "$(git ls-files --others)")
+'
+
+test_expect_success 'repo finds its work tree from work tree, too' '
+ (cd repo.git/work/sub/dir &&
+ : > tracked &&
+ git --git-dir=../../.. add tracked &&
+ cd ../../.. &&
+ test sub/dir/tracked = "$(git ls-files)")
+'
+
+test_expect_success '_gently() groks relative GIT_DIR & GIT_WORK_TREE' '
+ (cd repo.git/work/sub/dir &&
+ GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
+ git diff --exit-code tracked &&
+ echo changed > tracked &&
+ ! GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
+ git diff --exit-code tracked)
+'
+cat > diff-index-cached.expected <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 A sub/dir/tracked
+EOF
+cat > diff-index.expected <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 A sub/dir/tracked
+EOF
+
+
+test_expect_success 'git diff-index' '
+ GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff-index $EMPTY_TREE > result &&
+ test_cmp diff-index.expected result &&
+ GIT_DIR=repo.git git diff-index --cached $EMPTY_TREE > result &&
+ test_cmp diff-index-cached.expected result
+'
+cat >diff-files.expected <<\EOF
+:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M sub/dir/tracked
+EOF
+
+test_expect_success 'git diff-files' '
+ GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff-files > result &&
+ test_cmp diff-files.expected result
+'
+
+cat >diff-TREE.expected <<\EOF
+diff --git a/sub/dir/tracked b/sub/dir/tracked
+new file mode 100644
+index 0000000..5ea2ed4
+--- /dev/null
++++ b/sub/dir/tracked
+@@ -0,0 +1 @@
++changed
+EOF
+cat >diff-TREE-cached.expected <<\EOF
+diff --git a/sub/dir/tracked b/sub/dir/tracked
+new file mode 100644
+index 0000000..e69de29
+EOF
+cat >diff-FILES.expected <<\EOF
+diff --git a/sub/dir/tracked b/sub/dir/tracked
+index e69de29..5ea2ed4 100644
+--- a/sub/dir/tracked
++++ b/sub/dir/tracked
+@@ -0,0 +1 @@
++changed
+EOF
+
+test_expect_success 'git diff' '
+ GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff $EMPTY_TREE > result &&
+ test_cmp diff-TREE.expected result &&
+ GIT_DIR=repo.git git diff --cached $EMPTY_TREE > result &&
+ test_cmp diff-TREE-cached.expected result &&
+ GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff > result &&
+ test_cmp diff-FILES.expected result
+'
+
+test_expect_success 'git grep' '
+ (cd repo.git/work/sub &&
+ GIT_DIR=../.. GIT_WORK_TREE=.. git grep -l changed | grep dir/tracked)
+'
+
+test_expect_success 'git commit' '
+ (
+ cd repo.git &&
+ GIT_DIR=. GIT_WORK_TREE=work git commit -a -m done
+ )
+'
+
+test_expect_success 'absolute pathspec should fail gracefully' '
+ (
+ cd repo.git || exit 1
+ git config --unset core.worktree
+ test_must_fail git log HEAD -- /home
+ )
+'
+
+test_expect_success 'make_relative_path handles double slashes in GIT_DIR' '
+ : > dummy_file
+ echo git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file &&
+ git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file
+'
+
+test_done
diff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh
new file mode 100755
index 0000000..b3195c4
--- /dev/null
+++ b/t/t1502-rev-parse-parseopt.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='test git rev-parse --parseopt'
+. ./test-lib.sh
+
+cat > expect <<\END_EXPECT
+cat <<\EOF
+usage: some-command [options] <args>...
+
+ some-command does foo and bar!
+
+ -h, --help show the help
+ --foo some nifty option --foo
+ --bar ... some cool option --bar with an argument
+
+An option group Header
+ -C[...] option C with an optional argument
+
+Extras
+ --extra1 line above used to cause a segfault but no longer does
+
+EOF
+END_EXPECT
+
+cat > optionspec << EOF
+some-command [options] <args>...
+
+some-command does foo and bar!
+--
+h,help show the help
+
+foo some nifty option --foo
+bar= some cool option --bar with an argument
+
+ An option group Header
+C? option C with an optional argument
+
+Extras
+extra1 line above used to cause a segfault but no longer does
+EOF
+
+test_expect_success 'test --parseopt help output' '
+ git rev-parse --parseopt -- -h > output < optionspec
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo --bar 'ham' -- 'arg'
+EOF
+
+test_expect_success 'test --parseopt' '
+ git rev-parse --parseopt -- --foo --bar=ham arg < optionspec > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'test --parseopt with mixed options and arguments' '
+ git rev-parse --parseopt -- --foo arg --bar=ham < optionspec > output &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- 'arg' '--bar=ham'
+EOF
+
+test_expect_success 'test --parseopt with --' '
+ git rev-parse --parseopt -- --foo -- arg --bar=ham < optionspec > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'test --parseopt --stop-at-non-option' '
+ git rev-parse --parseopt --stop-at-non-option -- --foo arg --bar=ham < optionspec > output &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- '--' 'arg' '--bar=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash' '
+ git rev-parse --parseopt --keep-dashdash -- --foo -- arg --bar=ham < optionspec > output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+set -- --foo -- '--' 'arg' '--spam=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option with --' '
+ git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo -- arg --spam=ham <optionspec >output &&
+ test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- 'arg' '--spam=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash --stop-at-non-option without --' '
+ git rev-parse --parseopt --keep-dashdash --stop-at-non-option -- --foo arg --spam=ham <optionspec >output &&
+ test_cmp expect output
+'
+
+test_done
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
new file mode 100755
index 0000000..cc65394
--- /dev/null
+++ b/t/t1503-rev-parse-verify.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Christian Couder
+#
+test_description='test git rev-parse --verify'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_line_into_file()
+{
+ _line=$1
+ _file=$2
+
+ if [ -f "$_file" ]; then
+ echo "$_line" >> $_file || return $?
+ MSG="Add <$_line> into <$_file>."
+ else
+ echo "$_line" > $_file || return $?
+ git add $_file || return $?
+ MSG="Create file <$_file> with <$_line> inside."
+ fi
+
+ test_tick
+ git commit --quiet -m "$MSG" $_file
+}
+
+HASH1=
+HASH2=
+HASH3=
+HASH4=
+
+test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' '
+ add_line_into_file "1: Hello World" hello &&
+ HASH1=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "2: A new day for git" hello &&
+ HASH2=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "3: Another new day for git" hello &&
+ HASH3=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "4: Ciao for now" hello &&
+ HASH4=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'works with one good rev' '
+ rev_hash1=$(git rev-parse --verify $HASH1) &&
+ test "$rev_hash1" = "$HASH1" &&
+ rev_hash2=$(git rev-parse --verify $HASH2) &&
+ test "$rev_hash2" = "$HASH2" &&
+ rev_hash3=$(git rev-parse --verify $HASH3) &&
+ test "$rev_hash3" = "$HASH3" &&
+ rev_hash4=$(git rev-parse --verify $HASH4) &&
+ test "$rev_hash4" = "$HASH4" &&
+ rev_master=$(git rev-parse --verify master) &&
+ test "$rev_master" = "$HASH4" &&
+ rev_head=$(git rev-parse --verify HEAD) &&
+ test "$rev_head" = "$HASH4"
+'
+
+test_expect_success 'fails with any bad rev or many good revs' '
+ test_must_fail git rev-parse --verify 2>error &&
+ grep "single revision" error &&
+ test_must_fail git rev-parse --verify foo 2>error &&
+ grep "single revision" error &&
+ test_must_fail git rev-parse --verify HEAD bar 2>error &&
+ grep "single revision" error &&
+ test_must_fail git rev-parse --verify baz HEAD 2>error &&
+ grep "single revision" error &&
+ test_must_fail git rev-parse --verify $HASH2 HEAD 2>error &&
+ grep "single revision" error
+'
+
+test_expect_success 'fails silently when using -q' '
+ test_must_fail git rev-parse --verify --quiet 2>error &&
+ test -z "$(cat error)" &&
+ test_must_fail git rev-parse -q --verify foo 2>error &&
+ test -z "$(cat error)" &&
+ test_must_fail git rev-parse --verify -q HEAD bar 2>error &&
+ test -z "$(cat error)" &&
+ test_must_fail git rev-parse --quiet --verify baz HEAD 2>error &&
+ test -z "$(cat error)" &&
+ test_must_fail git rev-parse -q --verify $HASH2 HEAD 2>error &&
+ test -z "$(cat error)"
+'
+
+test_expect_success 'no stdout output on error' '
+ test -z "$(git rev-parse --verify)" &&
+ test -z "$(git rev-parse --verify foo)" &&
+ test -z "$(git rev-parse --verify baz HEAD)" &&
+ test -z "$(git rev-parse --verify HEAD bar)" &&
+ test -z "$(git rev-parse --verify $HASH2 HEAD)"
+'
+
+test_expect_success 'use --default' '
+ git rev-parse --verify --default master &&
+ git rev-parse --verify --default master HEAD &&
+ git rev-parse --default master --verify &&
+ git rev-parse --default master --verify HEAD &&
+ git rev-parse --verify HEAD --default master &&
+ test_must_fail git rev-parse --verify foo --default master &&
+ test_must_fail git rev-parse --default HEAD --verify bar &&
+ test_must_fail git rev-parse --verify --default HEAD baz &&
+ test_must_fail git rev-parse --default foo --verify &&
+ test_must_fail git rev-parse --verify --default bar
+'
+
+test_done
diff --git a/t/t1504-ceiling-dirs.sh b/t/t1504-ceiling-dirs.sh
new file mode 100755
index 0000000..df5ad8c
--- /dev/null
+++ b/t/t1504-ceiling-dirs.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+
+test_description='test GIT_CEILING_DIRECTORIES'
+. ./test-lib.sh
+
+test_prefix() {
+ test_expect_success "$1" \
+ "test '$2' = \"\$(git rev-parse --show-prefix)\""
+}
+
+test_fail() {
+ test_expect_code 128 "$1: prefix" \
+ "git rev-parse --show-prefix"
+}
+
+TRASH_ROOT="$PWD"
+ROOT_PARENT=$(dirname "$TRASH_ROOT")
+
+
+unset GIT_CEILING_DIRECTORIES
+test_prefix no_ceil ""
+
+export GIT_CEILING_DIRECTORIES
+
+GIT_CEILING_DIRECTORIES=""
+test_prefix ceil_empty ""
+
+GIT_CEILING_DIRECTORIES="$ROOT_PARENT"
+test_prefix ceil_at_parent ""
+
+GIT_CEILING_DIRECTORIES="$ROOT_PARENT/"
+test_prefix ceil_at_parent_slash ""
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT"
+test_prefix ceil_at_trash ""
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/"
+test_prefix ceil_at_trash_slash ""
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub"
+test_prefix ceil_at_sub ""
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/"
+test_prefix ceil_at_sub_slash ""
+
+
+mkdir -p sub/dir || exit 1
+cd sub/dir || exit 1
+
+unset GIT_CEILING_DIRECTORIES
+test_prefix subdir_no_ceil "sub/dir/"
+
+export GIT_CEILING_DIRECTORIES
+
+GIT_CEILING_DIRECTORIES=""
+test_prefix subdir_ceil_empty "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT"
+test_fail subdir_ceil_at_trash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/"
+test_fail subdir_ceil_at_trash_slash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub"
+test_fail subdir_ceil_at_sub
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/"
+test_fail subdir_ceil_at_sub_slash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/dir"
+test_prefix subdir_ceil_at_subdir "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/dir/"
+test_prefix subdir_ceil_at_subdir_slash "sub/dir/"
+
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/su"
+test_prefix subdir_ceil_at_su "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/su/"
+test_prefix subdir_ceil_at_su_slash "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/di"
+test_prefix subdir_ceil_at_sub_di "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub/di"
+test_prefix subdir_ceil_at_sub_di_slash "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/subdi"
+test_prefix subdir_ceil_at_subdi "sub/dir/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/subdi"
+test_prefix subdir_ceil_at_subdi_slash "sub/dir/"
+
+
+GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub"
+test_fail second_of_two
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:/bar"
+test_fail first_of_two
+
+GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub:/bar"
+test_fail second_of_three
+
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub"
+GIT_DIR=../../.git
+export GIT_DIR
+test_prefix git_dir_specified ""
+unset GIT_DIR
+
+
+cd ../.. || exit 1
+mkdir -p s/d || exit 1
+cd s/d || exit 1
+
+unset GIT_CEILING_DIRECTORIES
+test_prefix sd_no_ceil "s/d/"
+
+export GIT_CEILING_DIRECTORIES
+
+GIT_CEILING_DIRECTORIES=""
+test_prefix sd_ceil_empty "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT"
+test_fail sd_ceil_at_trash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/"
+test_fail sd_ceil_at_trash_slash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s"
+test_fail sd_ceil_at_s
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/"
+test_fail sd_ceil_at_s_slash
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/d"
+test_prefix sd_ceil_at_sd "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/d/"
+test_prefix sd_ceil_at_sd_slash "s/d/"
+
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/su"
+test_prefix sd_ceil_at_su "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/su/"
+test_prefix sd_ceil_at_su_slash "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/di"
+test_prefix sd_ceil_at_s_di "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/s/di"
+test_prefix sd_ceil_at_s_di_slash "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sdi"
+test_prefix sd_ceil_at_sdi "s/d/"
+
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sdi"
+test_prefix sd_ceil_at_sdi_slash "s/d/"
+
+
+test_done
diff --git a/t/t1505-rev-parse-last.sh b/t/t1505-rev-parse-last.sh
new file mode 100755
index 0000000..d709ecf
--- /dev/null
+++ b/t/t1505-rev-parse-last.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='test @{-N} syntax'
+
+. ./test-lib.sh
+
+
+make_commit () {
+ echo "$1" > "$1" &&
+ git add "$1" &&
+ git commit -m "$1"
+}
+
+
+test_expect_success 'setup' '
+
+ make_commit 1 &&
+ git branch side &&
+ make_commit 2 &&
+ make_commit 3 &&
+ git checkout side &&
+ make_commit 4 &&
+ git merge master &&
+ git checkout master
+
+'
+
+# 1 -- 2 -- 3 master
+# \ \
+# \ \
+# --- 4 --- 5 side
+#
+# and 'side' should be the last branch
+
+test_rev_equivalent () {
+
+ git rev-parse "$1" > expect &&
+ git rev-parse "$2" > output &&
+ test_cmp expect output
+
+}
+
+test_expect_success '@{-1} works' '
+ test_rev_equivalent side @{-1}
+'
+
+test_expect_success '@{-1}~2 works' '
+ test_rev_equivalent side~2 @{-1}~2
+'
+
+test_expect_success '@{-1}^2 works' '
+ test_rev_equivalent side^2 @{-1}^2
+'
+
+test_expect_success '@{-1}@{1} works' '
+ test_rev_equivalent side@{1} @{-1}@{1}
+'
+
+test_expect_success '@{-2} works' '
+ test_rev_equivalent master @{-2}
+'
+
+test_expect_success '@{-3} fails' '
+ test_must_fail git rev-parse @{-3}
+'
+
+test_done
+
+
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
new file mode 100755
index 0000000..af721f9
--- /dev/null
+++ b/t/t1506-rev-parse-diagnosis.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='test git rev-parse diagnosis for invalid argument'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+HASH_file=
+
+test_expect_success 'set up basic repo' '
+ echo one > file.txt &&
+ mkdir subdir &&
+ echo two > subdir/file.txt &&
+ echo three > subdir/file2.txt &&
+ git add . &&
+ git commit -m init &&
+ echo four > index-only.txt &&
+ git add index-only.txt &&
+ echo five > disk-only.txt
+'
+
+test_expect_success 'correct file objects' '
+ HASH_file=$(git rev-parse HEAD:file.txt) &&
+ git rev-parse HEAD:subdir/file.txt &&
+ git rev-parse :index-only.txt &&
+ (cd subdir &&
+ git rev-parse HEAD:subdir/file2.txt &&
+ test $HASH_file = $(git rev-parse HEAD:file.txt) &&
+ test $HASH_file = $(git rev-parse :file.txt) &&
+ test $HASH_file = $(git rev-parse :0:file.txt) )
+'
+
+test_expect_success 'incorrect revision id' '
+ test_must_fail git rev-parse foobar:file.txt 2>error &&
+ grep "Invalid object name '"'"'foobar'"'"'." error &&
+ test_must_fail git rev-parse foobar 2> error &&
+ grep "unknown revision or path not in the working tree." error
+'
+
+test_expect_success 'incorrect file in sha1:path' '
+ test_must_fail git rev-parse HEAD:nothing.txt 2> error &&
+ grep "fatal: Path '"'"'nothing.txt'"'"' does not exist in '"'"'HEAD'"'"'" error &&
+ test_must_fail git rev-parse HEAD:index-only.txt 2> error &&
+ grep "fatal: Path '"'"'index-only.txt'"'"' exists on disk, but not in '"'"'HEAD'"'"'." error &&
+ (cd subdir &&
+ test_must_fail git rev-parse HEAD:file2.txt 2> error &&
+ grep "Did you mean '"'"'HEAD:subdir/file2.txt'"'"'?" error )
+'
+
+test_expect_success 'incorrect file in :path and :N:path' '
+ test_must_fail git rev-parse :nothing.txt 2> error &&
+ grep "fatal: Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
+ test_must_fail git rev-parse :1:nothing.txt 2> error &&
+ grep "Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
+ test_must_fail git rev-parse :1:file.txt 2> error &&
+ grep "Did you mean '"'"':0:file.txt'"'"'?" error &&
+ (cd subdir &&
+ test_must_fail git rev-parse :1:file.txt 2> error &&
+ grep "Did you mean '"'"':0:file.txt'"'"'?" error &&
+ test_must_fail git rev-parse :file2.txt 2> error &&
+ grep "Did you mean '"'"':0:subdir/file2.txt'"'"'?" error &&
+ test_must_fail git rev-parse :2:file2.txt 2> error &&
+ grep "Did you mean '"'"':0:subdir/file2.txt'"'"'?" error) &&
+ test_must_fail git rev-parse :disk-only.txt 2> error &&
+ grep "fatal: Path '"'"'disk-only.txt'"'"' exists on disk, but not in the index." error
+'
+
+test_done
diff --git a/t/t1507-rev-parse-upstream.sh b/t/t1507-rev-parse-upstream.sh
new file mode 100755
index 0000000..8c8dfda
--- /dev/null
+++ b/t/t1507-rev-parse-upstream.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+test_description='test <branch>@{upstream} syntax'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+
+ test_commit 1 &&
+ git checkout -b side &&
+ test_commit 2 &&
+ git checkout master &&
+ git clone . clone &&
+ test_commit 3 &&
+ (cd clone &&
+ test_commit 4 &&
+ git branch --track my-side origin/side)
+
+'
+
+full_name () {
+ (cd clone &&
+ git rev-parse --symbolic-full-name "$@")
+}
+
+commit_subject () {
+ (cd clone &&
+ git show -s --pretty=format:%s "$@")
+}
+
+test_expect_success '@{upstream} resolves to correct full name' '
+ test refs/remotes/origin/master = "$(full_name @{upstream})"
+'
+
+test_expect_success '@{u} resolves to correct full name' '
+ test refs/remotes/origin/master = "$(full_name @{u})"
+'
+
+test_expect_success 'my-side@{upstream} resolves to correct full name' '
+ test refs/remotes/origin/side = "$(full_name my-side@{u})"
+'
+
+test_expect_success 'my-side@{u} resolves to correct commit' '
+ git checkout side &&
+ test_commit 5 &&
+ (cd clone && git fetch) &&
+ test 2 = "$(commit_subject my-side)" &&
+ test 5 = "$(commit_subject my-side@{u})"
+'
+
+test_expect_success 'not-tracking@{u} fails' '
+ test_must_fail full_name non-tracking@{u} &&
+ (cd clone && git checkout --no-track -b non-tracking) &&
+ test_must_fail full_name non-tracking@{u}
+'
+
+test_expect_success '<branch>@{u}@{1} resolves correctly' '
+ test_commit 6 &&
+ (cd clone && git fetch) &&
+ test 5 = $(commit_subject my-side@{u}@{1})
+'
+
+test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
+ git checkout HEAD^0 &&
+ test_must_fail git rev-parse @{u}
+'
+
+test_expect_success 'checkout -b new my-side@{u} forks from the same' '
+(
+ cd clone &&
+ git checkout -b new my-side@{u} &&
+ git rev-parse --symbolic-full-name my-side@{u} >expect &&
+ git rev-parse --symbolic-full-name new@{u} >actual &&
+ test_cmp expect actual
+)
+'
+
+test_expect_success 'merge my-side@{u} records the correct name' '
+(
+ sq="'\''" &&
+ cd clone || exit
+ git checkout master || exit
+ git branch -D new ;# can fail but is ok
+ git branch -t new my-side@{u} &&
+ git merge -s ours new@{u} &&
+ git show -s --pretty=format:%s >actual &&
+ echo "Merge remote branch ${sq}origin/side${sq}" >expect &&
+ test_cmp expect actual
+)
+'
+
+test_expect_success 'branch -d other@{u}' '
+ git checkout -t -b other master &&
+ git branch -d @{u} &&
+ git for-each-ref refs/heads/master >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'checkout other@{u}' '
+ git branch -f master HEAD &&
+ git checkout -t -b another master &&
+ git checkout @{u} &&
+ git symbolic-ref HEAD >actual &&
+ echo refs/heads/master >expect &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+commit 8f489d01d0cc65c3b0f09504ec50b5ed02a70bd5
+Reflog: master@{0} (C O Mitter <committer@example.com>)
+Reflog message: branch: Created from HEAD
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:15:13 2005 -0700
+
+ 3
+EOF
+test_expect_success 'log -g other@{u}' '
+ git log -1 -g other@{u} >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+commit 8f489d01d0cc65c3b0f09504ec50b5ed02a70bd5
+Reflog: master@{Thu Apr 7 15:17:13 2005 -0700} (C O Mitter <committer@example.com>)
+Reflog message: branch: Created from HEAD
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:15:13 2005 -0700
+
+ 3
+EOF
+
+test_expect_success 'log -g other@{u}@{now}' '
+ git log -1 -g other@{u}@{now} >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t1508-at-combinations.sh b/t/t1508-at-combinations.sh
new file mode 100755
index 0000000..d5d6244
--- /dev/null
+++ b/t/t1508-at-combinations.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='test various @{X} syntax combinations together'
+. ./test-lib.sh
+
+check() {
+test_expect_${3:-success} "$1 = $2" "
+ echo '$2' >expect &&
+ git log -1 --format=%s '$1' >actual &&
+ test_cmp expect actual
+"
+}
+nonsense() {
+test_expect_${2:-success} "$1 is nonsensical" "
+ test_must_fail git log -1 '$1'
+"
+}
+fail() {
+ "$@" failure
+}
+
+test_expect_success 'setup' '
+ test_commit master-one &&
+ test_commit master-two &&
+ git checkout -b upstream-branch &&
+ test_commit upstream-one &&
+ test_commit upstream-two &&
+ git checkout -b old-branch &&
+ test_commit old-one &&
+ test_commit old-two &&
+ git checkout -b new-branch &&
+ test_commit new-one &&
+ test_commit new-two &&
+ git config branch.old-branch.remote . &&
+ git config branch.old-branch.merge refs/heads/master &&
+ git config branch.new-branch.remote . &&
+ git config branch.new-branch.merge refs/heads/upstream-branch
+'
+
+check HEAD new-two
+check "@{1}" new-one
+check "@{-1}" old-two
+check "@{-1}@{1}" old-one
+check "@{u}" upstream-two
+check "@{u}@{1}" upstream-one
+check "@{-1}@{u}" master-two
+check "@{-1}@{u}@{1}" master-one
+nonsense "@{u}@{-1}"
+nonsense "@{1}@{u}"
+
+test_done
diff --git a/t/t1509-root-worktree.sh b/t/t1509-root-worktree.sh
new file mode 100755
index 0000000..7f60fd0
--- /dev/null
+++ b/t/t1509-root-worktree.sh
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+test_description='Test Git when git repository is located at root
+
+This test requires write access in root. Do not bother if you do not
+have a throwaway chroot or VM.
+
+Script t1509/prepare-chroot.sh may help you setup chroot, then you
+can chroot in and execute this test from there.
+'
+
+. ./test-lib.sh
+
+test_cmp_val() {
+ echo "$1" > expected
+ echo "$2" > result
+ test_cmp expected result
+}
+
+test_vars() {
+ test_expect_success "$1: gitdir" '
+ test_cmp_val "'"$2"'" "$(git rev-parse --git-dir)"
+ '
+
+ test_expect_success "$1: worktree" '
+ test_cmp_val "'"$3"'" "$(git rev-parse --show-toplevel)"
+ '
+
+ test_expect_success "$1: prefix" '
+ test_cmp_val "'"$4"'" "$(git rev-parse --show-prefix)"
+ '
+}
+
+test_foobar_root() {
+ test_expect_success 'add relative' '
+ test -z "$(cd / && git ls-files)" &&
+ git add foo/foome &&
+ git add foo/bar/barme &&
+ git add me &&
+ ( cd / && git ls-files --stage ) > result &&
+ test_cmp /ls.expected result &&
+ rm "$(git rev-parse --git-dir)/index"
+ '
+
+ test_expect_success 'add absolute' '
+ test -z "$(cd / && git ls-files)" &&
+ git add /foo/foome &&
+ git add /foo/bar/barme &&
+ git add /me &&
+ ( cd / && git ls-files --stage ) > result &&
+ test_cmp /ls.expected result &&
+ rm "$(git rev-parse --git-dir)/index"
+ '
+
+}
+
+test_foobar_foo() {
+ test_expect_success 'add relative' '
+ test -z "$(cd / && git ls-files)" &&
+ git add foome &&
+ git add bar/barme &&
+ git add ../me &&
+ ( cd / && git ls-files --stage ) > result &&
+ test_cmp /ls.expected result &&
+ rm "$(git rev-parse --git-dir)/index"
+ '
+
+ test_expect_success 'add absolute' '
+ test -z "$(cd / && git ls-files)" &&
+ git add /foo/foome &&
+ git add /foo/bar/barme &&
+ git add /me &&
+ ( cd / && git ls-files --stage ) > result &&
+ test_cmp /ls.expected result &&
+ rm "$(git rev-parse --git-dir)/index"
+ '
+}
+
+test_foobar_foobar() {
+ test_expect_success 'add relative' '
+ test -z "$(cd / && git ls-files)" &&
+ git add ../foome &&
+ git add barme &&
+ git add ../../me &&
+ ( cd / && git ls-files --stage ) > result &&
+ test_cmp /ls.expected result &&
+ rm "$(git rev-parse --git-dir)/index"
+ '
+
+ test_expect_success 'add absolute' '
+ test -z "$(cd / && git ls-files)" &&
+ git add /foo/foome &&
+ git add /foo/bar/barme &&
+ git add /me &&
+ ( cd / && git ls-files --stage ) > result &&
+ test_cmp /ls.expected result &&
+ rm "$(git rev-parse --git-dir)/index"
+ '
+}
+
+if ! test_have_prereq POSIXPERM || ! [ -w / ]; then
+ skip_all="Dangerous test skipped. Read this test if you want to execute it"
+ test_done
+fi
+
+if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
+ skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
+ test_done
+fi
+
+if [ "$UID" = 0 ]; then
+ skip_all="No you can't run this with root"
+ test_done
+fi
+
+ONE_SHA1=d00491fd7e5bb6fa28c517a0bb32b8b506539d4d
+
+test_expect_success 'setup' '
+ rm -rf /foo
+ mkdir /foo &&
+ mkdir /foo/bar &&
+ echo 1 > /foo/foome &&
+ echo 1 > /foo/bar/barme &&
+ echo 1 > /me
+'
+
+say "GIT_DIR absolute, GIT_WORK_TREE set"
+
+test_expect_success 'go to /' 'cd /'
+
+cat >ls.expected <<EOF
+100644 $ONE_SHA1 0 foo/bar/barme
+100644 $ONE_SHA1 0 foo/foome
+100644 $ONE_SHA1 0 me
+EOF
+
+export GIT_DIR="$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=/
+
+test_vars 'abs gitdir, root' "$GIT_DIR" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /foo' 'cd /foo'
+
+test_vars 'abs gitdir, foo' "$GIT_DIR" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+
+test_vars 'abs gitdir, foo/bar' "$GIT_DIR" "/" "foo/bar/"
+test_foobar_foobar
+
+say "GIT_DIR relative, GIT_WORK_TREE set"
+
+test_expect_success 'go to /' 'cd /'
+
+export GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git"
+export GIT_WORK_TREE=/
+
+test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /foo' 'cd /foo'
+
+export GIT_DIR="../$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=/
+
+test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+
+export GIT_DIR="../../$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=/
+
+test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
+test_foobar_foobar
+
+say "GIT_DIR relative, GIT_WORK_TREE relative"
+
+test_expect_success 'go to /' 'cd /'
+
+export GIT_DIR="$(echo $TRASH_DIRECTORY|sed 's,^/,,')/.git"
+export GIT_WORK_TREE=.
+
+test_vars 'rel gitdir, root' "$GIT_DIR" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /' 'cd /foo'
+
+export GIT_DIR="../$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=..
+
+test_vars 'rel gitdir, foo' "$TRASH_DIRECTORY/.git" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+
+export GIT_DIR="../../$TRASH_DIRECTORY/.git"
+export GIT_WORK_TREE=../..
+
+test_vars 'rel gitdir, foo/bar' "$TRASH_DIRECTORY/.git" "/" "foo/bar/"
+test_foobar_foobar
+
+say ".git at root"
+
+unset GIT_DIR
+unset GIT_WORK_TREE
+
+test_expect_success 'go to /' 'cd /'
+test_expect_success 'setup' '
+ rm -rf /.git
+ echo "Initialized empty Git repository in /.git/" > expected &&
+ git init > result &&
+ test_cmp expected result
+'
+
+test_vars 'auto gitdir, root' ".git" "/" ""
+test_foobar_root
+
+test_expect_success 'go to /foo' 'cd /foo'
+test_vars 'auto gitdir, foo' "/.git" "/" "foo/"
+test_foobar_foo
+
+test_expect_success 'go to /foo/bar' 'cd /foo/bar'
+test_vars 'auto gitdir, foo/bar' "/.git" "/" "foo/bar/"
+test_foobar_foobar
+
+test_expect_success 'cleanup' 'rm -rf /.git'
+
+say "auto bare gitdir"
+
+# DESTROYYYYY!!!!!
+test_expect_success 'setup' '
+ rm -rf /refs /objects /info /hooks
+ rm /*
+ cd / &&
+ echo "Initialized empty Git repository in /" > expected &&
+ git init --bare > result &&
+ test_cmp expected result
+'
+
+test_vars 'auto gitdir, root' "." "" ""
+
+test_expect_success 'go to /foo' 'cd /foo'
+
+test_vars 'auto gitdir, root' "/" "" ""
+
+test_done
diff --git a/t/t1509/excludes b/t/t1509/excludes
new file mode 100644
index 0000000..d4d21d3
--- /dev/null
+++ b/t/t1509/excludes
@@ -0,0 +1,14 @@
+*.o
+*~
+*.bak
+*.c
+*.h
+.git
+contrib
+Documentation
+git-gui
+gitk-git
+gitweb
+t/t4013
+t/t5100
+t/t5515
diff --git a/t/t1509/prepare-chroot.sh b/t/t1509/prepare-chroot.sh
new file mode 100755
index 0000000..c5334a8
--- /dev/null
+++ b/t/t1509/prepare-chroot.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+die() {
+ echo >&2 "$@"
+ exit 1
+}
+
+xmkdir() {
+ while [ -n "$1" ]; do
+ [ -d "$1" ] || mkdir "$1" || die "Unable to mkdir $1"
+ shift
+ done
+}
+
+R="$1"
+
+[ -n "$R" ] || die "Usage: prepare-chroot.sh <root>"
+[ -x git ] || die "This script needs to be executed at git source code's top directory"
+[ -x /bin/busybox ] || die "You need busybox"
+
+xmkdir "$R" "$R/bin" "$R/etc" "$R/lib" "$R/dev"
+[ -c "$R/dev/null" ] || die "/dev/null is missing. Do mknod $R/dev/null c 1 3 && chmod 666 $R/dev/null"
+echo "root:x:0:0:root:/:/bin/sh" > "$R/etc/passwd"
+echo "$(id -nu):x:$(id -u):$(id -g)::$(pwd)/t:/bin/sh" >> "$R/etc/passwd"
+echo "root::0:root" > "$R/etc/group"
+echo "$(id -ng)::$(id -g):$(id -nu)" >> "$R/etc/group"
+
+[ -x "$R/bin/busybox" ] || cp /bin/busybox "$R/bin/busybox"
+[ -x "$R/bin/sh" ] || ln -s /bin/busybox "$R/bin/sh"
+[ -x "$R/bin/su" ] || ln -s /bin/busybox "$R/bin/su"
+
+mkdir -p "$R$(pwd)"
+rsync --exclude-from t/t1509/excludes -Ha . "$R$(pwd)"
+ldd git | grep '/' | sed 's,.*\s\(/[^ ]*\).*,\1,' | while read i; do
+ mkdir -p "$R$(dirname $i)"
+ cp "$i" "$R/$i"
+done
+echo "Execute this in root: 'chroot $R /bin/su - $(id -nu)'"
diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh
new file mode 100755
index 0000000..de3edb5
--- /dev/null
+++ b/t/t2000-checkout-cache-clash.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git checkout-index test.
+
+This test registers the following filesystem structure in the
+cache:
+
+ path0 - a file
+ path1/file1 - a file in a directory
+
+And then tries to checkout in a work tree that has the following:
+
+ path0/file0 - a file in a directory
+ path1 - a file
+
+The git checkout-index command should fail when attempting to checkout
+path0, finding it is occupied by a directory, and path1/file1, finding
+path1 is occupied by a non-directory. With "-f" flag, it should remove
+the conflicting paths and succeed.
+'
+. ./test-lib.sh
+
+date >path0
+mkdir path1
+date >path1/file1
+
+test_expect_success \
+ 'git update-index --add various paths.' \
+ 'git update-index --add path0 path1/file1'
+
+rm -fr path0 path1
+mkdir path0
+date >path0/file0
+date >path1
+
+test_expect_success \
+ 'git checkout-index without -f should fail on conflicting work tree.' \
+ 'test_must_fail git checkout-index -a'
+
+test_expect_success \
+ 'git checkout-index with -f should succeed.' \
+ 'git checkout-index -f -a'
+
+test_expect_success \
+ 'git checkout-index conflicting paths.' \
+ 'test -f path0 && test -d path1 && test -f path1/file1'
+
+test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
+ mkdir -p tar/get &&
+ ln -s tar/get there &&
+ echo first &&
+ git checkout-index -a -f --prefix=there/ &&
+ echo second &&
+ git checkout-index -a -f --prefix=there/
+'
+
+test_done
diff --git a/t/t2001-checkout-cache-clash.sh b/t/t2001-checkout-cache-clash.sh
new file mode 100755
index 0000000..98aa73e
--- /dev/null
+++ b/t/t2001-checkout-cache-clash.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git checkout-index test.
+
+This test registers the following filesystem structure in the cache:
+
+ path0/file0 - a file in a directory
+ path1/file1 - a file in a directory
+
+and attempts to check it out when the work tree has:
+
+ path0/file0 - a file in a directory
+ path1 - a symlink pointing at "path0"
+
+Checkout cache should fail to extract path1/file1 because the leading
+path path1 is occupied by a non-directory. With "-f" it should remove
+the symlink path1 and create directory path1 and file path1/file1.
+'
+. ./test-lib.sh
+
+show_files() {
+ # show filesystem files, just [-dl] for type and name
+ find path? -ls |
+ sed -e 's/^[0-9]* * [0-9]* * \([-bcdl]\)[^ ]* *[0-9]* *[^ ]* *[^ ]* *[0-9]* [A-Z][a-z][a-z] [0-9][0-9] [^ ]* /fs: \1 /'
+ # what's in the cache, just mode and name
+ git ls-files --stage |
+ sed -e 's/^\([0-9]*\) [0-9a-f]* [0-3] /ca: \1 /'
+ # what's in the tree, just mode and name.
+ git ls-tree -r "$1" |
+ sed -e 's/^\([0-9]*\) [^ ]* [0-9a-f]* /tr: \1 /'
+}
+
+mkdir path0
+date >path0/file0
+test_expect_success \
+ 'git update-index --add path0/file0' \
+ 'git update-index --add path0/file0'
+test_expect_success \
+ 'writing tree out with git write-tree' \
+ 'tree1=$(git write-tree)'
+test_debug 'show_files $tree1'
+
+mkdir path1
+date >path1/file1
+test_expect_success \
+ 'git update-index --add path1/file1' \
+ 'git update-index --add path1/file1'
+test_expect_success \
+ 'writing tree out with git write-tree' \
+ 'tree2=$(git write-tree)'
+test_debug 'show_files $tree2'
+
+rm -fr path1
+test_expect_success \
+ 'read previously written tree and checkout.' \
+ 'git read-tree -m $tree1 && git checkout-index -f -a'
+test_debug 'show_files $tree1'
+
+test_expect_success SYMLINKS \
+ 'git update-index --add a symlink.' \
+ 'ln -s path0 path1 &&
+ git update-index --add path1'
+test_expect_success \
+ 'writing tree out with git write-tree' \
+ 'tree3=$(git write-tree)'
+test_debug 'show_files $tree3'
+
+# Morten says "Got that?" here.
+# Test begins.
+
+test_expect_success \
+ 'read previously written tree and checkout.' \
+ 'git read-tree $tree2 && git checkout-index -f -a'
+test_debug 'show_files $tree2'
+
+test_expect_success \
+ 'checking out conflicting path with -f' \
+ 'test ! -h path0 && test -d path0 &&
+ test ! -h path1 && test -d path1 &&
+ test ! -h path0/file0 && test -f path0/file0 &&
+ test ! -h path1/file1 && test -f path1/file1'
+
+test_done
diff --git a/t/t2002-checkout-cache-u.sh b/t/t2002-checkout-cache-u.sh
new file mode 100755
index 0000000..70361c8
--- /dev/null
+++ b/t/t2002-checkout-cache-u.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git checkout-index -u test.
+
+With -u flag, git checkout-index internally runs the equivalent of
+git update-index --refresh on the checked out entry.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'preparation' '
+echo frotz >path0 &&
+git update-index --add path0 &&
+t=$(git write-tree)'
+
+test_expect_success \
+'without -u, git checkout-index smudges stat information.' '
+rm -f path0 &&
+git read-tree $t &&
+git checkout-index -f -a &&
+test_must_fail git diff-files --exit-code'
+
+test_expect_success \
+'with -u, git checkout-index picks up stat information from new files.' '
+rm -f path0 &&
+git read-tree $t &&
+git checkout-index -u -f -a &&
+git diff-files --exit-code'
+
+test_done
diff --git a/t/t2003-checkout-cache-mkdir.sh b/t/t2003-checkout-cache-mkdir.sh
new file mode 100755
index 0000000..02a4fc5
--- /dev/null
+++ b/t/t2003-checkout-cache-mkdir.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git checkout-index --prefix test.
+
+This test makes sure that --prefix option works as advertised, and
+also verifies that such leading path may contain symlinks, unlike
+the GIT controlled paths.
+'
+
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'mkdir path1 &&
+ echo frotz >path0 &&
+ echo rezrov >path1/file1 &&
+ git update-index --add path0 path1/file1'
+
+test_expect_success SYMLINKS \
+ 'have symlink in place where dir is expected.' \
+ 'rm -fr path0 path1 &&
+ mkdir path2 &&
+ ln -s path2 path1 &&
+ git checkout-index -f -a &&
+ test ! -h path1 && test -d path1 &&
+ test -f path1/file1 && test ! -f path2/file1'
+
+test_expect_success \
+ 'use --prefix=path2/' \
+ 'rm -fr path0 path1 path2 &&
+ mkdir path2 &&
+ git checkout-index --prefix=path2/ -f -a &&
+ test -f path2/path0 &&
+ test -f path2/path1/file1 &&
+ test ! -f path0 &&
+ test ! -f path1/file1'
+
+test_expect_success \
+ 'use --prefix=tmp-' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ git checkout-index --prefix=tmp- -f -a &&
+ test -f tmp-path0 &&
+ test -f tmp-path1/file1 &&
+ test ! -f path0 &&
+ test ! -f path1/file1'
+
+test_expect_success \
+ 'use --prefix=tmp- but with a conflicting file and dir' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ echo nitfol >tmp-path1 &&
+ mkdir tmp-path0 &&
+ git checkout-index --prefix=tmp- -f -a &&
+ test -f tmp-path0 &&
+ test -f tmp-path1/file1 &&
+ test ! -f path0 &&
+ test ! -f path1/file1'
+
+# Linus fix #1
+test_expect_success SYMLINKS \
+ 'use --prefix=tmp/orary/ where tmp is a symlink' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ mkdir tmp1 tmp1/orary &&
+ ln -s tmp1 tmp &&
+ git checkout-index --prefix=tmp/orary/ -f -a &&
+ test -d tmp1/orary &&
+ test -f tmp1/orary/path0 &&
+ test -f tmp1/orary/path1/file1 &&
+ test -h tmp'
+
+# Linus fix #2
+test_expect_success SYMLINKS \
+ 'use --prefix=tmp/orary- where tmp is a symlink' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ mkdir tmp1 &&
+ ln -s tmp1 tmp &&
+ git checkout-index --prefix=tmp/orary- -f -a &&
+ test -f tmp1/orary-path0 &&
+ test -f tmp1/orary-path1/file1 &&
+ test -h tmp'
+
+# Linus fix #3
+test_expect_success SYMLINKS \
+ 'use --prefix=tmp- where tmp-path1 is a symlink' \
+ 'rm -fr path0 path1 path2 tmp* &&
+ mkdir tmp1 &&
+ ln -s tmp1 tmp-path1 &&
+ git checkout-index --prefix=tmp- -f -a &&
+ test -f tmp-path0 &&
+ test ! -h tmp-path1 &&
+ test -d tmp-path1 &&
+ test -f tmp-path1/file1'
+
+test_done
diff --git a/t/t2004-checkout-cache-temp.sh b/t/t2004-checkout-cache-temp.sh
new file mode 100755
index 0000000..36cca14
--- /dev/null
+++ b/t/t2004-checkout-cache-temp.sh
@@ -0,0 +1,212 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='git checkout-index --temp test.
+
+With --temp flag, git checkout-index writes to temporary merge files
+rather than the tracked path.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'preparation' '
+mkdir asubdir &&
+echo tree1path0 >path0 &&
+echo tree1path1 >path1 &&
+echo tree1path3 >path3 &&
+echo tree1path4 >path4 &&
+echo tree1asubdir/path5 >asubdir/path5 &&
+git update-index --add path0 path1 path3 path4 asubdir/path5 &&
+t1=$(git write-tree) &&
+rm -f path* .merge_* out .git/index &&
+echo tree2path0 >path0 &&
+echo tree2path1 >path1 &&
+echo tree2path2 >path2 &&
+echo tree2path4 >path4 &&
+git update-index --add path0 path1 path2 path4 &&
+t2=$(git write-tree) &&
+rm -f path* .merge_* out .git/index &&
+echo tree2path0 >path0 &&
+echo tree3path1 >path1 &&
+echo tree3path2 >path2 &&
+echo tree3path3 >path3 &&
+git update-index --add path0 path1 path2 path3 &&
+t3=$(git write-tree)'
+
+test_expect_success \
+'checkout one stage 0 to temporary file' '
+rm -f path* .merge_* out .git/index &&
+git read-tree $t1 &&
+git checkout-index --temp -- path1 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path1 &&
+p=$(cut "-d " -f1 out) &&
+test -f $p &&
+test $(cat $p) = tree1path1'
+
+test_expect_success \
+'checkout all stage 0 to temporary files' '
+rm -f path* .merge_* out .git/index &&
+git read-tree $t1 &&
+git checkout-index -a --temp >out &&
+test $(wc -l <out) = 5 &&
+for f in path0 path1 path3 path4 asubdir/path5
+do
+ test $(grep $f out | cut "-d " -f2) = $f &&
+ p=$(grep $f out | cut "-d " -f1) &&
+ test -f $p &&
+ test $(cat $p) = tree1$f
+done'
+
+test_expect_success \
+'prepare 3-way merge' '
+rm -f path* .merge_* out .git/index &&
+git read-tree -m $t1 $t2 $t3'
+
+test_expect_success \
+'checkout one stage 2 to temporary file' '
+rm -f path* .merge_* out &&
+git checkout-index --stage=2 --temp -- path1 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path1 &&
+p=$(cut "-d " -f1 out) &&
+test -f $p &&
+test $(cat $p) = tree2path1'
+
+test_expect_success \
+'checkout all stage 2 to temporary files' '
+rm -f path* .merge_* out &&
+git checkout-index --all --stage=2 --temp >out &&
+test $(wc -l <out) = 3 &&
+for f in path1 path2 path4
+do
+ test $(grep $f out | cut "-d " -f2) = $f &&
+ p=$(grep $f out | cut "-d " -f1) &&
+ test -f $p &&
+ test $(cat $p) = tree2$f
+done'
+
+test_expect_success \
+'checkout all stages/one file to nothing' '
+rm -f path* .merge_* out &&
+git checkout-index --stage=all --temp -- path0 >out &&
+test $(wc -l <out) = 0'
+
+test_expect_success \
+'checkout all stages/one file to temporary files' '
+rm -f path* .merge_* out &&
+git checkout-index --stage=all --temp -- path1 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path1 &&
+cut "-d " -f1 out | (read s1 s2 s3 &&
+test -f $s1 &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s1) = tree1path1 &&
+test $(cat $s2) = tree2path1 &&
+test $(cat $s3) = tree3path1)'
+
+test_expect_success \
+'checkout some stages/one file to temporary files' '
+rm -f path* .merge_* out &&
+git checkout-index --stage=all --temp -- path2 >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = path2 &&
+cut "-d " -f1 out | (read s1 s2 s3 &&
+test $s1 = . &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s2) = tree2path2 &&
+test $(cat $s3) = tree3path2)'
+
+test_expect_success \
+'checkout all stages/all files to temporary files' '
+rm -f path* .merge_* out &&
+git checkout-index -a --stage=all --temp >out &&
+test $(wc -l <out) = 5'
+
+test_expect_success \
+'-- path0: no entry' '
+test x$(grep path0 out | cut "-d " -f2) = x'
+
+test_expect_success \
+'-- path1: all 3 stages' '
+test $(grep path1 out | cut "-d " -f2) = path1 &&
+grep path1 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s1) = tree1path1 &&
+test $(cat $s2) = tree2path1 &&
+test $(cat $s3) = tree3path1)'
+
+test_expect_success \
+'-- path2: no stage 1, have stage 2 and 3' '
+test $(grep path2 out | cut "-d " -f2) = path2 &&
+grep path2 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test $s1 = . &&
+test -f $s2 &&
+test -f $s3 &&
+test $(cat $s2) = tree2path2 &&
+test $(cat $s3) = tree3path2)'
+
+test_expect_success \
+'-- path3: no stage 2, have stage 1 and 3' '
+test $(grep path3 out | cut "-d " -f2) = path3 &&
+grep path3 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test $s2 = . &&
+test -f $s3 &&
+test $(cat $s1) = tree1path3 &&
+test $(cat $s3) = tree3path3)'
+
+test_expect_success \
+'-- path4: no stage 3, have stage 1 and 3' '
+test $(grep path4 out | cut "-d " -f2) = path4 &&
+grep path4 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test -f $s2 &&
+test $s3 = . &&
+test $(cat $s1) = tree1path4 &&
+test $(cat $s2) = tree2path4)'
+
+test_expect_success \
+'-- asubdir/path5: no stage 2 and 3 have stage 1' '
+test $(grep asubdir/path5 out | cut "-d " -f2) = asubdir/path5 &&
+grep asubdir/path5 out | cut "-d " -f1 | (read s1 s2 s3 &&
+test -f $s1 &&
+test $s2 = . &&
+test $s3 = . &&
+test $(cat $s1) = tree1asubdir/path5)'
+
+test_expect_success \
+'checkout --temp within subdir' '
+(cd asubdir &&
+ git checkout-index -a --stage=all >out &&
+ test $(wc -l <out) = 1 &&
+ test $(grep path5 out | cut "-d " -f2) = path5 &&
+ grep path5 out | cut "-d " -f1 | (read s1 s2 s3 &&
+ test -f ../$s1 &&
+ test $s2 = . &&
+ test $s3 = . &&
+ test $(cat ../$s1) = tree1asubdir/path5)
+)'
+
+test_expect_success SYMLINKS \
+'checkout --temp symlink' '
+rm -f path* .merge_* out .git/index &&
+ln -s b a &&
+git update-index --add a &&
+t4=$(git write-tree) &&
+rm -f .git/index &&
+git read-tree $t4 &&
+git checkout-index --temp -a >out &&
+test $(wc -l <out) = 1 &&
+test $(cut "-d " -f2 out) = a &&
+p=$(cut "-d " -f1 out) &&
+test -f $p &&
+test $(cat $p) = b'
+
+test_done
diff --git a/t/t2005-checkout-index-symlinks.sh b/t/t2005-checkout-index-symlinks.sh
new file mode 100755
index 0000000..9fa5610
--- /dev/null
+++ b/t/t2005-checkout-index-symlinks.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Sixt
+#
+
+test_description='git checkout-index on filesystem w/o symlinks test.
+
+This tests that git checkout-index creates a symbolic link as a plain
+file if core.symlinks is false.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'preparation' '
+git config core.symlinks false &&
+l=$(printf file | git hash-object -t blob -w --stdin) &&
+echo "120000 $l symlink" | git update-index --index-info'
+
+test_expect_success \
+'the checked-out symlink must be a file' '
+git checkout-index symlink &&
+test -f symlink'
+
+test_expect_success \
+'the file must be the blob we added during the setup' '
+test "$(git hash-object -t blob symlink)" = $l'
+
+test_done
diff --git a/t/t2007-checkout-symlink.sh b/t/t2007-checkout-symlink.sh
new file mode 100755
index 0000000..05cc8fd
--- /dev/null
+++ b/t/t2007-checkout-symlink.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Junio C Hamano
+
+test_description='git checkout to switch between branches with symlink<->dir'
+
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all="symbolic links not supported - skipping tests"
+ test_done
+fi
+
+test_expect_success setup '
+
+ mkdir frotz &&
+ echo hello >frotz/filfre &&
+ git add frotz/filfre &&
+ test_tick &&
+ git commit -m "master has file frotz/filfre" &&
+
+ git branch side &&
+
+ echo goodbye >nitfol &&
+ git add nitfol
+ test_tick &&
+ git commit -m "master adds file nitfol" &&
+
+ git checkout side &&
+
+ git rm --cached frotz/filfre &&
+ mv frotz xyzzy &&
+ ln -s xyzzy frotz &&
+ git add xyzzy/filfre frotz &&
+ test_tick &&
+ git commit -m "side moves frotz/ to xyzzy/ and adds frotz->xyzzy/"
+
+'
+
+test_expect_success 'switch from symlink to dir' '
+
+ git checkout master
+
+'
+
+test_expect_success 'Remove temporary directories & switch to master' '
+ rm -fr frotz xyzzy nitfol &&
+ git checkout -f master
+'
+
+test_expect_success 'switch from dir to symlink' '
+
+ git checkout side
+
+'
+
+test_done
diff --git a/t/t2008-checkout-subdir.sh b/t/t2008-checkout-subdir.sh
new file mode 100755
index 0000000..3e098ab
--- /dev/null
+++ b/t/t2008-checkout-subdir.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 David Symonds
+
+test_description='git checkout from subdirectories'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ echo "base" > file0 &&
+ git add file0 &&
+ mkdir dir1 &&
+ echo "hello" > dir1/file1 &&
+ git add dir1/file1 &&
+ mkdir dir2 &&
+ echo "bonjour" > dir2/file2 &&
+ git add dir2/file2 &&
+ test_tick &&
+ git commit -m "populate tree"
+
+'
+
+test_expect_success 'remove and restore with relative path' '
+
+ (
+ cd dir1 &&
+ rm ../file0 &&
+ git checkout HEAD -- ../file0 &&
+ test "base" = "$(cat ../file0)" &&
+ rm ../dir2/file2 &&
+ git checkout HEAD -- ../dir2/file2 &&
+ test "bonjour" = "$(cat ../dir2/file2)" &&
+ rm ../file0 ./file1 &&
+ git checkout HEAD -- .. &&
+ test "base" = "$(cat ../file0)" &&
+ test "hello" = "$(cat file1)"
+ )
+
+'
+
+test_expect_success 'checkout with empty prefix' '
+
+ rm file0 &&
+ git checkout HEAD -- file0 &&
+ test "base" = "$(cat file0)"
+
+'
+
+test_expect_success 'checkout with simple prefix' '
+
+ rm dir1/file1 &&
+ git checkout HEAD -- dir1 &&
+ test "hello" = "$(cat dir1/file1)" &&
+ rm dir1/file1 &&
+ git checkout HEAD -- dir1/file1 &&
+ test "hello" = "$(cat dir1/file1)"
+
+'
+
+# This is not expected to work as ls-files was not designed
+# to deal with such. Enable it when ls-files is updated.
+: test_expect_success 'checkout with complex relative path' '
+
+ rm file1 &&
+ git checkout HEAD -- ../dir1/../dir1/file1 && test -f ./file1
+
+'
+
+test_expect_success 'relative path outside tree should fail' \
+ 'test_must_fail git checkout HEAD -- ../../Makefile'
+
+test_expect_success 'incorrect relative path to file should fail (1)' \
+ 'test_must_fail git checkout HEAD -- ../file0'
+
+test_expect_success 'incorrect relative path should fail (2)' \
+ '( cd dir1 && test_must_fail git checkout HEAD -- ./file0 )'
+
+test_expect_success 'incorrect relative path should fail (3)' \
+ '( cd dir1 && test_must_fail git checkout HEAD -- ../../file0 )'
+
+test_done
diff --git a/t/t2009-checkout-statinfo.sh b/t/t2009-checkout-statinfo.sh
new file mode 100755
index 0000000..f3c2152
--- /dev/null
+++ b/t/t2009-checkout-statinfo.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='checkout should leave clean stat info'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ echo hello >world &&
+ git update-index --add world &&
+ git commit -m initial &&
+ git branch side &&
+ echo goodbye >world &&
+ git update-index --add world &&
+ git commit -m second
+
+'
+
+test_expect_success 'branch switching' '
+
+ git reset --hard &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout master &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout side &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout master &&
+ test "$(git diff-files --raw)" = ""
+
+'
+
+test_expect_success 'path checkout' '
+
+ git reset --hard &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout master world &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout side world &&
+ test "$(git diff-files --raw)" = "" &&
+
+ git checkout master world &&
+ test "$(git diff-files --raw)" = ""
+
+'
+
+test_done
+
diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh
new file mode 100755
index 0000000..7cc0a35
--- /dev/null
+++ b/t/t2010-checkout-ambiguous.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='checkout and pathspecs/refspecs ambiguities'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo hello >world &&
+ echo hello >all &&
+ git add all world &&
+ git commit -m initial &&
+ git branch world
+'
+
+test_expect_success 'reference must be a tree' '
+ test_must_fail git checkout $(git hash-object ./all) --
+'
+
+test_expect_success 'branch switching' '
+ test "refs/heads/master" = "$(git symbolic-ref HEAD)" &&
+ git checkout world -- &&
+ test "refs/heads/world" = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success 'checkout world from the index' '
+ echo bye > world &&
+ git checkout -- world &&
+ git diff --exit-code --quiet
+'
+
+test_expect_success 'non ambiguous call' '
+ git checkout all
+'
+
+test_expect_success 'allow the most common case' '
+ git checkout world &&
+ test "refs/heads/world" = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success 'check ambiguity' '
+ test_must_fail git checkout world all
+'
+
+test_expect_success 'disambiguate checking out from a tree-ish' '
+ echo bye > world &&
+ git checkout world -- world &&
+ git diff --exit-code --quiet
+'
+
+test_done
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
new file mode 100755
index 0000000..15ebdc2
--- /dev/null
+++ b/t/t2011-checkout-invalid-head.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description='checkout switching away from an invalid branch'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo hello >world &&
+ git add world &&
+ git commit -m initial
+'
+
+test_expect_success 'checkout should not start branch from a tree' '
+ test_must_fail git checkout -b newbranch master^{tree}
+'
+
+test_expect_success 'checkout master from invalid HEAD' '
+ echo 0000000000000000000000000000000000000000 >.git/HEAD &&
+ git checkout master --
+'
+
+test_done
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
new file mode 100755
index 0000000..b44de9d
--- /dev/null
+++ b/t/t2012-checkout-last.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+test_description='checkout can switch to last branch and merge base'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo hello >world &&
+ git add world &&
+ git commit -m initial &&
+ git branch other &&
+ echo "hello again" >>world &&
+ git add world &&
+ git commit -m second
+'
+
+test_expect_success '"checkout -" does not work initially' '
+ test_must_fail git checkout -
+'
+
+test_expect_success 'first branch switch' '
+ git checkout other
+'
+
+test_expect_success '"checkout -" switches back' '
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" switches forth' '
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+'
+
+test_expect_success 'detach HEAD' '
+ git checkout $(git rev-parse HEAD)
+'
+
+test_expect_success '"checkout -" attaches again' '
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+'
+
+test_expect_success '"checkout -" detaches again' '
+ git checkout - &&
+ test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" &&
+ test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'more switches' '
+ for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
+ do
+ git checkout -b branch$i
+ done
+'
+
+more_switches () {
+ for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
+ do
+ git checkout branch$i
+ done
+}
+
+test_expect_success 'switch to the last' '
+ more_switches &&
+ git checkout @{-1} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch2"
+'
+
+test_expect_success 'switch to second from the last' '
+ more_switches &&
+ git checkout @{-2} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch3"
+'
+
+test_expect_success 'switch to third from the last' '
+ more_switches &&
+ git checkout @{-3} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch4"
+'
+
+test_expect_success 'switch to fourth from the last' '
+ more_switches &&
+ git checkout @{-4} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch5"
+'
+
+test_expect_success 'switch to twelfth from the last' '
+ more_switches &&
+ git checkout @{-12} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13"
+'
+
+test_expect_success 'merge base test setup' '
+ git checkout -b another other &&
+ echo "hello again" >>world &&
+ git add world &&
+ git commit -m third
+'
+
+test_expect_success 'another...master' '
+ git checkout another &&
+ git checkout another...master &&
+ test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
+test_expect_success '...master' '
+ git checkout another &&
+ git checkout ...master &&
+ test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
+test_expect_success 'master...' '
+ git checkout another &&
+ git checkout master... &&
+ test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
+test_done
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
new file mode 100755
index 0000000..fda3f0a
--- /dev/null
+++ b/t/t2013-checkout-submodule.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='checkout can handle submodules'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir submodule &&
+ (cd submodule &&
+ git init &&
+ test_commit first) &&
+ git add submodule &&
+ test_tick &&
+ git commit -m superproject &&
+ (cd submodule &&
+ test_commit second) &&
+ git add submodule &&
+ test_tick &&
+ git commit -m updated.superproject
+'
+
+test_expect_success '"reset <submodule>" updates the index' '
+ git update-index --refresh &&
+ git diff-files --quiet &&
+ git diff-index --quiet --cached HEAD &&
+ test_must_fail git reset HEAD^ submodule &&
+ test_must_fail git diff-files --quiet &&
+ git reset submodule &&
+ git diff-files --quiet
+'
+
+test_expect_success '"checkout <submodule>" updates the index only' '
+ git update-index --refresh &&
+ git diff-files --quiet &&
+ git diff-index --quiet --cached HEAD &&
+ git checkout HEAD^ submodule &&
+ test_must_fail git diff-files --quiet &&
+ git checkout HEAD submodule &&
+ git diff-files --quiet
+'
+
+test_done
diff --git a/t/t2014-switch.sh b/t/t2014-switch.sh
new file mode 100755
index 0000000..ccfb147
--- /dev/null
+++ b/t/t2014-switch.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+test_description='Peter MacMillan'
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo Hello >file &&
+ git add file &&
+ test_tick &&
+ git commit -m V1 &&
+ echo Hello world >file &&
+ git add file &&
+ git checkout -b other
+'
+
+test_expect_success 'check all changes are staged' '
+ git diff --exit-code
+'
+
+test_expect_success 'second commit' '
+ git commit -m V2
+'
+
+test_expect_success 'check' '
+ git diff --cached --exit-code
+'
+
+test_done
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
new file mode 100755
index 0000000..c551d39
--- /dev/null
+++ b/t/t2015-checkout-unborn.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='checkout from unborn branch protects contents'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir parent &&
+ (cd parent &&
+ git init &&
+ echo content >file &&
+ git add file &&
+ git commit -m base
+ ) &&
+ git fetch parent master:origin
+'
+
+test_expect_success 'checkout from unborn preserves untracked files' '
+ echo precious >expect &&
+ echo precious >file &&
+ test_must_fail git checkout -b new origin &&
+ test_cmp expect file
+'
+
+test_expect_success 'checkout from unborn preserves index contents' '
+ echo precious >expect &&
+ echo precious >file &&
+ git add file &&
+ test_must_fail git checkout -b new origin &&
+ test_cmp expect file &&
+ git show :file >file &&
+ test_cmp expect file
+'
+
+test_expect_success 'checkout from unborn merges identical index contents' '
+ echo content >file &&
+ git add file &&
+ git checkout -b new origin
+'
+
+test_done
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
new file mode 100755
index 0000000..2144184
--- /dev/null
+++ b/t/t2016-checkout-patch.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+
+test_description='git checkout --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+ mkdir dir &&
+ echo parent > dir/foo &&
+ echo dummy > bar &&
+ git add bar dir/foo &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+ set_and_save_state dir/foo work head &&
+ (echo n; echo n) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_saved_state dir/foo
+'
+
+test_expect_success 'git checkout -p' '
+ (echo n; echo y) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p with staged changes' '
+ set_state dir/foo work index
+ (echo n; echo y) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo index index
+'
+
+test_expect_success 'git checkout -p HEAD with NO staged changes: abort' '
+ set_and_save_state dir/foo work head &&
+ (echo n; echo y; echo n) | git checkout -p HEAD &&
+ verify_saved_state bar &&
+ verify_saved_state dir/foo
+'
+
+test_expect_success 'git checkout -p HEAD with NO staged changes: apply' '
+ (echo n; echo y; echo y) | git checkout -p HEAD &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p HEAD with change already staged' '
+ set_state dir/foo index index
+ # the third n is to get out in case it mistakenly does not apply
+ (echo n; echo y; echo n) | git checkout -p HEAD &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p HEAD^' '
+ # the third n is to get out in case it mistakenly does not apply
+ (echo n; echo y; echo n) | git checkout -p HEAD^ &&
+ verify_saved_state bar &&
+ verify_state dir/foo parent parent
+'
+
+test_expect_success 'git checkout -p handles deletion' '
+ set_state dir/foo work index &&
+ rm dir/foo &&
+ (echo n; echo y) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo index index
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success 'path limiting works: dir' '
+ set_state dir/foo work head &&
+ (echo y; echo n) | git checkout -p dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'path limiting works: -- dir' '
+ set_state dir/foo work head &&
+ (echo y; echo n) | git checkout -p -- dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'path limiting works: HEAD^ -- dir' '
+ # the third n is to get out in case it mistakenly does not apply
+ (echo y; echo n; echo n) | git checkout -p HEAD^ -- dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo parent parent
+'
+
+test_expect_success 'path limiting works: foo inside dir' '
+ set_state dir/foo work head &&
+ # the third n is to get out in case it mistakenly does not apply
+ (echo y; echo n; echo n) | (cd dir && git checkout -p foo) &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+test_done
diff --git a/t/t2017-checkout-orphan.sh b/t/t2017-checkout-orphan.sh
new file mode 100755
index 0000000..2d2f63f
--- /dev/null
+++ b/t/t2017-checkout-orphan.sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Erick Mattos
+#
+
+test_description='git checkout --orphan
+
+Main Tests for --orphan functionality.'
+
+. ./test-lib.sh
+
+TEST_FILE=foo
+
+test_expect_success 'Setup' '
+ echo "Initial" >"$TEST_FILE" &&
+ git add "$TEST_FILE" &&
+ git commit -m "First Commit"
+ test_tick &&
+ echo "State 1" >>"$TEST_FILE" &&
+ git add "$TEST_FILE" &&
+ test_tick &&
+ git commit -m "Second Commit"
+'
+
+test_expect_success '--orphan creates a new orphan branch from HEAD' '
+ git checkout --orphan alpha &&
+ test_must_fail git rev-parse --verify HEAD &&
+ test "refs/heads/alpha" = "$(git symbolic-ref HEAD)" &&
+ test_tick &&
+ git commit -m "Third Commit" &&
+ test_must_fail git rev-parse --verify HEAD^ &&
+ git diff-tree --quiet master alpha
+'
+
+test_expect_success '--orphan creates a new orphan branch from <start_point>' '
+ git checkout master &&
+ git checkout --orphan beta master^ &&
+ test_must_fail git rev-parse --verify HEAD &&
+ test "refs/heads/beta" = "$(git symbolic-ref HEAD)" &&
+ test_tick &&
+ git commit -m "Fourth Commit" &&
+ test_must_fail git rev-parse --verify HEAD^ &&
+ git diff-tree --quiet master^ beta
+'
+
+test_expect_success '--orphan must be rejected with -b' '
+ git checkout master &&
+ test_must_fail git checkout --orphan new -b newer &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan must be rejected with -t' '
+ git checkout master &&
+ test_must_fail git checkout --orphan new -t master &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan ignores branch.autosetupmerge' '
+ git checkout master &&
+ git config branch.autosetupmerge always &&
+ git checkout --orphan gamma &&
+ test -z "$(git config branch.gamma.merge)" &&
+ test refs/heads/gamma = "$(git symbolic-ref HEAD)" &&
+ test_must_fail git rev-parse --verify HEAD^
+'
+
+test_expect_success '--orphan makes reflog by default' '
+ git checkout master &&
+ git config --unset core.logAllRefUpdates &&
+ git checkout --orphan delta &&
+ test_must_fail git rev-parse --verify delta@{0} &&
+ git commit -m Delta &&
+ git rev-parse --verify delta@{0}
+'
+
+test_expect_success '--orphan does not make reflog when core.logAllRefUpdates = false' '
+ git checkout master &&
+ git config core.logAllRefUpdates false &&
+ git checkout --orphan epsilon &&
+ test_must_fail git rev-parse --verify epsilon@{0} &&
+ git commit -m Epsilon &&
+ test_must_fail git rev-parse --verify epsilon@{0}
+'
+
+test_expect_success '--orphan with -l makes reflog when core.logAllRefUpdates = false' '
+ git checkout master &&
+ git checkout -l --orphan zeta &&
+ test_must_fail git rev-parse --verify zeta@{0} &&
+ git commit -m Zeta &&
+ git rev-parse --verify zeta@{0}
+'
+
+test_expect_success 'giving up --orphan not committed when -l and core.logAllRefUpdates = false deletes reflog' '
+ git checkout master &&
+ git checkout -l --orphan eta &&
+ test_must_fail git rev-parse --verify eta@{0} &&
+ git checkout master &&
+ test_must_fail git rev-parse --verify eta@{0}
+'
+
+test_expect_success '--orphan is rejected with an existing name' '
+ git checkout master &&
+ test_must_fail git checkout --orphan master &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan refuses to switch if a merge is needed' '
+ git checkout master &&
+ git reset --hard &&
+ echo local >>"$TEST_FILE" &&
+ cat "$TEST_FILE" >"$TEST_FILE.saved" &&
+ test_must_fail git checkout --orphan new master^ &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)" &&
+ test_cmp "$TEST_FILE" "$TEST_FILE.saved" &&
+ git diff-index --quiet --cached HEAD &&
+ git reset --hard
+'
+
+test_done
diff --git a/t/t2030-unresolve-info.sh b/t/t2030-unresolve-info.sh
new file mode 100755
index 0000000..cb7effe
--- /dev/null
+++ b/t/t2030-unresolve-info.sh
@@ -0,0 +1,170 @@
+#!/bin/sh
+
+test_description='undoing resolution'
+
+. ./test-lib.sh
+
+check_resolve_undo () {
+ msg=$1
+ shift
+ while case $# in
+ 0) break ;;
+ 1|2|3) die "Bug in check-resolve-undo test" ;;
+ esac
+ do
+ path=$1
+ shift
+ for stage in 1 2 3
+ do
+ sha1=$1
+ shift
+ case "$sha1" in
+ '') continue ;;
+ esac
+ sha1=$(git rev-parse --verify "$sha1")
+ printf "100644 %s %s\t%s\n" $sha1 $stage $path
+ done
+ done >"$msg.expect" &&
+ git ls-files --resolve-undo >"$msg.actual" &&
+ test_cmp "$msg.expect" "$msg.actual"
+}
+
+prime_resolve_undo () {
+ git reset --hard &&
+ git checkout second^0 &&
+ test_tick &&
+ test_must_fail git merge third^0 &&
+ echo merge does not leave anything &&
+ check_resolve_undo empty &&
+ echo different >fi/le &&
+ git add fi/le &&
+ echo resolving records &&
+ check_resolve_undo recorded fi/le initial:fi/le second:fi/le third:fi/le
+}
+
+test_expect_success setup '
+ mkdir fi &&
+ test_commit initial fi/le first &&
+ git branch side &&
+ git branch another &&
+ test_commit second fi/le second &&
+ git checkout side &&
+ test_commit third fi/le third &&
+ git checkout another &&
+ test_commit fourth fi/le fourth &&
+ git checkout master
+'
+
+test_expect_success 'add records switch clears' '
+ prime_resolve_undo &&
+ test_tick &&
+ git commit -m merged &&
+ echo committing keeps &&
+ check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+ git checkout second^0 &&
+ echo switching clears &&
+ check_resolve_undo cleared
+'
+
+test_expect_success 'rm records reset clears' '
+ prime_resolve_undo &&
+ test_tick &&
+ git commit -m merged &&
+ echo committing keeps &&
+ check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+
+ echo merge clears upfront &&
+ test_must_fail git merge fourth^0 &&
+ check_resolve_undo nuked &&
+
+ git rm -f fi/le &&
+ echo resolving records &&
+ check_resolve_undo recorded fi/le initial:fi/le HEAD:fi/le fourth:fi/le &&
+
+ git reset --hard &&
+ echo resetting discards &&
+ check_resolve_undo discarded
+'
+
+test_expect_success 'plumbing clears' '
+ prime_resolve_undo &&
+ test_tick &&
+ git commit -m merged &&
+ echo committing keeps &&
+ check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+
+ echo plumbing clear &&
+ git update-index --clear-resolve-undo &&
+ check_resolve_undo cleared
+'
+
+test_expect_success 'add records checkout -m undoes' '
+ prime_resolve_undo &&
+ git diff HEAD &&
+ git checkout --conflict=merge fi/le &&
+ echo checkout used the record and removed it &&
+ check_resolve_undo removed &&
+ echo the index and the work tree is unmerged again &&
+ git diff >actual &&
+ grep "^++<<<<<<<" actual
+'
+
+test_expect_success 'unmerge with plumbing' '
+ prime_resolve_undo &&
+ git update-index --unresolve fi/le &&
+ git ls-files -u >actual &&
+ test $(wc -l <actual) = 3
+'
+
+test_expect_success 'rerere and rerere forget' '
+ mkdir .git/rr-cache &&
+ prime_resolve_undo &&
+ echo record the resolution &&
+ git rerere &&
+ rerere_id=$(cd .git/rr-cache && echo */postimage) &&
+ rerere_id=${rerere_id%/postimage} &&
+ test -f .git/rr-cache/$rerere_id/postimage &&
+ git checkout -m fi/le &&
+ echo resurrect the conflict &&
+ grep "^=======" fi/le &&
+ echo reresolve the conflict &&
+ git rerere &&
+ test "z$(cat fi/le)" = zdifferent &&
+ echo register the resolution again &&
+ git add fi/le &&
+ check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+ test -z "$(git ls-files -u)" &&
+ git rerere forget fi/le &&
+ ! test -f .git/rr-cache/$rerere_id/postimage &&
+ tr "\0" "\n" <.git/MERGE_RR >actual &&
+ echo "$rerere_id fi/le" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rerere and rerere forget (subdirectory)' '
+ rm -fr .git/rr-cache &&
+ mkdir .git/rr-cache &&
+ prime_resolve_undo &&
+ echo record the resolution &&
+ (cd fi && git rerere) &&
+ rerere_id=$(cd .git/rr-cache && echo */postimage) &&
+ rerere_id=${rerere_id%/postimage} &&
+ test -f .git/rr-cache/$rerere_id/postimage &&
+ (cd fi && git checkout -m le) &&
+ echo resurrect the conflict &&
+ grep "^=======" fi/le &&
+ echo reresolve the conflict &&
+ (cd fi && git rerere) &&
+ test "z$(cat fi/le)" = zdifferent &&
+ echo register the resolution again &&
+ (cd fi && git add le) &&
+ check_resolve_undo kept fi/le initial:fi/le second:fi/le third:fi/le &&
+ test -z "$(git ls-files -u)" &&
+ (cd fi && git rerere forget le) &&
+ ! test -f .git/rr-cache/$rerere_id/postimage &&
+ tr "\0" "\n" <.git/MERGE_RR >actual &&
+ echo "$rerere_id fi/le" >expect &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2050-git-dir-relative.sh b/t/t2050-git-dir-relative.sh
new file mode 100755
index 0000000..b7131d8
--- /dev/null
+++ b/t/t2050-git-dir-relative.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='check problems with relative GIT_DIR
+
+This test creates a working tree state with a file and subdir:
+
+ top (committed several times)
+ subdir (a subdirectory)
+
+It creates a commit-hook and tests it, then moves .git
+into the subdir while keeping the worktree location,
+and tries commits from the top and the subdir, checking
+that the commit-hook still gets called.'
+
+. ./test-lib.sh
+
+COMMIT_FILE="$(pwd)/output"
+export COMMIT_FILE
+
+test_expect_success 'Setting up post-commit hook' '
+mkdir -p .git/hooks &&
+echo >.git/hooks/post-commit "#!/bin/sh
+touch \"\${COMMIT_FILE}\"
+echo Post commit hook was called." &&
+chmod +x .git/hooks/post-commit'
+
+test_expect_success 'post-commit hook used ordinarily' '
+echo initial >top &&
+git add top
+git commit -m initial &&
+test -r "${COMMIT_FILE}"
+'
+
+rm -rf "${COMMIT_FILE}"
+mkdir subdir
+mv .git subdir
+
+test_expect_success 'post-commit-hook created and used from top dir' '
+echo changed >top &&
+git --git-dir subdir/.git add top &&
+git --git-dir subdir/.git commit -m topcommit &&
+test -r "${COMMIT_FILE}"
+'
+
+rm -rf "${COMMIT_FILE}"
+
+test_expect_success 'post-commit-hook from sub dir' '
+echo changed again >top
+cd subdir &&
+git --git-dir .git --work-tree .. add ../top &&
+git --git-dir .git --work-tree .. commit -m subcommit &&
+test -r "${COMMIT_FILE}"
+'
+
+test_done
diff --git a/t/t2100-update-cache-badpath.sh b/t/t2100-update-cache-badpath.sh
new file mode 100755
index 0000000..2df3fdd
--- /dev/null
+++ b/t/t2100-update-cache-badpath.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git update-index nonsense-path test.
+
+This test creates the following structure in the cache:
+
+ path0 - a file
+ path1 - a symlink
+ path2/file2 - a file in a directory
+ path3/file3 - a file in a directory
+
+and tries to git update-index --add the following:
+
+ path0/file0 - a file in a directory
+ path1/file1 - a file in a directory
+ path2 - a file
+ path3 - a symlink
+
+All of the attempts should fail.
+'
+
+. ./test-lib.sh
+
+mkdir path2 path3
+date >path0
+if test_have_prereq SYMLINKS
+then
+ ln -s xyzzy path1
+else
+ date > path1
+fi
+date >path2/file2
+date >path3/file3
+
+test_expect_success \
+ 'git update-index --add to add various paths.' \
+ 'git update-index --add -- path0 path1 path2/file2 path3/file3'
+
+rm -fr path?
+
+mkdir path0 path1
+date >path2
+if test_have_prereq SYMLINKS
+then
+ ln -s frotz path3
+else
+ date > path3
+fi
+date >path0/file0
+date >path1/file1
+
+for p in path0/file0 path1/file1 path2 path3
+do
+ test_expect_success \
+ "git update-index to add conflicting path $p should fail." \
+ "test_must_fail git update-index --add -- $p"
+done
+test_done
diff --git a/t/t2101-update-index-reupdate.sh b/t/t2101-update-index-reupdate.sh
new file mode 100755
index 0000000..648184f
--- /dev/null
+++ b/t/t2101-update-index-reupdate.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git update-index --again test.
+'
+
+. ./test-lib.sh
+
+cat > expected <<\EOF
+100644 3b18e512dba79e4c8300dd08aeb37f8e728b8dad 0 file1
+100644 9db8893856a8a02eaa73470054b7c1c5a7c82e47 0 file2
+EOF
+test_expect_success 'update-index --add' \
+ 'echo hello world >file1 &&
+ echo goodbye people >file2 &&
+ git update-index --add file1 file2 &&
+ git ls-files -s >current &&
+ cmp current expected'
+
+test_expect_success 'update-index --again' \
+ 'rm -f file1 &&
+ echo hello everybody >file2 &&
+ if git update-index --again
+ then
+ echo should have refused to remove file1
+ exit 1
+ else
+ echo happy - failed as expected
+ fi &&
+ git ls-files -s >current &&
+ cmp current expected'
+
+cat > expected <<\EOF
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF
+test_expect_success 'update-index --remove --again' \
+ 'git update-index --remove --again &&
+ git ls-files -s >current &&
+ cmp current expected'
+
+test_expect_success 'first commit' 'git commit -m initial'
+
+cat > expected <<\EOF
+100644 53ab446c3f4e42ce9bb728a0ccb283a101be4979 0 dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF
+test_expect_success 'update-index again' \
+ 'mkdir -p dir1 &&
+ echo hello world >dir1/file3 &&
+ echo goodbye people >file2 &&
+ git update-index --add file2 dir1/file3 &&
+ echo hello everybody >file2
+ echo happy >dir1/file3 &&
+ git update-index --again &&
+ git ls-files -s >current &&
+ cmp current expected'
+
+cat > expected <<\EOF
+100644 d7fb3f695f06c759dbf3ab00046e7cc2da22d10f 0 dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF
+test_expect_success 'update-index --update from subdir' \
+ 'echo not so happy >file2 &&
+ cd dir1 &&
+ cat ../file2 >file3 &&
+ git update-index --again &&
+ cd .. &&
+ git ls-files -s >current &&
+ cmp current expected'
+
+cat > expected <<\EOF
+100644 594fb5bb1759d90998e2bf2a38261ae8e243c760 0 dir1/file3
+100644 0f1ae1422c2bf43f117d3dbd715c988a9ed2103f 0 file2
+EOF
+test_expect_success 'update-index --update with pathspec' \
+ 'echo very happy >file2 &&
+ cat file2 >dir1/file3 &&
+ git update-index --again dir1/ &&
+ git ls-files -s >current &&
+ cmp current expected'
+
+test_done
diff --git a/t/t2102-update-index-symlinks.sh b/t/t2102-update-index-symlinks.sh
new file mode 100755
index 0000000..4d0d0a3
--- /dev/null
+++ b/t/t2102-update-index-symlinks.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Sixt
+#
+
+test_description='git update-index on filesystem w/o symlinks test.
+
+This tests that git update-index keeps the symbolic link property
+even if a plain file is in the working tree if core.symlinks is false.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'preparation' '
+git config core.symlinks false &&
+l=$(printf file | git hash-object -t blob -w --stdin) &&
+echo "120000 $l symlink" | git update-index --index-info'
+
+test_expect_success \
+'modify the symbolic link' '
+printf new-file > symlink &&
+git update-index symlink'
+
+test_expect_success \
+'the index entry must still be a symbolic link' '
+case "`git ls-files --stage --cached symlink`" in
+120000" "*symlink) echo pass;;
+*) echo fail; git ls-files --stage --cached symlink; (exit 1);;
+esac'
+
+test_done
diff --git a/t/t2103-update-index-ignore-missing.sh b/t/t2103-update-index-ignore-missing.sh
new file mode 100755
index 0000000..332694e
--- /dev/null
+++ b/t/t2103-update-index-ignore-missing.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+test_description='update-index with options'
+
+. ./test-lib.sh
+
+test_expect_success basics '
+ >one &&
+ >two &&
+ >three &&
+
+ # need --add when adding
+ test_must_fail git update-index one &&
+ test -z "$(git ls-files)" &&
+ git update-index --add one &&
+ test zone = "z$(git ls-files)" &&
+
+ # update-index is atomic
+ echo 1 >one &&
+ test_must_fail git update-index one two &&
+ echo "M one" >expect &&
+ git diff-files --name-status >actual &&
+ test_cmp expect actual &&
+
+ git update-index --add one two three &&
+ for i in one three two; do echo $i; done >expect &&
+ git ls-files >actual &&
+ test_cmp expect actual &&
+
+ test_tick &&
+ (
+ test_create_repo xyzzy &&
+ cd xyzzy &&
+ >file &&
+ git add file
+ git commit -m "sub initial"
+ ) &&
+ git add xyzzy &&
+
+ test_tick &&
+ git commit -m initial &&
+ git tag initial
+'
+
+test_expect_success '--ignore-missing --refresh' '
+ git reset --hard initial &&
+ echo 2 >one &&
+ test_must_fail git update-index --refresh &&
+ echo 1 >one &&
+ git update-index --refresh &&
+ rm -f two &&
+ test_must_fail git update-index --refresh &&
+ git update-index --ignore-missing --refresh
+
+'
+
+test_expect_success '--unmerged --refresh' '
+ git reset --hard initial &&
+ info=$(git ls-files -s one | sed -e "s/ 0 / 1 /") &&
+ git rm --cached one &&
+ echo "$info" | git update-index --index-info &&
+ test_must_fail git update-index --refresh &&
+ git update-index --unmerged --refresh &&
+ echo 2 >two &&
+ test_must_fail git update-index --unmerged --refresh >actual &&
+ grep two actual &&
+ ! grep one actual &&
+ ! grep three actual
+'
+
+test_expect_success '--ignore-submodules --refresh (1)' '
+ git reset --hard initial &&
+ rm -f two &&
+ test_must_fail git update-index --ignore-submodules --refresh
+'
+
+test_expect_success '--ignore-submodules --refresh (2)' '
+ git reset --hard initial &&
+ test_tick &&
+ (
+ cd xyzzy &&
+ git commit -m "sub second" --allow-empty
+ ) &&
+ test_must_fail git update-index --refresh &&
+ test_must_fail git update-index --ignore-missing --refresh &&
+ git update-index --ignore-submodules --refresh
+'
+
+test_done
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
new file mode 100755
index 0000000..1d0879b
--- /dev/null
+++ b/t/t2104-update-index-skip-worktree.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nguyá»…n Thái Ngá»c Duy
+#
+
+test_description='skip-worktree bit test'
+
+. ./test-lib.sh
+
+cat >expect.full <<EOF
+H 1
+H 2
+H sub/1
+H sub/2
+EOF
+
+cat >expect.skip <<EOF
+S 1
+H 2
+S sub/1
+H sub/2
+EOF
+
+test_expect_success 'setup' '
+ mkdir sub &&
+ touch ./1 ./2 sub/1 sub/2 &&
+ git add 1 2 sub/1 sub/2 &&
+ git ls-files -t | test_cmp expect.full -
+'
+
+test_expect_success 'index is at version 2' '
+ test "$(test-index-version < .git/index)" = 2
+'
+
+test_expect_success 'update-index --skip-worktree' '
+ git update-index --skip-worktree 1 sub/1 &&
+ git ls-files -t | test_cmp expect.skip -
+'
+
+test_expect_success 'index is at version 3 after having some skip-worktree entries' '
+ test "$(test-index-version < .git/index)" = 3
+'
+
+test_expect_success 'ls-files -t' '
+ git ls-files -t | test_cmp expect.skip -
+'
+
+test_expect_success 'update-index --no-skip-worktree' '
+ git update-index --no-skip-worktree 1 sub/1 &&
+ git ls-files -t | test_cmp expect.full -
+'
+
+test_expect_success 'index version is back to 2 when there is no skip-worktree entry' '
+ test "$(test-index-version < .git/index)" = 2
+'
+
+test_done
diff --git a/t/t2105-update-index-gitfile.sh b/t/t2105-update-index-gitfile.sh
new file mode 100755
index 0000000..641607d
--- /dev/null
+++ b/t/t2105-update-index-gitfile.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Brad King
+#
+
+test_description='git update-index for gitlink to .git file.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'submodule with absolute .git file' '
+ mkdir sub1 &&
+ (cd sub1 &&
+ git init &&
+ REAL="$(pwd)/.real" &&
+ mv .git "$REAL"
+ echo "gitdir: $REAL" >.git &&
+ test_commit first)
+'
+
+test_expect_success 'add gitlink to absolute .git file' '
+ git update-index --add -- sub1
+'
+
+test_expect_success 'submodule with relative .git file' '
+ mkdir sub2 &&
+ (cd sub2 &&
+ git init &&
+ mv .git .real &&
+ echo "gitdir: .real" >.git &&
+ test_commit first)
+'
+
+test_expect_success 'add gitlink to relative .git file' '
+ git update-index --add -- sub2
+'
+
+test_done
diff --git a/t/t2106-update-index-assume-unchanged.sh b/t/t2106-update-index-assume-unchanged.sh
new file mode 100755
index 0000000..99d858c
--- /dev/null
+++ b/t/t2106-update-index-assume-unchanged.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='git update-index --assume-unchanged test.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' \
+ ': >file &&
+ git add file &&
+ git commit -m initial &&
+ git branch other &&
+ echo upstream >file &&
+ git add file &&
+ git commit -m upstream'
+
+test_expect_success 'do not switch branches with dirty file' \
+ 'git reset --hard &&
+ git checkout other &&
+ echo dirt >file &&
+ git update-index --assume-unchanged file &&
+ test_must_fail git checkout master'
+
+test_done
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
new file mode 100755
index 0000000..2ad2819
--- /dev/null
+++ b/t/t2200-add-update.sh
@@ -0,0 +1,184 @@
+#!/bin/sh
+
+test_description='git add -u
+
+This test creates a working tree state with three files:
+
+ top (previously committed, modified)
+ dir/sub (previously committed, modified)
+ dir/other (untracked)
+
+and issues a git add -u with path limiting on "dir" to add
+only the updates to dir/sub.
+
+Also tested are "git add -u" without limiting, and "git add -u"
+without contents changes, and other conditions'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo initial >check &&
+ echo initial >top &&
+ echo initial >foo &&
+ mkdir dir1 dir2 &&
+ echo initial >dir1/sub1 &&
+ echo initial >dir1/sub2 &&
+ echo initial >dir2/sub3 &&
+ git add check dir1 dir2 top foo &&
+ test_tick
+ git commit -m initial &&
+
+ echo changed >check &&
+ echo changed >top &&
+ echo changed >dir2/sub3 &&
+ rm -f dir1/sub1 &&
+ echo other >dir2/other
+'
+
+test_expect_success update '
+ git add -u dir1 dir2
+'
+
+test_expect_success 'update noticed a removal' '
+ test "$(git ls-files dir1/sub1)" = ""
+'
+
+test_expect_success 'update touched correct path' '
+ test "$(git diff-files --name-status dir2/sub3)" = ""
+'
+
+test_expect_success 'update did not touch other tracked files' '
+ test "$(git diff-files --name-status check)" = "M check" &&
+ test "$(git diff-files --name-status top)" = "M top"
+'
+
+test_expect_success 'update did not touch untracked files' '
+ test "$(git ls-files dir2/other)" = ""
+'
+
+test_expect_success 'cache tree has not been corrupted' '
+
+ git ls-files -s |
+ sed -e "s/ 0 / /" >expect &&
+ git ls-tree -r $(git write-tree) |
+ sed -e "s/ blob / /" >current &&
+ test_cmp expect current
+
+'
+
+test_expect_success 'update from a subdirectory' '
+ (
+ cd dir1 &&
+ echo more >sub2 &&
+ git add -u sub2
+ )
+'
+
+test_expect_success 'change gets noticed' '
+
+ test "$(git diff-files --name-status dir1)" = ""
+
+'
+
+test_expect_success SYMLINKS 'replace a file with a symlink' '
+
+ rm foo &&
+ ln -s top foo &&
+ git add -u -- foo
+
+'
+
+test_expect_success 'add everything changed' '
+
+ git add -u &&
+ test -z "$(git diff-files)"
+
+'
+
+test_expect_success 'touch and then add -u' '
+
+ touch check &&
+ git add -u &&
+ test -z "$(git diff-files)"
+
+'
+
+test_expect_success 'touch and then add explicitly' '
+
+ touch check &&
+ git add check &&
+ test -z "$(git diff-files)"
+
+'
+
+test_expect_success 'add -n -u should not add but just report' '
+
+ (
+ echo "add '\''check'\''" &&
+ echo "remove '\''top'\''"
+ ) >expect &&
+ before=$(git ls-files -s check top) &&
+ echo changed >>check &&
+ rm -f top &&
+ git add -n -u >actual &&
+ after=$(git ls-files -s check top) &&
+
+ test "$before" = "$after" &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'add -u resolves unmerged paths' '
+ git reset --hard &&
+ one=$(echo 1 | git hash-object -w --stdin) &&
+ two=$(echo 2 | git hash-object -w --stdin) &&
+ three=$(echo 3 | git hash-object -w --stdin) &&
+ {
+ for path in path1 path2
+ do
+ echo "100644 $one 1 $path"
+ echo "100644 $two 2 $path"
+ echo "100644 $three 3 $path"
+ done
+ echo "100644 $one 1 path3"
+ echo "100644 $one 1 path4"
+ echo "100644 $one 3 path5"
+ echo "100644 $one 3 path6"
+ } |
+ git update-index --index-info &&
+ echo 3 >path1 &&
+ echo 2 >path3 &&
+ echo 2 >path5 &&
+ git add -u &&
+ git ls-files -s path1 path2 path3 path4 path5 path6 >actual &&
+ {
+ echo "100644 $three 0 path1"
+ echo "100644 $one 1 path3"
+ echo "100644 $one 1 path4"
+ echo "100644 $one 3 path5"
+ echo "100644 $one 3 path6"
+ } >expect &&
+ test_cmp expect actual &&
+
+ # Bonus tests. Explicit resolving
+ git add path3 path5 &&
+ test_must_fail git add path4 &&
+ test_must_fail git add path6 &&
+ git rm path4 &&
+ git rm path6 &&
+
+ git ls-files -s "path?" >actual &&
+ {
+ echo "100644 $three 0 path1"
+ echo "100644 $two 0 path3"
+ echo "100644 $two 0 path5"
+ } >expect
+
+'
+
+test_expect_success '"add -u non-existent" should fail' '
+ test_must_fail git add -u non-existent &&
+ ! (git ls-files | grep "non-existent")
+'
+
+test_done
diff --git a/t/t2201-add-update-typechange.sh b/t/t2201-add-update-typechange.sh
new file mode 100755
index 0000000..2e8f702
--- /dev/null
+++ b/t/t2201-add-update-typechange.sh
@@ -0,0 +1,150 @@
+#!/bin/sh
+
+test_description='more git add -u'
+
+. ./test-lib.sh
+
+_z40=0000000000000000000000000000000000000000
+
+test_expect_success setup '
+ >xyzzy &&
+ _empty=$(git hash-object --stdin <xyzzy) &&
+ >yomin &&
+ >caskly &&
+ if test_have_prereq SYMLINKS; then
+ ln -s frotz nitfol &&
+ T_letter=T
+ else
+ printf %s frotz > nitfol &&
+ T_letter=M
+ fi &&
+ mkdir rezrov &&
+ >rezrov/bozbar &&
+ git add caskly xyzzy yomin nitfol rezrov/bozbar &&
+
+ test_tick &&
+ git commit -m initial
+
+'
+
+test_expect_success modify '
+ rm -f xyzzy yomin nitfol caskly &&
+ # caskly disappears (not a submodule)
+ mkdir caskly &&
+ # nitfol changes from symlink to regular
+ >nitfol &&
+ # rezrov/bozbar disappears
+ rm -fr rezrov &&
+ if test_have_prereq SYMLINKS; then
+ ln -s xyzzy rezrov
+ else
+ printf %s xyzzy > rezrov
+ fi &&
+ # xyzzy disappears (not a submodule)
+ mkdir xyzzy &&
+ echo gnusto >xyzzy/bozbar &&
+ # yomin gets replaced with a submodule
+ mkdir yomin &&
+ >yomin/yomin &&
+ (
+ cd yomin &&
+ git init &&
+ git add yomin &&
+ git commit -m "sub initial"
+ ) &&
+ yomin=$(GIT_DIR=yomin/.git git rev-parse HEAD) &&
+ # yonk is added and then turned into a submodule
+ # this should appear as T in diff-files and as A in diff-index
+ >yonk &&
+ git add yonk &&
+ rm -f yonk &&
+ mkdir yonk &&
+ >yonk/yonk &&
+ (
+ cd yonk &&
+ git init &&
+ git add yonk &&
+ git commit -m "sub initial"
+ ) &&
+ yonk=$(GIT_DIR=yonk/.git git rev-parse HEAD) &&
+ # zifmia is added and then removed
+ # this should appear in diff-files but not in diff-index.
+ >zifmia &&
+ git add zifmia &&
+ rm -f zifmia &&
+ mkdir zifmia &&
+ {
+ git ls-tree -r HEAD |
+ sed -e "s/^/:/" -e "
+ / caskly/{
+ s/ caskly/ $_z40 D&/
+ s/blob/000000/
+ }
+ / nitfol/{
+ s/ nitfol/ $_z40 $T_letter&/
+ s/blob/100644/
+ }
+ / rezrov.bozbar/{
+ s/ rezrov.bozbar/ $_z40 D&/
+ s/blob/000000/
+ }
+ / xyzzy/{
+ s/ xyzzy/ $_z40 D&/
+ s/blob/000000/
+ }
+ / yomin/{
+ s/ yomin/ $_z40 T&/
+ s/blob/160000/
+ }
+ "
+ } >expect &&
+ {
+ cat expect
+ echo ":100644 160000 $_empty $_z40 T yonk"
+ echo ":100644 000000 $_empty $_z40 D zifmia"
+ } >expect-files &&
+ {
+ cat expect
+ echo ":000000 160000 $_z40 $_z40 A yonk"
+ } >expect-index &&
+ {
+ echo "100644 $_empty 0 nitfol"
+ echo "160000 $yomin 0 yomin"
+ echo "160000 $yonk 0 yonk"
+ } >expect-final
+'
+
+test_expect_success diff-files '
+ git diff-files --raw >actual &&
+ test_cmp expect-files actual
+'
+
+test_expect_success diff-index '
+ git diff-index --raw HEAD -- >actual &&
+ test_cmp expect-index actual
+'
+
+test_expect_success 'add -u' '
+ rm -f ".git/saved-index" &&
+ cp -p ".git/index" ".git/saved-index" &&
+ git add -u &&
+ git ls-files -s >actual &&
+ test_cmp expect-final actual
+'
+
+test_expect_success 'commit -a' '
+ if test -f ".git/saved-index"
+ then
+ rm -f ".git/index" &&
+ mv ".git/saved-index" ".git/index"
+ fi &&
+ git commit -m "second" -a &&
+ git ls-files -s >actual &&
+ test_cmp expect-final actual &&
+ rm -f .git/index &&
+ git read-tree HEAD &&
+ git ls-files -s >actual &&
+ test_cmp expect-final actual
+'
+
+test_done
diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh
new file mode 100755
index 0000000..6a81510
--- /dev/null
+++ b/t/t2202-add-addremove.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description='git add --all'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ (
+ echo .gitignore
+ echo will-remove
+ ) >expect &&
+ (
+ echo actual
+ echo expect
+ echo ignored
+ ) >.gitignore &&
+ >will-remove &&
+ git add --all &&
+ test_tick &&
+ git commit -m initial &&
+ git ls-files >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git add --all' '
+ (
+ echo .gitignore
+ echo not-ignored
+ echo "M .gitignore"
+ echo "A not-ignored"
+ echo "D will-remove"
+ ) >expect &&
+ >ignored &&
+ >not-ignored &&
+ echo modification >>.gitignore &&
+ rm -f will-remove &&
+ git add --all &&
+ git update-index --refresh &&
+ git ls-files >actual &&
+ git diff-index --name-status --cached HEAD >>actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2203-add-intent.sh b/t/t2203-add-intent.sh
new file mode 100755
index 0000000..58a3299
--- /dev/null
+++ b/t/t2203-add-intent.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='Intent to add'
+
+. ./test-lib.sh
+
+test_expect_success 'intent to add' '
+ echo hello >file &&
+ echo hello >elif &&
+ git add -N file &&
+ git add elif
+'
+
+test_expect_success 'check result of "add -N"' '
+ git ls-files -s file >actual &&
+ empty=$(git hash-object --stdin </dev/null) &&
+ echo "100644 $empty 0 file" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'intent to add is just an ordinary empty blob' '
+ git add -u &&
+ git ls-files -s file >actual &&
+ git ls-files -s elif | sed -e "s/elif/file/" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'intent to add does not clobber existing paths' '
+ git add -N file elif &&
+ empty=$(git hash-object --stdin </dev/null) &&
+ git ls-files -s >actual &&
+ ! grep "$empty" actual
+'
+
+test_expect_success 'cannot commit with i-t-a entry' '
+ test_tick &&
+ git commit -a -m initial &&
+ git reset --hard &&
+
+ echo xyzzy >rezrov &&
+ echo frotz >nitfol &&
+ git add rezrov &&
+ git add -N nitfol &&
+ test_must_fail git commit
+'
+
+test_expect_success 'can commit with an unrelated i-t-a entry in index' '
+ git reset --hard &&
+ echo xyzzy >rezrov &&
+ echo frotz >nitfol &&
+ git add rezrov &&
+ git add -N nitfol &&
+ git commit -m partial rezrov
+'
+
+test_expect_success 'can "commit -a" with an i-t-a entry' '
+ git reset --hard &&
+ : >nitfol &&
+ git add -N nitfol &&
+ git commit -a -m all
+'
+
+test_done
+
diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh
new file mode 100755
index 0000000..24afdab
--- /dev/null
+++ b/t/t2204-add-ignored.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='giving ignored paths to git add'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir sub dir dir/sub &&
+ echo sub >.gitignore &&
+ echo ign >>.gitignore &&
+ for p in . sub dir dir/sub
+ do
+ >"$p/ign" &&
+ >"$p/file" || exit 1
+ done
+'
+
+for i in file dir/file dir 'd*'
+do
+ test_expect_success "no complaints for unignored $i" '
+ rm -f .git/index &&
+ git add "$i" &&
+ git ls-files "$i" >out &&
+ test -s out
+ '
+done
+
+for i in ign dir/ign dir/sub dir/sub/*ign sub/file sub sub/*
+do
+ test_expect_success "complaints for ignored $i" '
+ rm -f .git/index &&
+ test_must_fail git add "$i" 2>err &&
+ git ls-files "$i" >out &&
+ ! test -s out &&
+ grep -e "Use -f if" err &&
+ cat err
+ '
+
+ test_expect_success "complaints for ignored $i with unignored file" '
+ rm -f .git/index &&
+ test_must_fail git add "$i" file 2>err &&
+ git ls-files "$i" >out &&
+ ! test -s out &&
+ grep -e "Use -f if" err &&
+ cat err
+ '
+done
+
+for i in sub sub/*
+do
+ test_expect_success "complaints for ignored $i in dir" '
+ rm -f .git/index &&
+ (
+ cd dir &&
+ test_must_fail git add "$i" 2>err &&
+ git ls-files "$i" >out &&
+ ! test -s out &&
+ grep -e "Use -f if" err &&
+ cat err
+ )
+ '
+done
+
+for i in ign file
+do
+ test_expect_success "complaints for ignored $i in sub" '
+ rm -f .git/index &&
+ (
+ cd sub &&
+ test_must_fail git add "$i" 2>err &&
+ git ls-files "$i" >out &&
+ ! test -s out &&
+ grep -e "Use -f if" err &&
+ cat err
+ )
+ '
+done
+
+test_done
diff --git a/t/t2300-cd-to-toplevel.sh b/t/t2300-cd-to-toplevel.sh
new file mode 100755
index 0000000..9965bc5
--- /dev/null
+++ b/t/t2300-cd-to-toplevel.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='cd_to_toplevel'
+
+. ./test-lib.sh
+
+test_cd_to_toplevel () {
+ test_expect_success $3 "$2" '
+ (
+ cd '"'$1'"' &&
+ . "$(git --exec-path)"/git-sh-setup &&
+ cd_to_toplevel &&
+ [ "$(pwd -P)" = "$TOPLEVEL" ]
+ )
+ '
+}
+
+TOPLEVEL="$(pwd -P)/repo"
+mkdir -p repo/sub/dir
+mv .git repo/
+SUBDIRECTORY_OK=1
+
+test_cd_to_toplevel repo 'at physical root'
+
+test_cd_to_toplevel repo/sub/dir 'at physical subdir'
+
+ln -s repo symrepo 2>/dev/null
+test_cd_to_toplevel symrepo 'at symbolic root' SYMLINKS
+
+ln -s repo/sub/dir subdir-link 2>/dev/null
+test_cd_to_toplevel subdir-link 'at symbolic subdir' SYMLINKS
+
+cd repo
+ln -s sub/dir internal-link 2>/dev/null
+test_cd_to_toplevel internal-link 'at internal symbolic subdir' SYMLINKS
+
+test_done
diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
new file mode 100755
index 0000000..2eec011
--- /dev/null
+++ b/t/t3000-ls-files-others.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git ls-files test (--others should pick up symlinks).
+
+This test runs git ls-files --others with the following on the
+filesystem.
+
+ path0 - a file
+ path1 - a symlink
+ path2/file2 - a file in a directory
+ path3-junk - a file to confuse things
+ path3/file3 - a file in a directory
+ path4 - an empty directory
+'
+. ./test-lib.sh
+
+test_expect_success 'setup ' '
+ date >path0 &&
+ if test_have_prereq SYMLINKS
+ then
+ ln -s xyzzy path1
+ else
+ date >path1
+ fi &&
+ mkdir path2 path3 path4 &&
+ date >path2/file2 &&
+ date >path2-junk &&
+ date >path3/file3 &&
+ date >path3-junk &&
+ git update-index --add path3-junk path3/file3
+'
+
+test_expect_success 'setup: expected output' '
+ cat >expected1 <<-\EOF &&
+ expected1
+ expected2
+ expected3
+ output
+ path0
+ path1
+ path2-junk
+ path2/file2
+ EOF
+
+ sed -e "s|path2/file2|path2/|" <expected1 >expected2 &&
+ cp expected2 expected3 &&
+ echo path4/ >>expected2
+'
+
+test_expect_success 'ls-files --others' '
+ git ls-files --others >output &&
+ test_cmp expected1 output
+'
+
+test_expect_success 'ls-files --others --directory' '
+ git ls-files --others --directory >output &&
+ test_cmp expected2 output
+'
+
+test_expect_success '--no-empty-directory hides empty directory' '
+ git ls-files --others --directory --no-empty-directory >output &&
+ test_cmp expected3 output
+'
+
+test_done
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
new file mode 100755
index 0000000..6d2f2b6
--- /dev/null
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -0,0 +1,217 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git ls-files --others --exclude
+
+This test runs git ls-files --others and tests --exclude patterns.
+'
+
+. ./test-lib.sh
+
+rm -fr one three
+for dir in . one one/two three
+do
+ mkdir -p $dir &&
+ for i in 1 2 3 4 5 6 7 8
+ do
+ >$dir/a.$i
+ done
+done
+>"#ignore1"
+>"#ignore2"
+>"#hidden"
+
+cat >expect <<EOF
+a.2
+a.4
+a.5
+a.8
+one/a.3
+one/a.4
+one/a.5
+one/a.7
+one/two/a.2
+one/two/a.3
+one/two/a.5
+one/two/a.7
+one/two/a.8
+three/a.2
+three/a.3
+three/a.4
+three/a.5
+three/a.8
+EOF
+
+echo '.gitignore
+\#ignore1
+\#ignore2*
+\#hid*n
+output
+expect
+.gitignore
+*.7
+!*.8' >.git/ignore
+
+echo '*.1
+/*.3
+!*.6' >.gitignore
+echo '*.2
+two/*.4
+!*.7
+*.8' >one/.gitignore
+echo '!*.2
+!*.8' >one/two/.gitignore
+
+allignores='.gitignore one/.gitignore one/two/.gitignore'
+
+test_expect_success \
+ 'git ls-files --others with various exclude options.' \
+ 'git ls-files --others \
+ --exclude=\*.6 \
+ --exclude-per-directory=.gitignore \
+ --exclude-from=.git/ignore \
+ >output &&
+ test_cmp expect output'
+
+# Test \r\n (MSDOS-like systems)
+printf '*.1\r\n/*.3\r\n!*.6\r\n' >.gitignore
+
+test_expect_success \
+ 'git ls-files --others with \r\n line endings.' \
+ 'git ls-files --others \
+ --exclude=\*.6 \
+ --exclude-per-directory=.gitignore \
+ --exclude-from=.git/ignore \
+ >output &&
+ test_cmp expect output'
+
+test_expect_success 'setup skip-worktree gitignore' '
+ git add $allignores &&
+ git update-index --skip-worktree $allignores &&
+ rm $allignores
+'
+
+test_expect_success \
+ 'git ls-files --others with various exclude options.' \
+ 'git ls-files --others \
+ --exclude=\*.6 \
+ --exclude-per-directory=.gitignore \
+ --exclude-from=.git/ignore \
+ >output &&
+ test_cmp expect output'
+
+test_expect_success 'restore gitignore' '
+ git checkout $allignores &&
+ rm .git/index
+'
+
+cat > excludes-file <<\EOF
+*.[1-8]
+e*
+\#*
+EOF
+
+git config core.excludesFile excludes-file
+
+git status | grep "^# " > output
+
+cat > expect << EOF
+# .gitignore
+# a.6
+# one/
+# output
+# three/
+EOF
+
+test_expect_success 'git status honors core.excludesfile' \
+ 'test_cmp expect output'
+
+test_expect_success 'trailing slash in exclude allows directory match(1)' '
+
+ git ls-files --others --exclude=one/ >output &&
+ if grep "^one/" output
+ then
+ echo Ooops
+ false
+ else
+ : happy
+ fi
+
+'
+
+test_expect_success 'trailing slash in exclude allows directory match (2)' '
+
+ git ls-files --others --exclude=one/two/ >output &&
+ if grep "^one/two/" output
+ then
+ echo Ooops
+ false
+ else
+ : happy
+ fi
+
+'
+
+test_expect_success 'trailing slash in exclude forces directory match (1)' '
+
+ >two
+ git ls-files --others --exclude=two/ >output &&
+ grep "^two" output
+
+'
+
+test_expect_success 'trailing slash in exclude forces directory match (2)' '
+
+ git ls-files --others --exclude=one/a.1/ >output &&
+ grep "^one/a.1" output
+
+'
+
+test_expect_success 'negated exclude matches can override previous ones' '
+
+ git ls-files --others --exclude="a.*" --exclude="!a.1" >output &&
+ grep "^a.1" output
+'
+
+test_expect_success 'subdirectory ignore (setup)' '
+ mkdir -p top/l1/l2 &&
+ (
+ cd top &&
+ git init &&
+ echo /.gitignore >.gitignore &&
+ echo l1 >>.gitignore &&
+ echo l2 >l1/.gitignore &&
+ >l1/l2/l1
+ )
+'
+
+test_expect_success 'subdirectory ignore (toplevel)' '
+ (
+ cd top &&
+ git ls-files -o --exclude-standard
+ ) >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'subdirectory ignore (l1/l2)' '
+ (
+ cd top/l1/l2 &&
+ git ls-files -o --exclude-standard
+ ) >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'subdirectory ignore (l1)' '
+ (
+ cd top/l1 &&
+ git ls-files -o --exclude-standard
+ ) >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
new file mode 100755
index 0000000..8704b04
--- /dev/null
+++ b/t/t3002-ls-files-dashpath.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git ls-files test (-- to terminate the path list).
+
+This test runs git ls-files --others with the following on the
+filesystem.
+
+ path0 - a file
+ -foo - a file with a funny name.
+ -- - another file with a funny name.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ setup \
+ 'echo frotz >path0 &&
+ echo frotz >./-foo &&
+ echo frotz >./--'
+
+test_expect_success \
+ 'git ls-files without path restriction.' \
+ 'git ls-files --others >output &&
+ test_cmp output - <<EOF
+--
+-foo
+output
+path0
+EOF
+'
+
+test_expect_success \
+ 'git ls-files with path restriction.' \
+ 'git ls-files --others path0 >output &&
+ test_cmp output - <<EOF
+path0
+EOF
+'
+
+test_expect_success \
+ 'git ls-files with path restriction with --.' \
+ 'git ls-files --others -- path0 >output &&
+ test_cmp output - <<EOF
+path0
+EOF
+'
+
+test_expect_success \
+ 'git ls-files with path restriction with -- --.' \
+ 'git ls-files --others -- -- >output &&
+ test_cmp output - <<EOF
+--
+EOF
+'
+
+test_expect_success \
+ 'git ls-files with no path restriction.' \
+ 'git ls-files --others -- >output &&
+ test_cmp output - <<EOF
+--
+-foo
+output
+path0
+EOF
+'
+
+test_done
diff --git a/t/t3003-ls-files-exclude.sh b/t/t3003-ls-files-exclude.sh
new file mode 100755
index 0000000..d5ec333
--- /dev/null
+++ b/t/t3003-ls-files-exclude.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='ls-files --exclude does not affect index files'
+. ./test-lib.sh
+
+test_expect_success 'create repo with file' '
+ echo content >file &&
+ git add file &&
+ git commit -m file &&
+ echo modification >file
+'
+
+check_output() {
+test_expect_success "ls-files output contains file ($1)" "
+ echo '$2' >expect &&
+ git ls-files --exclude-standard --$1 >output &&
+ test_cmp expect output
+"
+}
+
+check_all_output() {
+ check_output 'cached' 'file'
+ check_output 'modified' 'file'
+}
+
+check_all_output
+test_expect_success 'add file to gitignore' '
+ echo file >.gitignore
+'
+check_all_output
+
+test_expect_success 'ls-files -i lists only tracked-but-ignored files' '
+ echo content >other-file &&
+ git add other-file &&
+ echo file >expect &&
+ git ls-files -i --exclude-standard >output &&
+ test_cmp expect output
+'
+
+test_done
diff --git a/t/t3010-ls-files-killed-modified.sh b/t/t3010-ls-files-killed-modified.sh
new file mode 100755
index 0000000..95671c2
--- /dev/null
+++ b/t/t3010-ls-files-killed-modified.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git ls-files -k and -m flags test.
+
+This test prepares the following in the cache:
+
+ path0 - a file
+ path1 - a symlink
+ path2/file2 - a file in a directory
+ path3/file3 - a file in a directory
+
+and the following on the filesystem:
+
+ path0/file0 - a file in a directory
+ path1/file1 - a file in a directory
+ path2 - a file
+ path3 - a symlink
+ path4 - a file
+ path5 - a symlink
+ path6/file6 - a file in a directory
+
+git ls-files -k should report that existing filesystem
+objects except path4, path5 and path6/file6 to be killed.
+
+Also for modification test, the cache and working tree have:
+
+ path7 - an empty file, modified to a non-empty file.
+ path8 - a non-empty file, modified to an empty file.
+ path9 - an empty file, cache dirtied.
+ path10 - a non-empty file, cache dirtied.
+
+We should report path0, path1, path2/file2, path3/file3, path7 and path8
+modified without reporting path9 and path10.
+'
+. ./test-lib.sh
+
+date >path0
+if test_have_prereq SYMLINKS
+then
+ ln -s xyzzy path1
+else
+ date > path1
+fi
+mkdir path2 path3
+date >path2/file2
+date >path3/file3
+: >path7
+date >path8
+: >path9
+date >path10
+test_expect_success \
+ 'git update-index --add to add various paths.' \
+ "git update-index --add -- path0 path1 path?/file? path7 path8 path9 path10"
+
+rm -fr path? ;# leave path10 alone
+date >path2
+if test_have_prereq SYMLINKS
+then
+ ln -s frotz path3
+ ln -s nitfol path5
+else
+ date > path3
+ date > path5
+fi
+mkdir path0 path1 path6
+date >path0/file0
+date >path1/file1
+date >path6/file6
+date >path7
+: >path8
+: >path9
+touch path10
+
+test_expect_success \
+ 'git ls-files -k to show killed files.' \
+ 'git ls-files -k >.output'
+cat >.expected <<EOF
+path0/file0
+path1/file1
+path2
+path3
+EOF
+
+test_expect_success \
+ 'validate git ls-files -k output.' \
+ 'test_cmp .expected .output'
+
+test_expect_success \
+ 'git ls-files -m to show modified files.' \
+ 'git ls-files -m >.output'
+cat >.expected <<EOF
+path0
+path1
+path2/file2
+path3/file3
+path7
+path8
+EOF
+
+test_expect_success \
+ 'validate git ls-files -m output.' \
+ 'test_cmp .expected .output'
+
+test_done
diff --git a/t/t3020-ls-files-error-unmatch.sh b/t/t3020-ls-files-error-unmatch.sh
new file mode 100755
index 0000000..a7d8187
--- /dev/null
+++ b/t/t3020-ls-files-error-unmatch.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Carl D. Worth
+#
+
+test_description='git ls-files test for --error-unmatch option
+
+This test runs git ls-files --error-unmatch to ensure it correctly
+returns an error when a non-existent path is provided on the command
+line.
+'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ touch foo bar &&
+ git update-index --add foo bar &&
+ git commit -m "add foo bar"
+'
+
+test_expect_success \
+ 'git ls-files --error-unmatch should fail with unmatched path.' \
+ 'test_must_fail git ls-files --error-unmatch foo bar-does-not-match'
+
+test_expect_success \
+ 'git ls-files --error-unmatch should succeed eith matched paths.' \
+ 'git ls-files --error-unmatch foo bar'
+
+test_done
+1
diff --git a/t/t3030-merge-recursive.sh b/t/t3030-merge-recursive.sh
new file mode 100755
index 0000000..d541544
--- /dev/null
+++ b/t/t3030-merge-recursive.sh
@@ -0,0 +1,583 @@
+#!/bin/sh
+
+test_description='merge-recursive backend test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup 1' '
+
+ echo hello >a &&
+ o0=$(git hash-object a) &&
+ cp a b &&
+ cp a c &&
+ mkdir d &&
+ cp a d/e &&
+
+ test_tick &&
+ git add a b c d/e &&
+ git commit -m initial &&
+ c0=$(git rev-parse --verify HEAD) &&
+ git branch side &&
+ git branch df-1 &&
+ git branch df-2 &&
+ git branch df-3 &&
+ git branch remove &&
+ git branch submod &&
+
+ echo hello >>a &&
+ cp a d/e &&
+ o1=$(git hash-object a) &&
+
+ git add a d/e &&
+
+ test_tick &&
+ git commit -m "master modifies a and d/e" &&
+ c1=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o1 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o1 d/e"
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'setup 2' '
+
+ rm -rf [abcd] &&
+ git checkout side &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ test_cmp expected actual &&
+
+ echo goodbye >>a &&
+ o2=$(git hash-object a) &&
+
+ git add a &&
+
+ test_tick &&
+ git commit -m "side modifies a" &&
+ c2=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o2 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o2 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'setup 3' '
+
+ rm -rf [abcd] &&
+ git checkout df-1 &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ test_cmp expected actual &&
+
+ rm -f b && mkdir b && echo df-1 >b/c && git add b/c &&
+ o3=$(git hash-object b/c) &&
+
+ test_tick &&
+ git commit -m "df-1 makes b/c" &&
+ c3=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o3 b/c"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 a"
+ echo "100644 $o3 0 b/c"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'setup 4' '
+
+ rm -rf [abcd] &&
+ git checkout df-2 &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ test_cmp expected actual &&
+
+ rm -f a && mkdir a && echo df-2 >a/c && git add a/c &&
+ o4=$(git hash-object a/c) &&
+
+ test_tick &&
+ git commit -m "df-2 makes a/c" &&
+ c4=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o4 a/c"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o4 0 a/c"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'setup 5' '
+
+ rm -rf [abcd] &&
+ git checkout remove &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ test_cmp expected actual &&
+
+ rm -f b &&
+ echo remove-conflict >a &&
+
+ git add a &&
+ git rm b &&
+ o5=$(git hash-object a) &&
+
+ test_tick &&
+ git commit -m "remove removes b and modifies a" &&
+ c5=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o5 a"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o5 0 a"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'setup 6' '
+
+ rm -rf [abcd] &&
+ git checkout df-3 &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o0 d/e"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o0 0 d/e"
+ ) >expected &&
+ test_cmp expected actual &&
+
+ rm -fr d && echo df-3 >d && git add d &&
+ o6=$(git hash-object d) &&
+
+ test_tick &&
+ git commit -m "df-3 makes d" &&
+ c6=$(git rev-parse --verify HEAD) &&
+ ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+ (
+ echo "100644 blob $o0 a"
+ echo "100644 blob $o0 b"
+ echo "100644 blob $o0 c"
+ echo "100644 blob $o6 d"
+ echo "100644 $o0 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o6 0 d"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'setup 7' '
+
+ git checkout submod &&
+ git rm d/e &&
+ test_tick &&
+ git commit -m "remove d/e" &&
+ git update-index --add --cacheinfo 160000 $c1 d &&
+ test_tick &&
+ git commit -m "make d/ a submodule"
+'
+
+test_expect_success 'merge-recursive simple' '
+
+ rm -fr [abcd] &&
+ git checkout -f "$c2" &&
+
+ git merge-recursive "$c0" -- "$c2" "$c1"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 1 a"
+ echo "100644 $o2 2 a"
+ echo "100644 $o1 3 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'fail if the index has unresolved entries' '
+
+ rm -fr [abcd] &&
+ git checkout -f "$c1" &&
+
+ test_must_fail git merge "$c5" &&
+ test_must_fail git merge "$c5" 2> out &&
+ grep "not possible because you have unmerged files" out &&
+ git add -u &&
+ test_must_fail git merge "$c5" 2> out &&
+ grep "You have not concluded your merge" out &&
+ rm -f .git/MERGE_HEAD &&
+ test_must_fail git merge "$c5" 2> out &&
+ grep "Your local changes to .* would be overwritten by merge." out
+'
+
+test_expect_success 'merge-recursive remove conflict' '
+
+ rm -fr [abcd] &&
+ git checkout -f "$c1" &&
+
+ git merge-recursive "$c0" -- "$c1" "$c5"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive remove conflict' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 1 a"
+ echo "100644 $o1 2 a"
+ echo "100644 $o5 3 a"
+ echo "100644 $o0 0 c"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f simple' '
+ rm -fr [abcd] &&
+ git reset --hard &&
+ git checkout -f "$c1" &&
+
+ git merge-recursive "$c0" -- "$c1" "$c3"
+'
+
+test_expect_success 'merge-recursive result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 a"
+ echo "100644 $o3 0 b/c"
+ echo "100644 $o0 0 c"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+ rm -fr [abcd] &&
+ git reset --hard &&
+ git checkout -f "$c1" &&
+
+ git merge-recursive "$c0" -- "$c1" "$c4"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 1 a"
+ echo "100644 $o1 2 a"
+ echo "100644 $o4 0 a/c"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict the other way' '
+
+ rm -fr [abcd] &&
+ git reset --hard &&
+ git checkout -f "$c4" &&
+
+ git merge-recursive "$c0" -- "$c4" "$c1"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result the other way' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o0 1 a"
+ echo "100644 $o1 3 a"
+ echo "100644 $o4 0 a/c"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+ rm -fr [abcd] &&
+ git reset --hard &&
+ git checkout -f "$c1" &&
+
+ git merge-recursive "$c0" -- "$c1" "$c6"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o6 3 d"
+ echo "100644 $o0 1 d/e"
+ echo "100644 $o1 2 d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'merge-recursive d/f conflict' '
+
+ rm -fr [abcd] &&
+ git reset --hard &&
+ git checkout -f "$c6" &&
+
+ git merge-recursive "$c0" -- "$c6" "$c1"
+ status=$?
+ case "$status" in
+ 1)
+ : happy
+ ;;
+ *)
+ echo >&2 "why status $status!!!"
+ false
+ ;;
+ esac
+'
+
+test_expect_success 'merge-recursive d/f conflict result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o6 2 d"
+ echo "100644 $o0 1 d/e"
+ echo "100644 $o1 3 d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'reset and 3-way merge' '
+
+ git reset --hard "$c2" &&
+ git read-tree -m "$c0" "$c2" "$c1"
+
+'
+
+test_expect_success 'reset and bind merge' '
+
+ git reset --hard master &&
+ git read-tree --prefix=M/ master &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 M/a"
+ echo "100644 $o0 0 M/b"
+ echo "100644 $o0 0 M/c"
+ echo "100644 $o1 0 M/d/e"
+ echo "100644 $o1 0 a"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ test_cmp expected actual &&
+
+ git read-tree --prefix=a1/ master &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 M/a"
+ echo "100644 $o0 0 M/b"
+ echo "100644 $o0 0 M/c"
+ echo "100644 $o1 0 M/d/e"
+ echo "100644 $o1 0 a"
+ echo "100644 $o1 0 a1/a"
+ echo "100644 $o0 0 a1/b"
+ echo "100644 $o0 0 a1/c"
+ echo "100644 $o1 0 a1/d/e"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o1 0 d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+ git read-tree --prefix=z/ master &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 M/a"
+ echo "100644 $o0 0 M/b"
+ echo "100644 $o0 0 M/c"
+ echo "100644 $o1 0 M/d/e"
+ echo "100644 $o1 0 a"
+ echo "100644 $o1 0 a1/a"
+ echo "100644 $o0 0 a1/b"
+ echo "100644 $o0 0 a1/c"
+ echo "100644 $o1 0 a1/d/e"
+ echo "100644 $o0 0 b"
+ echo "100644 $o0 0 c"
+ echo "100644 $o1 0 d/e"
+ echo "100644 $o1 0 z/a"
+ echo "100644 $o0 0 z/b"
+ echo "100644 $o0 0 z/c"
+ echo "100644 $o1 0 z/d/e"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'merge removes empty directories' '
+
+ git reset --hard master &&
+ git checkout -b rm &&
+ git rm d/e &&
+ git commit -mremoved-d/e &&
+ git checkout master &&
+ git merge -s recursive rm &&
+ test_must_fail test -d d
+'
+
+test_expect_failure 'merge-recursive simple w/submodule' '
+
+ git checkout submod &&
+ git merge remove
+'
+
+test_expect_failure 'merge-recursive simple w/submodule result' '
+
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o5 0 a"
+ echo "100644 $o0 0 c"
+ echo "160000 $c1 0 d"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t3031-merge-criscross.sh b/t/t3031-merge-criscross.sh
new file mode 100755
index 0000000..7f41607
--- /dev/null
+++ b/t/t3031-merge-criscross.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='merge-recursive backend test'
+
+. ./test-lib.sh
+
+# A <- create some files
+# / \
+# B C <- cause rename/delete conflicts between B and C
+# / \
+# |\ /|
+# | D E |
+# | \ / |
+# | X |
+# | / \ |
+# | / \ |
+# |/ \|
+# F G <- merge E into B, D into C
+# \ /
+# \ /
+# \ /
+# H <- recursive merge crashes
+#
+
+# initialize
+test_expect_success 'setup repo with criss-cross history' '
+ mkdir data &&
+
+ # create a bunch of files
+ n=1 &&
+ while test $n -le 10
+ do
+ echo $n > data/$n &&
+ n=$(($n+1)) ||
+ break
+ done &&
+
+ # check them in
+ git add data &&
+ git commit -m A &&
+ git branch A &&
+
+ # a file in one branch
+ git checkout -b B A &&
+ git rm data/9 &&
+ git add data &&
+ git commit -m B &&
+
+ # with a branch off of it
+ git branch D &&
+
+ # put some commits on D
+ git checkout D &&
+ echo testD > data/testD &&
+ git add data &&
+ git commit -m D &&
+
+ # back up to the top, create another branch and cause
+ # a rename conflict with the file we deleted earlier
+ git checkout -b C A &&
+ git mv data/9 data/new-9 &&
+ git add data &&
+ git commit -m C &&
+
+ # with a branch off of it
+ git branch E &&
+
+ # put a commit on E
+ git checkout E &&
+ echo testE > data/testE &&
+ git add data &&
+ git commit -m E &&
+
+ # now, merge E into B
+ git checkout B &&
+ test_must_fail git merge E &&
+ # force-resolve
+ git add data &&
+ git commit -m F &&
+ git branch F &&
+
+ # and merge D into C
+ git checkout C &&
+ test_must_fail git merge D &&
+ # force-resolve
+ git add data &&
+ git commit -m G &&
+ git branch G
+'
+
+test_expect_success 'recursive merge between F and G, causes segfault' '
+ git merge F
+'
+
+test_done
diff --git a/t/t3040-subprojects-basic.sh b/t/t3040-subprojects-basic.sh
new file mode 100755
index 0000000..f6973e9
--- /dev/null
+++ b/t/t3040-subprojects-basic.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+test_description='Basic subproject functionality'
+. ./test-lib.sh
+
+test_expect_success 'Super project creation' \
+ ': >Makefile &&
+ git add Makefile &&
+ git commit -m "Superproject created"'
+
+
+cat >expected <<EOF
+:000000 160000 00000... A sub1
+:000000 160000 00000... A sub2
+EOF
+test_expect_success 'create subprojects' \
+ 'mkdir sub1 &&
+ ( cd sub1 && git init && : >Makefile && git add * &&
+ git commit -q -m "subproject 1" ) &&
+ mkdir sub2 &&
+ ( cd sub2 && git init && : >Makefile && git add * &&
+ git commit -q -m "subproject 2" ) &&
+ git update-index --add sub1 &&
+ git add sub2 &&
+ git commit -q -m "subprojects added" &&
+ git diff-tree --abbrev=5 HEAD^ HEAD |cut -d" " -f-3,5- >current &&
+ test_cmp expected current'
+
+git branch save HEAD
+
+test_expect_success 'check if fsck ignores the subprojects' \
+ 'git fsck --full'
+
+test_expect_success 'check if commit in a subproject detected' \
+ '( cd sub1 &&
+ echo "all:" >>Makefile &&
+ echo " true" >>Makefile &&
+ git commit -q -a -m "make all" ) && {
+ git diff-files --exit-code
+ test $? = 1
+ }'
+
+test_expect_success 'check if a changed subproject HEAD can be committed' \
+ 'git commit -q -a -m "sub1 changed" && {
+ git diff-tree --exit-code HEAD^ HEAD
+ test $? = 1
+ }'
+
+test_expect_success 'check if diff-index works for subproject elements' \
+ 'git diff-index --exit-code --cached save -- sub1
+ test $? = 1'
+
+test_expect_success 'check if diff-tree works for subproject elements' \
+ 'git diff-tree --exit-code HEAD^ HEAD -- sub1
+ test $? = 1'
+
+test_expect_success 'check if git diff works for subproject elements' \
+ 'git diff --exit-code HEAD^ HEAD
+ test $? = 1'
+
+test_expect_success 'check if clone works' \
+ 'git ls-files -s >expected &&
+ git clone -l -s . cloned &&
+ ( cd cloned && git ls-files -s ) >current &&
+ test_cmp expected current'
+
+test_expect_success 'removing and adding subproject' \
+ 'git update-index --force-remove -- sub2 &&
+ mv sub2 sub3 &&
+ git add sub3 &&
+ git commit -q -m "renaming a subproject" && {
+ git diff -M --name-status --exit-code HEAD^ HEAD
+ test $? = 1
+ }'
+
+# the index must contain the object name the HEAD of the
+# subproject sub1 was at the point "save"
+test_expect_success 'checkout in superproject' \
+ 'git checkout save &&
+ git diff-index --exit-code --raw --cached save -- sub1'
+
+# just interesting what happened...
+# git diff --name-status -M save master
+
+test_done
diff --git a/t/t3050-subprojects-fetch.sh b/t/t3050-subprojects-fetch.sh
new file mode 100755
index 0000000..4261e96
--- /dev/null
+++ b/t/t3050-subprojects-fetch.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='fetching and pushing project with subproject'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_tick &&
+ mkdir -p sub && (
+ cd sub &&
+ git init &&
+ >subfile &&
+ git add subfile
+ git commit -m "subproject commit #1"
+ ) &&
+ >mainfile
+ git add sub mainfile &&
+ test_tick &&
+ git commit -m "superproject commit #1"
+'
+
+test_expect_success clone '
+ git clone "file://$(pwd)/.git" cloned &&
+ (git rev-parse HEAD; git ls-files -s) >expected &&
+ (
+ cd cloned &&
+ (git rev-parse HEAD; git ls-files -s) >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success advance '
+ echo more >mainfile &&
+ git update-index --force-remove sub &&
+ mv sub/.git sub/.git-disabled &&
+ git add sub/subfile mainfile &&
+ mv sub/.git-disabled sub/.git &&
+ test_tick &&
+ git commit -m "superproject commit #2"
+'
+
+test_expect_success fetch '
+ (git rev-parse HEAD; git ls-files -s) >expected &&
+ (
+ cd cloned &&
+ git pull &&
+ (git rev-parse HEAD; git ls-files -s) >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
new file mode 100755
index 0000000..3ce501b
--- /dev/null
+++ b/t/t3060-ls-files-with-tree.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carl D. Worth
+#
+
+test_description='git ls-files test (--with-tree).
+
+This test runs git ls-files --with-tree and in particular in
+a scenario known to trigger a crash with some versions of git.
+'
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ # The bug we are exercising requires a fair number of entries
+ # in a sub-directory so that add_index_entry will trigger a
+ # realloc.
+
+ echo file >expected &&
+ mkdir sub &&
+ bad= &&
+ for n in 0 1 2 3 4 5
+ do
+ for m in 0 1 2 3 4 5 6 7 8 9
+ do
+ num=00$n$m &&
+ >sub/file-$num &&
+ echo file-$num >>expected || {
+ bad=t
+ break
+ }
+ done && test -z "$bad" || {
+ bad=t
+ break
+ }
+ done && test -z "$bad" &&
+ git add . &&
+ git commit -m "add a bunch of files" &&
+
+ # We remove them all so that we will have something to add
+ # back with --with-tree and so that we will definitely be
+ # under the realloc size to trigger the bug.
+ rm -rf sub &&
+ git commit -a -m "remove them all" &&
+
+ # The bug also requires some entry before our directory so that
+ # prune_path will modify the_index.cache
+
+ mkdir a_directory_that_sorts_before_sub &&
+ >a_directory_that_sorts_before_sub/file &&
+ mkdir sub &&
+ >sub/file &&
+ git add .
+'
+
+# We have to run from a sub-directory to trigger prune_path
+# Then we finally get to run our --with-tree test
+cd sub
+
+test_expect_success 'git -ls-files --with-tree should succeed from subdir' '
+
+ git ls-files --with-tree=HEAD~1 >../output
+
+'
+
+cd ..
+test_expect_success \
+ 'git -ls-files --with-tree should add entries from named tree.' \
+ 'test_cmp expected output'
+
+test_done
diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
new file mode 100755
index 0000000..eee0d34
--- /dev/null
+++ b/t/t3100-ls-tree-restrict.sh
@@ -0,0 +1,168 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git ls-tree test.
+
+This test runs git ls-tree with the following in a tree.
+
+ path0 - a file
+ path1 - a symlink
+ path2/foo - a file in a directory
+ path2/bazbo - a symlink in a directory
+ path2/baz/b - a file in a directory in a directory
+
+The new path restriction code should do the right thing for path2 and
+path2/baz. Also path0/ should snow nothing.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'mkdir path2 path2/baz &&
+ echo Hi >path0 &&
+ if test_have_prereq SYMLINKS
+ then
+ ln -s path0 path1 &&
+ ln -s ../path1 path2/bazbo
+ make_expected () {
+ cat >expected
+ }
+ else
+ printf path0 > path1 &&
+ printf ../path1 > path2/bazbo
+ make_expected () {
+ sed -e "s/120000 /100644 /" >expected
+ }
+ fi &&
+ echo Lo >path2/foo &&
+ echo Mi >path2/baz/b &&
+ find path? \( -type f -o -type l \) -print |
+ xargs git update-index --add &&
+ tree=`git write-tree` &&
+ echo $tree'
+
+test_output () {
+ sed -e "s/ $_x40 / X /" <current >check
+ test_cmp expected check
+}
+
+test_expect_success \
+ 'ls-tree plain' \
+ 'git ls-tree $tree >current &&
+ make_expected <<\EOF &&
+100644 blob X path0
+120000 blob X path1
+040000 tree X path2
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree recursive' \
+ 'git ls-tree -r $tree >current &&
+ make_expected <<\EOF &&
+100644 blob X path0
+120000 blob X path1
+100644 blob X path2/baz/b
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree recursive with -t' \
+ 'git ls-tree -r -t $tree >current &&
+ make_expected <<\EOF &&
+100644 blob X path0
+120000 blob X path1
+040000 tree X path2
+040000 tree X path2/baz
+100644 blob X path2/baz/b
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree recursive with -d' \
+ 'git ls-tree -r -d $tree >current &&
+ make_expected <<\EOF &&
+040000 tree X path2
+040000 tree X path2/baz
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path' \
+ 'git ls-tree $tree path >current &&
+ make_expected <<\EOF &&
+EOF
+ test_output'
+
+
+# it used to be path1 and then path0, but with pathspec semantics
+# they are shown in canonical order.
+test_expect_success \
+ 'ls-tree filtered with path1 path0' \
+ 'git ls-tree $tree path1 path0 >current &&
+ make_expected <<\EOF &&
+100644 blob X path0
+120000 blob X path1
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path0/' \
+ 'git ls-tree $tree path0/ >current &&
+ make_expected <<\EOF &&
+EOF
+ test_output'
+
+# It used to show path2 and its immediate children but
+# with pathspec semantics it shows only path2
+test_expect_success \
+ 'ls-tree filtered with path2' \
+ 'git ls-tree $tree path2 >current &&
+ make_expected <<\EOF &&
+040000 tree X path2
+EOF
+ test_output'
+
+# ... and path2/ shows the children.
+test_expect_success \
+ 'ls-tree filtered with path2/' \
+ 'git ls-tree $tree path2/ >current &&
+ make_expected <<\EOF &&
+040000 tree X path2/baz
+120000 blob X path2/bazbo
+100644 blob X path2/foo
+EOF
+ test_output'
+
+# The same change -- exact match does not show children of
+# path2/baz
+test_expect_success \
+ 'ls-tree filtered with path2/baz' \
+ 'git ls-tree $tree path2/baz >current &&
+ make_expected <<\EOF &&
+040000 tree X path2/baz
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filtered with path2/bak' \
+ 'git ls-tree $tree path2/bak >current &&
+ make_expected <<\EOF &&
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree -t filtered with path2/bak' \
+ 'git ls-tree -t $tree path2/bak >current &&
+ make_expected <<\EOF &&
+040000 tree X path2
+EOF
+ test_output'
+
+test_done
diff --git a/t/t3101-ls-tree-dirname.sh b/t/t3101-ls-tree-dirname.sh
new file mode 100755
index 0000000..06654c6
--- /dev/null
+++ b/t/t3101-ls-tree-dirname.sh
@@ -0,0 +1,227 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+# Copyright (c) 2005 Robert Fitzsimons
+#
+
+test_description='git ls-tree directory and filenames handling.
+
+This test runs git ls-tree with the following in a tree.
+
+ 1.txt - a file
+ 2.txt - a file
+ path0/a/b/c/1.txt - a file in a directory
+ path1/b/c/1.txt - a file in a directory
+ path2/1.txt - a file in a directory
+ path3/1.txt - a file in a directory
+ path3/2.txt - a file in a directory
+
+Test the handling of mulitple directories which have matching file
+entries. Also test odd filename and missing entries handling.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'echo 111 >1.txt &&
+ echo 222 >2.txt &&
+ mkdir path0 path0/a path0/a/b path0/a/b/c &&
+ echo 111 >path0/a/b/c/1.txt &&
+ mkdir path1 path1/b path1/b/c &&
+ echo 111 >path1/b/c/1.txt &&
+ mkdir path2 &&
+ echo 111 >path2/1.txt &&
+ mkdir path3 &&
+ echo 111 >path3/1.txt &&
+ echo 222 >path3/2.txt &&
+ find *.txt path* \( -type f -o -type l \) -print |
+ xargs git update-index --add &&
+ tree=`git write-tree` &&
+ echo $tree'
+
+test_output () {
+ sed -e "s/ $_x40 / X /" <current >check
+ test_cmp expected check
+}
+
+test_expect_success \
+ 'ls-tree plain' \
+ 'git ls-tree $tree >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 2.txt
+040000 tree X path0
+040000 tree X path1
+040000 tree X path2
+040000 tree X path3
+EOF
+ test_output'
+
+# Recursive does not show tree nodes anymore...
+test_expect_success \
+ 'ls-tree recursive' \
+ 'git ls-tree -r $tree >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 2.txt
+100644 blob X path0/a/b/c/1.txt
+100644 blob X path1/b/c/1.txt
+100644 blob X path2/1.txt
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter 1.txt' \
+ 'git ls-tree $tree 1.txt >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter path1/b/c/1.txt' \
+ 'git ls-tree $tree path1/b/c/1.txt >current &&
+ cat >expected <<\EOF &&
+100644 blob X path1/b/c/1.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter all 1.txt files' \
+ 'git ls-tree $tree 1.txt path0/a/b/c/1.txt path1/b/c/1.txt path2/1.txt path3/1.txt >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X path0/a/b/c/1.txt
+100644 blob X path1/b/c/1.txt
+100644 blob X path2/1.txt
+100644 blob X path3/1.txt
+EOF
+ test_output'
+
+# I am not so sure about this one after ls-tree doing pathspec match.
+# Having both path0/a and path0/a/b/c makes path0/a redundant, and
+# it behaves as if path0/a/b/c, path1/b/c, path2 and path3 are specified.
+test_expect_success \
+ 'ls-tree filter directories' \
+ 'git ls-tree $tree path3 path2 path0/a/b/c path1/b/c path0/a >current &&
+ cat >expected <<\EOF &&
+040000 tree X path0/a/b/c
+040000 tree X path1/b/c
+040000 tree X path2
+040000 tree X path3
+EOF
+ test_output'
+
+# Again, duplicates are filtered away so this is equivalent to
+# having 1.txt and path3
+test_expect_success \
+ 'ls-tree filter odd names' \
+ 'git ls-tree $tree 1.txt ./1.txt .//1.txt path3/1.txt path3/./1.txt path3 path3// >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+EOF
+ test_output'
+
+test_expect_success \
+ 'ls-tree filter missing files and extra slashes' \
+ 'git ls-tree $tree 1.txt/ abc.txt path3//23.txt path3/2.txt/// >current &&
+ cat >expected <<\EOF &&
+EOF
+ test_output'
+
+test_expect_success 'ls-tree filter is leading path match' '
+ git ls-tree $tree pa path3/a >current &&
+ >expected &&
+ test_output
+'
+
+test_expect_success 'ls-tree --full-name' '
+ (
+ cd path0 &&
+ git ls-tree --full-name $tree a
+ ) >current &&
+ cat >expected <<\EOF &&
+040000 tree X path0/a
+EOF
+ test_output
+'
+
+test_expect_success 'ls-tree --full-tree' '
+ (
+ cd path1/b/c &&
+ git ls-tree --full-tree $tree
+ ) >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 2.txt
+040000 tree X path0
+040000 tree X path1
+040000 tree X path2
+040000 tree X path3
+EOF
+ test_output
+'
+
+test_expect_success 'ls-tree --full-tree -r' '
+ (
+ cd path3/ &&
+ git ls-tree --full-tree -r $tree
+ ) >current &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 2.txt
+100644 blob X path0/a/b/c/1.txt
+100644 blob X path1/b/c/1.txt
+100644 blob X path2/1.txt
+100644 blob X path3/1.txt
+100644 blob X path3/2.txt
+EOF
+ test_output
+'
+
+test_expect_success 'ls-tree --abbrev=5' '
+ git ls-tree --abbrev=5 $tree >current &&
+ sed -e "s/ $_x05[0-9a-f]* / X /" <current >check &&
+ cat >expected <<\EOF &&
+100644 blob X 1.txt
+100644 blob X 2.txt
+040000 tree X path0
+040000 tree X path1
+040000 tree X path2
+040000 tree X path3
+EOF
+ test_cmp expected check
+'
+
+test_expect_success 'ls-tree --name-only' '
+ git ls-tree --name-only $tree >current
+ cat >expected <<\EOF &&
+1.txt
+2.txt
+path0
+path1
+path2
+path3
+EOF
+ test_output
+'
+
+test_expect_success 'ls-tree --name-only -r' '
+ git ls-tree --name-only -r $tree >current
+ cat >expected <<\EOF &&
+1.txt
+2.txt
+path0/a/b/c/1.txt
+path1/b/c/1.txt
+path2/1.txt
+path3/1.txt
+path3/2.txt
+EOF
+ test_output
+'
+
+test_done
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
new file mode 100755
index 0000000..f54a533
--- /dev/null
+++ b/t/t3200-branch.sh
@@ -0,0 +1,518 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Amos Waterland
+#
+
+test_description='git branch --foo should not create bogus branch
+
+This test runs git branch --help and checks that the argument is properly
+handled. Specifically, that a bogus branch is not created.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'prepare a trivial repository' \
+ 'echo Hello > A &&
+ git update-index --add A &&
+ git commit -m "Initial commit." &&
+ echo World >> A &&
+ git update-index --add A &&
+ git commit -m "Second commit." &&
+ HEAD=$(git rev-parse --verify HEAD)'
+
+test_expect_success \
+ 'git branch --help should not have created a bogus branch' '
+ git branch --help </dev/null >/dev/null 2>/dev/null;
+ ! test -f .git/refs/heads/--help
+'
+
+test_expect_success \
+ 'git branch abc should create a branch' \
+ 'git branch abc && test -f .git/refs/heads/abc'
+
+test_expect_success \
+ 'git branch a/b/c should create a branch' \
+ 'git branch a/b/c && test -f .git/refs/heads/a/b/c'
+
+cat >expect <<EOF
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
+EOF
+test_expect_success \
+ 'git branch -l d/e/f should create a branch and a log' \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ git branch -l d/e/f &&
+ test -f .git/refs/heads/d/e/f &&
+ test -f .git/logs/refs/heads/d/e/f &&
+ test_cmp expect .git/logs/refs/heads/d/e/f'
+
+test_expect_success \
+ 'git branch -d d/e/f should delete a branch and a log' \
+ 'git branch -d d/e/f &&
+ test ! -f .git/refs/heads/d/e/f &&
+ test ! -f .git/logs/refs/heads/d/e/f'
+
+test_expect_success \
+ 'git branch j/k should work after branch j has been deleted' \
+ 'git branch j &&
+ git branch -d j &&
+ git branch j/k'
+
+test_expect_success \
+ 'git branch l should work after branch l/m has been deleted' \
+ 'git branch l/m &&
+ git branch -d l/m &&
+ git branch l'
+
+test_expect_success \
+ 'git branch -m m m/m should work' \
+ 'git branch -l m &&
+ git branch -m m m/m &&
+ test -f .git/logs/refs/heads/m/m'
+
+test_expect_success \
+ 'git branch -m n/n n should work' \
+ 'git branch -l n/n &&
+ git branch -m n/n n
+ test -f .git/logs/refs/heads/n'
+
+test_expect_success 'git branch -m o/o o should fail when o/p exists' '
+ git branch o/o &&
+ git branch o/p &&
+ test_must_fail git branch -m o/o o
+'
+
+test_expect_success 'git branch -m q r/q should fail when r exists' '
+ git branch q &&
+ git branch r &&
+ test_must_fail git branch -m q r/q
+'
+
+mv .git/config .git/config-saved
+
+test_expect_success 'git branch -m q q2 without config should succeed' '
+ git branch -m q q2 &&
+ git branch -m q2 q
+'
+
+mv .git/config-saved .git/config
+
+git config branch.s/s.dummy Hello
+
+test_expect_success \
+ 'git branch -m s/s s should work when s/t is deleted' \
+ 'git branch -l s/s &&
+ test -f .git/logs/refs/heads/s/s &&
+ git branch -l s/t &&
+ test -f .git/logs/refs/heads/s/t &&
+ git branch -d s/t &&
+ git branch -m s/s s &&
+ test -f .git/logs/refs/heads/s'
+
+test_expect_success 'config information was renamed, too' \
+ "test $(git config branch.s.dummy) = Hello &&
+ test_must_fail git config branch.s/s/dummy"
+
+test_expect_success 'renaming a symref is not allowed' \
+'
+ git symbolic-ref refs/heads/master2 refs/heads/master &&
+ test_must_fail git branch -m master2 master3 &&
+ git symbolic-ref refs/heads/master2 &&
+ test -f .git/refs/heads/master &&
+ ! test -f .git/refs/heads/master3
+'
+
+test_expect_success SYMLINKS \
+ 'git branch -m u v should fail when the reflog for u is a symlink' '
+ git branch -l u &&
+ mv .git/logs/refs/heads/u real-u &&
+ ln -s real-u .git/logs/refs/heads/u &&
+ test_must_fail git branch -m u v
+'
+
+test_expect_success 'test tracking setup via --track' \
+ 'git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track my1 local/master &&
+ test $(git config branch.my1.remote) = local &&
+ test $(git config branch.my1.merge) = refs/heads/master'
+
+test_expect_success 'test tracking setup (non-wildcard, matching)' \
+ 'git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/master:refs/remotes/local/master &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track my4 local/master &&
+ test $(git config branch.my4.remote) = local &&
+ test $(git config branch.my4.merge) = refs/heads/master'
+
+test_expect_success 'test tracking setup (non-wildcard, not matching)' \
+ 'git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track my5 local/master &&
+ ! test "$(git config branch.my5.remote)" = local &&
+ ! test "$(git config branch.my5.merge)" = refs/heads/master'
+
+test_expect_success 'test tracking setup via config' \
+ 'git config branch.autosetupmerge true &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch my3 local/master &&
+ test $(git config branch.my3.remote) = local &&
+ test $(git config branch.my3.merge) = refs/heads/master'
+
+test_expect_success 'test overriding tracking setup via --no-track' \
+ 'git config branch.autosetupmerge true &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track my2 local/master &&
+ git config branch.autosetupmerge false &&
+ ! test "$(git config branch.my2.remote)" = local &&
+ ! test "$(git config branch.my2.merge)" = refs/heads/master'
+
+test_expect_success 'no tracking without .fetch entries' \
+ 'git config branch.autosetupmerge true &&
+ git branch my6 s &&
+ git config branch.automsetupmerge false &&
+ test -z "$(git config branch.my6.remote)" &&
+ test -z "$(git config branch.my6.merge)"'
+
+test_expect_success 'test tracking setup via --track but deeper' \
+ 'git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/o/o || git fetch local) &&
+ git branch --track my7 local/o/o &&
+ test "$(git config branch.my7.remote)" = local &&
+ test "$(git config branch.my7.merge)" = refs/heads/o/o'
+
+test_expect_success 'test deleting branch deletes branch config' \
+ 'git branch -d my7 &&
+ test -z "$(git config branch.my7.remote)" &&
+ test -z "$(git config branch.my7.merge)"'
+
+test_expect_success 'test deleting branch without config' \
+ 'git branch my7 s &&
+ sha1=$(git rev-parse my7 | cut -c 1-7) &&
+ test "$(git branch -d my7 2>&1)" = "Deleted branch my7 (was $sha1)."'
+
+test_expect_success 'test --track without .fetch entries' \
+ 'git branch --track my8 &&
+ test "$(git config branch.my8.remote)" &&
+ test "$(git config branch.my8.merge)"'
+
+test_expect_success \
+ 'branch from non-branch HEAD w/autosetupmerge=always' \
+ 'git config branch.autosetupmerge always &&
+ git branch my9 HEAD^ &&
+ git config branch.autosetupmerge false'
+
+test_expect_success \
+ 'branch from non-branch HEAD w/--track causes failure' \
+ 'test_must_fail git branch --track my10 HEAD^'
+
+# Keep this test last, as it changes the current branch
+cat >expect <<EOF
+0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
+EOF
+test_expect_success \
+ 'git checkout -b g/h/i -l should create a branch and a log' \
+ 'GIT_COMMITTER_DATE="2005-05-26 23:30" \
+ git checkout -b g/h/i -l master &&
+ test -f .git/refs/heads/g/h/i &&
+ test -f .git/logs/refs/heads/g/h/i &&
+ test_cmp expect .git/logs/refs/heads/g/h/i'
+
+test_expect_success 'checkout -b makes reflog by default' '
+ git checkout master &&
+ git config --unset core.logAllRefUpdates &&
+ git checkout -b alpha &&
+ git rev-parse --verify alpha@{0}
+'
+
+test_expect_success 'checkout -b does not make reflog when core.logAllRefUpdates = false' '
+ git checkout master &&
+ git config core.logAllRefUpdates false &&
+ git checkout -b beta &&
+ test_must_fail git rev-parse --verify beta@{0}
+'
+
+test_expect_success 'checkout -b with -l makes reflog when core.logAllRefUpdates = false' '
+ git checkout master &&
+ git checkout -lb gamma &&
+ git config --unset core.logAllRefUpdates &&
+ git rev-parse --verify gamma@{0}
+'
+
+test_expect_success 'avoid ambiguous track' '
+ git config branch.autosetupmerge true &&
+ git config remote.ambi1.url lalala &&
+ git config remote.ambi1.fetch refs/heads/lalala:refs/heads/master &&
+ git config remote.ambi2.url lilili &&
+ git config remote.ambi2.fetch refs/heads/lilili:refs/heads/master &&
+ git branch all1 master &&
+ test -z "$(git config branch.all1.merge)"
+'
+
+test_expect_success 'autosetuprebase local on a tracked local branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ git config branch.autosetuprebase local &&
+ (git show-ref -q refs/remotes/local/o || git fetch local) &&
+ git branch mybase &&
+ git branch --track myr1 mybase &&
+ test "$(git config branch.myr1.remote)" = . &&
+ test "$(git config branch.myr1.merge)" = refs/heads/mybase &&
+ test "$(git config branch.myr1.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase always on a tracked local branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ git config branch.autosetuprebase always &&
+ (git show-ref -q refs/remotes/local/o || git fetch local) &&
+ git branch mybase2 &&
+ git branch --track myr2 mybase &&
+ test "$(git config branch.myr2.remote)" = . &&
+ test "$(git config branch.myr2.merge)" = refs/heads/mybase &&
+ test "$(git config branch.myr2.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase remote on a tracked local branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ git config branch.autosetuprebase remote &&
+ (git show-ref -q refs/remotes/local/o || git fetch local) &&
+ git branch mybase3 &&
+ git branch --track myr3 mybase2 &&
+ test "$(git config branch.myr3.remote)" = . &&
+ test "$(git config branch.myr3.merge)" = refs/heads/mybase2 &&
+ ! test "$(git config branch.myr3.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase never on a tracked local branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ git config branch.autosetuprebase never &&
+ (git show-ref -q refs/remotes/local/o || git fetch local) &&
+ git branch mybase4 &&
+ git branch --track myr4 mybase2 &&
+ test "$(git config branch.myr4.remote)" = . &&
+ test "$(git config branch.myr4.merge)" = refs/heads/mybase2 &&
+ ! test "$(git config branch.myr4.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase local on a tracked remote branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ git config branch.autosetuprebase local &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track myr5 local/master &&
+ test "$(git config branch.myr5.remote)" = local &&
+ test "$(git config branch.myr5.merge)" = refs/heads/master &&
+ ! test "$(git config branch.myr5.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase never on a tracked remote branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ git config branch.autosetuprebase never &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track myr6 local/master &&
+ test "$(git config branch.myr6.remote)" = local &&
+ test "$(git config branch.myr6.merge)" = refs/heads/master &&
+ ! test "$(git config branch.myr6.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase remote on a tracked remote branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ git config branch.autosetuprebase remote &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track myr7 local/master &&
+ test "$(git config branch.myr7.remote)" = local &&
+ test "$(git config branch.myr7.merge)" = refs/heads/master &&
+ test "$(git config branch.myr7.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase always on a tracked remote branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ git config branch.autosetuprebase remote &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track myr8 local/master &&
+ test "$(git config branch.myr8.remote)" = local &&
+ test "$(git config branch.myr8.merge)" = refs/heads/master &&
+ test "$(git config branch.myr8.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase unconfigured on a tracked remote branch' '
+ git config --unset branch.autosetuprebase &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --track myr9 local/master &&
+ test "$(git config branch.myr9.remote)" = local &&
+ test "$(git config branch.myr9.merge)" = refs/heads/master &&
+ test "z$(git config branch.myr9.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase unconfigured on a tracked local branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/o || git fetch local) &&
+ git branch mybase10 &&
+ git branch --track myr10 mybase2 &&
+ test "$(git config branch.myr10.remote)" = . &&
+ test "$(git config branch.myr10.merge)" = refs/heads/mybase2 &&
+ test "z$(git config branch.myr10.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase unconfigured on untracked local branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr11 mybase2 &&
+ test "z$(git config branch.myr11.remote)" = z &&
+ test "z$(git config branch.myr11.merge)" = z &&
+ test "z$(git config branch.myr11.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase unconfigured on untracked remote branch' '
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr12 local/master &&
+ test "z$(git config branch.myr12.remote)" = z &&
+ test "z$(git config branch.myr12.merge)" = z &&
+ test "z$(git config branch.myr12.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase never on an untracked local branch' '
+ git config branch.autosetuprebase never &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr13 mybase2 &&
+ test "z$(git config branch.myr13.remote)" = z &&
+ test "z$(git config branch.myr13.merge)" = z &&
+ test "z$(git config branch.myr13.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase local on an untracked local branch' '
+ git config branch.autosetuprebase local &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr14 mybase2 &&
+ test "z$(git config branch.myr14.remote)" = z &&
+ test "z$(git config branch.myr14.merge)" = z &&
+ test "z$(git config branch.myr14.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase remote on an untracked local branch' '
+ git config branch.autosetuprebase remote &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr15 mybase2 &&
+ test "z$(git config branch.myr15.remote)" = z &&
+ test "z$(git config branch.myr15.merge)" = z &&
+ test "z$(git config branch.myr15.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase always on an untracked local branch' '
+ git config branch.autosetuprebase always &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr16 mybase2 &&
+ test "z$(git config branch.myr16.remote)" = z &&
+ test "z$(git config branch.myr16.merge)" = z &&
+ test "z$(git config branch.myr16.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase never on an untracked remote branch' '
+ git config branch.autosetuprebase never &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr17 local/master &&
+ test "z$(git config branch.myr17.remote)" = z &&
+ test "z$(git config branch.myr17.merge)" = z &&
+ test "z$(git config branch.myr17.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase local on an untracked remote branch' '
+ git config branch.autosetuprebase local &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr18 local/master &&
+ test "z$(git config branch.myr18.remote)" = z &&
+ test "z$(git config branch.myr18.merge)" = z &&
+ test "z$(git config branch.myr18.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase remote on an untracked remote branch' '
+ git config branch.autosetuprebase remote &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr19 local/master &&
+ test "z$(git config branch.myr19.remote)" = z &&
+ test "z$(git config branch.myr19.merge)" = z &&
+ test "z$(git config branch.myr19.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase always on an untracked remote branch' '
+ git config branch.autosetuprebase always &&
+ git config remote.local.url . &&
+ git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+ (git show-ref -q refs/remotes/local/master || git fetch local) &&
+ git branch --no-track myr20 local/master &&
+ test "z$(git config branch.myr20.remote)" = z &&
+ test "z$(git config branch.myr20.merge)" = z &&
+ test "z$(git config branch.myr20.rebase)" = z
+'
+
+test_expect_success 'detect misconfigured autosetuprebase (bad value)' '
+ git config branch.autosetuprebase garbage &&
+ test_must_fail git branch
+'
+
+test_expect_success 'detect misconfigured autosetuprebase (no value)' '
+ git config --unset branch.autosetuprebase &&
+ echo "[branch] autosetuprebase" >> .git/config &&
+ test_must_fail git branch &&
+ git config --unset branch.autosetuprebase
+'
+
+test_expect_success 'attempt to delete a branch without base and unmerged to HEAD' '
+ git checkout my9 &&
+ git config --unset branch.my8.merge &&
+ test_must_fail git branch -d my8
+'
+
+test_expect_success 'attempt to delete a branch merged to its base' '
+ # we are on my9 which is the initial commit; traditionally
+ # we would not have allowed deleting my8 that is not merged
+ # to my9, but it is set to track master that already has my8
+ git config branch.my8.merge refs/heads/master &&
+ git branch -d my8
+'
+
+test_expect_success 'attempt to delete a branch merged to its base' '
+ git checkout master &&
+ echo Third >>A &&
+ git commit -m "Third commit" A &&
+ git branch -t my10 my9 &&
+ git branch -f my10 HEAD^ &&
+ # we are on master which is at the third commit, and my10
+ # is behind us, so traditionally we would have allowed deleting
+ # it; but my10 is set to track my9 that is further behind.
+ test_must_fail git branch -d my10
+'
+
+test_done
diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh
new file mode 100755
index 0000000..f86f4bc
--- /dev/null
+++ b/t/t3201-branch-contains.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='branch --contains <commit>, --merged, and --no-merged'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git branch side &&
+
+ echo 1 >file &&
+ test_tick &&
+ git commit -a -m "second on master" &&
+
+ git checkout side &&
+ echo 1 >file &&
+ test_tick &&
+ git commit -a -m "second on side" &&
+
+ git merge master
+
+'
+
+test_expect_success 'branch --contains=master' '
+
+ git branch --contains=master >actual &&
+ {
+ echo " master" && echo "* side"
+ } >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'branch --contains master' '
+
+ git branch --contains master >actual &&
+ {
+ echo " master" && echo "* side"
+ } >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'branch --contains=side' '
+
+ git branch --contains=side >actual &&
+ {
+ echo "* side"
+ } >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'side: branch --merged' '
+
+ git branch --merged >actual &&
+ {
+ echo " master" &&
+ echo "* side"
+ } >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'side: branch --no-merged' '
+
+ git branch --no-merged >actual &&
+ >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'master: branch --merged' '
+
+ git checkout master &&
+ git branch --merged >actual &&
+ {
+ echo "* master"
+ } >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'master: branch --no-merged' '
+
+ git branch --no-merged >actual &&
+ {
+ echo " side"
+ } >expect &&
+ test_cmp expect actual
+
+'
+
+test_done
diff --git a/t/t3202-show-branch-octopus.sh b/t/t3202-show-branch-octopus.sh
new file mode 100755
index 0000000..0a5d5e6
--- /dev/null
+++ b/t/t3202-show-branch-octopus.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='test show-branch with more than 8 heads'
+
+. ./test-lib.sh
+
+numbers="1 2 3 4 5 6 7 8 9 10"
+
+test_expect_success 'setup' '
+
+ > file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+
+ for i in $numbers
+ do
+ git checkout -b branch$i master &&
+ > file$i &&
+ git add file$i &&
+ test_tick &&
+ git commit -m branch$i || break
+ done
+
+'
+
+cat > expect << EOF
+! [branch1] branch1
+ ! [branch2] branch2
+ ! [branch3] branch3
+ ! [branch4] branch4
+ ! [branch5] branch5
+ ! [branch6] branch6
+ ! [branch7] branch7
+ ! [branch8] branch8
+ ! [branch9] branch9
+ * [branch10] branch10
+----------
+ * [branch10] branch10
+ + [branch9] branch9
+ + [branch8] branch8
+ + [branch7] branch7
+ + [branch6] branch6
+ + [branch5] branch5
+ + [branch4] branch4
+ + [branch3] branch3
+ + [branch2] branch2
++ [branch1] branch1
++++++++++* [branch10^] initial
+EOF
+
+test_expect_success 'show-branch with more than 8 branches' '
+
+ git show-branch $(for i in $numbers; do echo branch$i; done) > out &&
+ test_cmp expect out
+
+'
+
+test_expect_success 'show-branch with showbranch.default' '
+ for i in $numbers; do
+ git config --add showbranch.default branch$i
+ done &&
+ git show-branch >out &&
+ test_cmp expect out
+'
+
+test_done
diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh
new file mode 100755
index 0000000..809d1c4
--- /dev/null
+++ b/t/t3203-branch-output.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='git branch display tests'
+. ./test-lib.sh
+
+test_expect_success 'make commits' '
+ echo content >file &&
+ git add file &&
+ git commit -m one &&
+ echo content >>file &&
+ git commit -a -m two
+'
+
+test_expect_success 'make branches' '
+ git branch branch-one
+ git branch branch-two HEAD^
+'
+
+test_expect_success 'make remote branches' '
+ git update-ref refs/remotes/origin/branch-one branch-one
+ git update-ref refs/remotes/origin/branch-two branch-two
+ git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/branch-one
+'
+
+cat >expect <<'EOF'
+ branch-one
+ branch-two
+* master
+EOF
+test_expect_success 'git branch shows local branches' '
+ git branch >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+ origin/HEAD -> origin/branch-one
+ origin/branch-one
+ origin/branch-two
+EOF
+test_expect_success 'git branch -r shows remote branches' '
+ git branch -r >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+ branch-one
+ branch-two
+* master
+ remotes/origin/HEAD -> origin/branch-one
+ remotes/origin/branch-one
+ remotes/origin/branch-two
+EOF
+test_expect_success 'git branch -a shows local and remote branches' '
+ git branch -a >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+two
+one
+two
+EOF
+test_expect_success 'git branch -v shows branch summaries' '
+ git branch -v >tmp &&
+ awk "{print \$NF}" <tmp >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+* (no branch)
+ branch-one
+ branch-two
+ master
+EOF
+test_expect_success 'git branch shows detached HEAD properly' '
+ git checkout HEAD^0 &&
+ git branch >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh
new file mode 100755
index 0000000..5251740
--- /dev/null
+++ b/t/t3210-pack-refs.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Amos Waterland
+# Copyright (c) 2006 Christian Couder
+#
+
+test_description='git pack-refs should not change the branch semantic
+
+This test runs git pack-refs and git show-ref and checks that the branch
+semantic is still the same.
+'
+. ./test-lib.sh
+
+echo '[core] logallrefupdates = true' >>.git/config
+
+test_expect_success \
+ 'prepare a trivial repository' \
+ 'echo Hello > A &&
+ git update-index --add A &&
+ git commit -m "Initial commit." &&
+ HEAD=$(git rev-parse --verify HEAD)'
+
+SHA1=
+
+test_expect_success \
+ 'see if git show-ref works as expected' \
+ 'git branch a &&
+ SHA1=`cat .git/refs/heads/a` &&
+ echo "$SHA1 refs/heads/a" >expect &&
+ git show-ref a >result &&
+ test_cmp expect result'
+
+test_expect_success \
+ 'see if a branch still exists when packed' \
+ 'git branch b &&
+ git pack-refs --all &&
+ rm -f .git/refs/heads/b &&
+ echo "$SHA1 refs/heads/b" >expect &&
+ git show-ref b >result &&
+ test_cmp expect result'
+
+test_expect_success 'git branch c/d should barf if branch c exists' '
+ git branch c &&
+ git pack-refs --all &&
+ rm -f .git/refs/heads/c &&
+ test_must_fail git branch c/d
+'
+
+test_expect_success \
+ 'see if a branch still exists after git pack-refs --prune' \
+ 'git branch e &&
+ git pack-refs --all --prune &&
+ echo "$SHA1 refs/heads/e" >expect &&
+ git show-ref e >result &&
+ test_cmp expect result'
+
+test_expect_success 'see if git pack-refs --prune remove ref files' '
+ git branch f &&
+ git pack-refs --all --prune &&
+ ! test -f .git/refs/heads/f
+'
+
+test_expect_success \
+ 'git branch g should work when git branch g/h has been deleted' \
+ 'git branch g/h &&
+ git pack-refs --all --prune &&
+ git branch -d g/h &&
+ git branch g &&
+ git pack-refs --all &&
+ git branch -d g'
+
+test_expect_success 'git branch i/j/k should barf if branch i exists' '
+ git branch i &&
+ git pack-refs --all --prune &&
+ test_must_fail git branch i/j/k
+'
+
+test_expect_success \
+ 'test git branch k after branch k/l/m and k/lm have been deleted' \
+ 'git branch k/l &&
+ git branch k/lm &&
+ git branch -d k/l &&
+ git branch k/l/m &&
+ git branch -d k/l/m &&
+ git branch -d k/lm &&
+ git branch k'
+
+test_expect_success \
+ 'test git branch n after some branch deletion and pruning' \
+ 'git branch n/o &&
+ git branch n/op &&
+ git branch -d n/o &&
+ git branch n/o/p &&
+ git branch -d n/op &&
+ git pack-refs --all --prune &&
+ git branch -d n/o/p &&
+ git branch n'
+
+test_expect_success \
+ 'see if up-to-date packed refs are preserved' \
+ 'git branch q &&
+ git pack-refs --all --prune &&
+ git update-ref refs/heads/q refs/heads/q &&
+ ! test -f .git/refs/heads/q'
+
+test_expect_success 'pack, prune and repack' '
+ git tag foo &&
+ git pack-refs --all --prune &&
+ git show-ref >all-of-them &&
+ git pack-refs &&
+ git show-ref >again &&
+ test_cmp all-of-them again
+'
+
+test_done
diff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh
new file mode 100755
index 0000000..a99e4d8
--- /dev/null
+++ b/t/t3300-funny-names.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Pathnames with funny characters.
+
+This test tries pathnames with funny characters in the working
+tree, index, and tree objects.
+'
+
+. ./test-lib.sh
+
+p0='no-funny'
+p1='tabs ," (dq) and spaces'
+p2='just space'
+
+cat >"$p0" <<\EOF
+1. A quick brown fox jumps over the lazy cat, oops dog.
+2. A quick brown fox jumps over the lazy cat, oops dog.
+3. A quick brown fox jumps over the lazy cat, oops dog.
+EOF
+
+cat 2>/dev/null >"$p1" "$p0"
+echo 'Foo Bar Baz' >"$p2"
+
+test -f "$p1" && cmp "$p0" "$p1" || {
+ # since FAT/NTFS does not allow tabs in filenames, skip this test
+ skip_all='Your filesystem does not allow tabs in filenames, test skipped.'
+ test_done
+}
+
+echo 'just space
+no-funny' >expected
+test_expect_success 'git ls-files no-funny' \
+ 'git update-index --add "$p0" "$p2" &&
+ git ls-files >current &&
+ test_cmp expected current'
+
+t0=`git write-tree`
+echo "$t0" >t0
+
+cat > expected <<\EOF
+just space
+no-funny
+"tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git ls-files with-funny' \
+ 'git update-index --add "$p1" &&
+ git ls-files >current &&
+ test_cmp expected current'
+
+echo 'just space
+no-funny
+tabs ," (dq) and spaces' >expected
+test_expect_success 'git ls-files -z with-funny' \
+ 'git ls-files -z | perl -pe y/\\000/\\012/ >current &&
+ test_cmp expected current'
+
+t1=`git write-tree`
+echo "$t1" >t1
+
+cat > expected <<\EOF
+just space
+no-funny
+"tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git ls-tree with funny' \
+ 'git ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current &&
+ test_cmp expected current'
+
+cat > expected <<\EOF
+A "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git diff-index with-funny' \
+ 'git diff-index --name-status $t0 >current &&
+ test_cmp expected current'
+
+test_expect_success 'git diff-tree with-funny' \
+ 'git diff-tree --name-status $t0 $t1 >current &&
+ test_cmp expected current'
+
+echo 'A
+tabs ," (dq) and spaces' >expected
+test_expect_success 'git diff-index -z with-funny' \
+ 'git diff-index -z --name-status $t0 | perl -pe y/\\000/\\012/ >current &&
+ test_cmp expected current'
+
+test_expect_success 'git diff-tree -z with-funny' \
+ 'git diff-tree -z --name-status $t0 $t1 | perl -pe y/\\000/\\012/ >current &&
+ test_cmp expected current'
+
+cat > expected <<\EOF
+CNUM no-funny "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git diff-tree -C with-funny' \
+ 'git diff-tree -C --find-copies-harder --name-status \
+ $t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current &&
+ test_cmp expected current'
+
+cat > expected <<\EOF
+RNUM no-funny "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git diff-tree delete with-funny' \
+ 'git update-index --force-remove "$p0" &&
+ git diff-index -M --name-status \
+ $t0 | sed -e 's/^R[0-9]*/RNUM/' >current &&
+ test_cmp expected current'
+
+cat > expected <<\EOF
+diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+similarity index NUM%
+rename from no-funny
+rename to "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git diff-tree delete with-funny' \
+ 'git diff-index -M -p $t0 |
+ sed -e "s/index [0-9]*%/index NUM%/" >current &&
+ test_cmp expected current'
+
+chmod +x "$p1"
+cat > expected <<\EOF
+diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
+old mode 100644
+new mode 100755
+similarity index NUM%
+rename from no-funny
+rename to "tabs\t,\" (dq) and spaces"
+EOF
+test_expect_success 'git diff-tree delete with-funny' \
+ 'git diff-index -M -p $t0 |
+ sed -e "s/index [0-9]*%/index NUM%/" >current &&
+ test_cmp expected current'
+
+cat >expected <<\EOF
+ "tabs\t,\" (dq) and spaces"
+ 1 files changed, 0 insertions(+), 0 deletions(-)
+EOF
+test_expect_success 'git diff-tree rename with-funny applied' \
+ 'git diff-index -M -p $t0 |
+ git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
+ test_cmp expected current'
+
+cat > expected <<\EOF
+ no-funny
+ "tabs\t,\" (dq) and spaces"
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+EOF
+test_expect_success 'git diff-tree delete with-funny applied' \
+ 'git diff-index -p $t0 |
+ git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
+ test_cmp expected current'
+
+test_expect_success 'git apply non-git diff' \
+ 'git diff-index -p $t0 |
+ sed -ne "/^[-+@]/p" |
+ git apply --stat | sed -e "s/|.*//" -e "s/ *\$//" >current &&
+ test_cmp expected current'
+
+test_done
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
new file mode 100755
index 0000000..1d82f79
--- /dev/null
+++ b/t/t3301-notes.sh
@@ -0,0 +1,1057 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test commit notes'
+
+. ./test-lib.sh
+
+cat > fake_editor.sh << \EOF
+#!/bin/sh
+echo "$MSG" > "$1"
+echo "$MSG" >& 2
+EOF
+chmod a+x fake_editor.sh
+GIT_EDITOR=./fake_editor.sh
+export GIT_EDITOR
+
+test_expect_success 'cannot annotate non-existing HEAD' '
+ (MSG=3 && export MSG && test_must_fail git notes add)
+'
+
+test_expect_success setup '
+ : > a1 &&
+ git add a1 &&
+ test_tick &&
+ git commit -m 1st &&
+ : > a2 &&
+ git add a2 &&
+ test_tick &&
+ git commit -m 2nd
+'
+
+test_expect_success 'need valid notes ref' '
+ (MSG=1 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&
+ test_must_fail git notes add) &&
+ (MSG=2 GIT_NOTES_REF=/ && export MSG GIT_NOTES_REF &&
+ test_must_fail git notes show)
+'
+
+test_expect_success 'refusing to add notes in refs/heads/' '
+ (MSG=1 GIT_NOTES_REF=refs/heads/bogus &&
+ export MSG GIT_NOTES_REF &&
+ test_must_fail git notes add)
+'
+
+test_expect_success 'refusing to edit notes in refs/remotes/' '
+ (MSG=1 GIT_NOTES_REF=refs/remotes/bogus &&
+ export MSG GIT_NOTES_REF &&
+ test_must_fail git notes edit)
+'
+
+# 1 indicates caught gracefully by die, 128 means git-show barked
+test_expect_success 'handle empty notes gracefully' '
+ git notes show ; test 1 = $?
+'
+
+test_expect_success 'show non-existent notes entry with %N' '
+ for l in A B
+ do
+ echo "$l"
+ done >expect &&
+ git show -s --format='A%n%NB' >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'create notes' '
+ git config core.notesRef refs/notes/commits &&
+ MSG=b4 git notes add &&
+ test ! -f .git/NOTES_EDITMSG &&
+ test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+ test b4 = $(git notes show) &&
+ git show HEAD^ &&
+ test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'show notes entry with %N' '
+ for l in A b4 B
+ do
+ echo "$l"
+ done >expect &&
+ git show -s --format='A%n%NB' >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+d423f8c refs/notes/commits@{0}: notes: Notes added by 'git notes add'
+EOF
+
+test_expect_success 'create reflog entry' '
+ git reflog show refs/notes/commits >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'edit existing notes' '
+ MSG=b3 git notes edit &&
+ test ! -f .git/NOTES_EDITMSG &&
+ test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+ test b3 = $(git notes show) &&
+ git show HEAD^ &&
+ test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'cannot add note where one exists' '
+ ! MSG=b2 git notes add &&
+ test ! -f .git/NOTES_EDITMSG &&
+ test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+ test b3 = $(git notes show) &&
+ git show HEAD^ &&
+ test_must_fail git notes show HEAD^
+'
+
+test_expect_success 'can overwrite existing note with "git notes add -f"' '
+ MSG=b1 git notes add -f &&
+ test ! -f .git/NOTES_EDITMSG &&
+ test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
+ test b1 = $(git notes show) &&
+ git show HEAD^ &&
+ test_must_fail git notes show HEAD^
+'
+
+cat > expect << EOF
+commit 268048bfb8a1fb38e703baceb8ab235421bf80c5
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:14:13 2005 -0700
+
+ 2nd
+
+Notes:
+ b1
+EOF
+
+test_expect_success 'show notes' '
+ ! (git cat-file commit HEAD | grep b1) &&
+ git log -1 > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'create multi-line notes (setup)' '
+ : > a3 &&
+ git add a3 &&
+ test_tick &&
+ git commit -m 3rd &&
+ MSG="b3
+c3c3c3c3
+d3d3d3" git notes add
+'
+
+cat > expect-multiline << EOF
+commit 1584215f1d29c65e99c6c6848626553fdd07fd75
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:15:13 2005 -0700
+
+ 3rd
+
+Notes:
+ b3
+ c3c3c3c3
+ d3d3d3
+EOF
+
+printf "\n" >> expect-multiline
+cat expect >> expect-multiline
+
+test_expect_success 'show multi-line notes' '
+ git log -2 > output &&
+ test_cmp expect-multiline output
+'
+test_expect_success 'create -F notes (setup)' '
+ : > a4 &&
+ git add a4 &&
+ test_tick &&
+ git commit -m 4th &&
+ echo "xyzzy" > note5 &&
+ git notes add -F note5
+'
+
+cat > expect-F << EOF
+commit 15023535574ded8b1a89052b32673f84cf9582b8
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:16:13 2005 -0700
+
+ 4th
+
+Notes:
+ xyzzy
+EOF
+
+printf "\n" >> expect-F
+cat expect-multiline >> expect-F
+
+test_expect_success 'show -F notes' '
+ git log -3 > output &&
+ test_cmp expect-F output
+'
+
+cat >expect << EOF
+commit 15023535574ded8b1a89052b32673f84cf9582b8
+tree e070e3af51011e47b183c33adf9736736a525709
+parent 1584215f1d29c65e99c6c6848626553fdd07fd75
+author A U Thor <author@example.com> 1112912173 -0700
+committer C O Mitter <committer@example.com> 1112912173 -0700
+
+ 4th
+EOF
+test_expect_success 'git log --pretty=raw does not show notes' '
+ git log -1 --pretty=raw >output &&
+ test_cmp expect output
+'
+
+cat >>expect <<EOF
+
+Notes:
+ xyzzy
+EOF
+test_expect_success 'git log --show-notes' '
+ git log -1 --pretty=raw --show-notes >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'git log --no-notes' '
+ git log -1 --no-notes >output &&
+ ! grep xyzzy output
+'
+
+test_expect_success 'git format-patch does not show notes' '
+ git format-patch -1 --stdout >output &&
+ ! grep xyzzy output
+'
+
+test_expect_success 'git format-patch --show-notes does show notes' '
+ git format-patch --show-notes -1 --stdout >output &&
+ grep xyzzy output
+'
+
+for pretty in \
+ "" --pretty --pretty=raw --pretty=short --pretty=medium \
+ --pretty=full --pretty=fuller --pretty=format:%s --oneline
+do
+ case "$pretty" in
+ "") p= not= negate="" ;;
+ ?*) p="$pretty" not=" not" negate="!" ;;
+ esac
+ test_expect_success "git show $pretty does$not show notes" '
+ git show $p >output &&
+ eval "$negate grep xyzzy output"
+ '
+done
+
+test_expect_success 'create -m notes (setup)' '
+ : > a5 &&
+ git add a5 &&
+ test_tick &&
+ git commit -m 5th &&
+ git notes add -m spam -m "foo
+bar
+baz"
+'
+
+whitespace=" "
+cat > expect-m << EOF
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:17:13 2005 -0700
+
+ 5th
+
+Notes:
+ spam
+$whitespace
+ foo
+ bar
+ baz
+EOF
+
+printf "\n" >> expect-m
+cat expect-F >> expect-m
+
+test_expect_success 'show -m notes' '
+ git log -4 > output &&
+ test_cmp expect-m output
+'
+
+test_expect_success 'remove note with add -f -F /dev/null (setup)' '
+ git notes add -f -F /dev/null
+'
+
+cat > expect-rm-F << EOF
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:17:13 2005 -0700
+
+ 5th
+EOF
+
+printf "\n" >> expect-rm-F
+cat expect-F >> expect-rm-F
+
+test_expect_success 'verify note removal with -F /dev/null' '
+ git log -4 > output &&
+ test_cmp expect-rm-F output &&
+ ! git notes show
+'
+
+test_expect_success 'do not create empty note with -m "" (setup)' '
+ git notes add -m ""
+'
+
+test_expect_success 'verify non-creation of note with -m ""' '
+ git log -4 > output &&
+ test_cmp expect-rm-F output &&
+ ! git notes show
+'
+
+cat > expect-combine_m_and_F << EOF
+foo
+
+xyzzy
+
+bar
+
+zyxxy
+
+baz
+EOF
+
+test_expect_success 'create note with combination of -m and -F' '
+ echo "xyzzy" > note_a &&
+ echo "zyxxy" > note_b &&
+ git notes add -m "foo" -F note_a -m "bar" -F note_b -m "baz" &&
+ git notes show > output &&
+ test_cmp expect-combine_m_and_F output
+'
+
+test_expect_success 'remove note with "git notes remove" (setup)' '
+ git notes remove HEAD^ &&
+ git notes remove
+'
+
+cat > expect-rm-remove << EOF
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:17:13 2005 -0700
+
+ 5th
+
+commit 15023535574ded8b1a89052b32673f84cf9582b8
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:16:13 2005 -0700
+
+ 4th
+EOF
+
+printf "\n" >> expect-rm-remove
+cat expect-multiline >> expect-rm-remove
+
+test_expect_success 'verify note removal with "git notes remove"' '
+ git log -4 > output &&
+ test_cmp expect-rm-remove output &&
+ ! git notes show HEAD^
+'
+
+cat > expect << EOF
+c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
+c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
+EOF
+
+test_expect_success 'list notes with "git notes list"' '
+ git notes list > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'list notes with "git notes"' '
+ git notes > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+c18dc024e14f08d18d14eea0d747ff692d66d6a3
+EOF
+
+test_expect_success 'list specific note with "git notes list <object>"' '
+ git notes list HEAD^^ > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+EOF
+
+test_expect_success 'listing non-existing notes fails' '
+ test_must_fail git notes list HEAD > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+Initial set of notes
+
+More notes appended with git notes append
+EOF
+
+test_expect_success 'append to existing note with "git notes append"' '
+ git notes add -m "Initial set of notes" &&
+ git notes append -m "More notes appended with git notes append" &&
+ git notes show > output &&
+ test_cmp expect output
+'
+
+cat > expect_list << EOF
+c18dc024e14f08d18d14eea0d747ff692d66d6a3 1584215f1d29c65e99c6c6848626553fdd07fd75
+c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 268048bfb8a1fb38e703baceb8ab235421bf80c5
+4b6ad22357cc8a1296720574b8d2fbc22fab0671 bd1753200303d0a0344be813e504253b3d98e74d
+EOF
+
+test_expect_success '"git notes list" does not expand to "git notes list HEAD"' '
+ git notes list > output &&
+ test_cmp expect_list output
+'
+
+test_expect_success 'appending empty string does not change existing note' '
+ git notes append -m "" &&
+ git notes show > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'git notes append == add when there is no existing note' '
+ git notes remove HEAD &&
+ test_must_fail git notes list HEAD &&
+ git notes append -m "Initial set of notes
+
+More notes appended with git notes append" &&
+ git notes show > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'appending empty string to non-existing note does not create note' '
+ git notes remove HEAD &&
+ test_must_fail git notes list HEAD &&
+ git notes append -m "" &&
+ test_must_fail git notes list HEAD
+'
+
+test_expect_success 'create other note on a different notes ref (setup)' '
+ : > a6 &&
+ git add a6 &&
+ test_tick &&
+ git commit -m 6th &&
+ GIT_NOTES_REF="refs/notes/other" git notes add -m "other note"
+'
+
+cat > expect-other << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:18:13 2005 -0700
+
+ 6th
+
+Notes (other):
+ other note
+EOF
+
+cat > expect-not-other << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:18:13 2005 -0700
+
+ 6th
+EOF
+
+test_expect_success 'Do not show note on other ref by default' '
+ git log -1 > output &&
+ test_cmp expect-not-other output
+'
+
+test_expect_success 'Do show note when ref is given in GIT_NOTES_REF' '
+ GIT_NOTES_REF="refs/notes/other" git log -1 > output &&
+ test_cmp expect-other output
+'
+
+test_expect_success 'Do show note when ref is given in core.notesRef config' '
+ git config core.notesRef "refs/notes/other" &&
+ git log -1 > output &&
+ test_cmp expect-other output
+'
+
+test_expect_success 'Do not show note when core.notesRef is overridden' '
+ GIT_NOTES_REF="refs/notes/wrong" git log -1 > output &&
+ test_cmp expect-not-other output
+'
+
+cat > expect-both << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:18:13 2005 -0700
+
+ 6th
+
+Notes:
+ order test
+
+Notes (other):
+ other note
+
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:17:13 2005 -0700
+
+ 5th
+
+Notes:
+ replacement for deleted note
+EOF
+
+test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
+ GIT_NOTES_REF=refs/notes/commits git notes add \
+ -m"replacement for deleted note" HEAD^ &&
+ GIT_NOTES_REF=refs/notes/commits git notes add -m"order test" &&
+ git config --unset core.notesRef &&
+ git config notes.displayRef "refs/notes/*" &&
+ git log -2 > output &&
+ test_cmp expect-both output
+'
+
+test_expect_success 'core.notesRef is implicitly in notes.displayRef' '
+ git config core.notesRef refs/notes/commits &&
+ git config notes.displayRef refs/notes/other &&
+ git log -2 > output &&
+ test_cmp expect-both output
+'
+
+test_expect_success 'notes.displayRef can be given more than once' '
+ git config --unset core.notesRef &&
+ git config notes.displayRef refs/notes/commits &&
+ git config --add notes.displayRef refs/notes/other &&
+ git log -2 > output &&
+ test_cmp expect-both output
+'
+
+cat > expect-both-reversed << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:18:13 2005 -0700
+
+ 6th
+
+Notes (other):
+ other note
+
+Notes:
+ order test
+EOF
+
+test_expect_success 'notes.displayRef respects order' '
+ git config core.notesRef refs/notes/other &&
+ git config --unset-all notes.displayRef &&
+ git config notes.displayRef refs/notes/commits &&
+ git log -1 > output &&
+ test_cmp expect-both-reversed output
+'
+
+test_expect_success 'GIT_NOTES_DISPLAY_REF works' '
+ git config --unset-all core.notesRef &&
+ git config --unset-all notes.displayRef &&
+ GIT_NOTES_DISPLAY_REF=refs/notes/commits:refs/notes/other \
+ git log -2 > output &&
+ test_cmp expect-both output
+'
+
+cat > expect-none << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:18:13 2005 -0700
+
+ 6th
+
+commit bd1753200303d0a0344be813e504253b3d98e74d
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:17:13 2005 -0700
+
+ 5th
+EOF
+
+test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '
+ git config notes.displayRef "refs/notes/*" &&
+ GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log -2 > output &&
+ test_cmp expect-none output
+'
+
+test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '
+ GIT_NOTES_REF= GIT_NOTES_DISPLAY_REF= git log --show-notes=* -2 > output &&
+ test_cmp expect-both output
+'
+
+cat > expect-commits << EOF
+commit 387a89921c73d7ed72cd94d179c1c7048ca47756
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:18:13 2005 -0700
+
+ 6th
+
+Notes:
+ order test
+EOF
+
+test_expect_success '--no-standard-notes' '
+ git log --no-standard-notes --show-notes=commits -1 > output &&
+ test_cmp expect-commits output
+'
+
+test_expect_success '--standard-notes' '
+ git log --no-standard-notes --show-notes=commits \
+ --standard-notes -2 > output &&
+ test_cmp expect-both output
+'
+
+test_expect_success '--show-notes=ref accumulates' '
+ git log --show-notes=other --show-notes=commits \
+ --no-standard-notes -1 > output &&
+ test_cmp expect-both-reversed output
+'
+
+test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
+ git config core.notesRef refs/notes/other &&
+ echo "Note on a tree" > expect
+ git notes add -m "Note on a tree" HEAD: &&
+ git notes show HEAD: > actual &&
+ test_cmp expect actual &&
+ echo "Note on a blob" > expect
+ filename=$(git ls-tree --name-only HEAD | head -n1) &&
+ git notes add -m "Note on a blob" HEAD:$filename &&
+ git notes show HEAD:$filename > actual &&
+ test_cmp expect actual &&
+ echo "Note on a tag" > expect
+ git tag -a -m "This is an annotated tag" foobar HEAD^ &&
+ git notes add -m "Note on a tag" foobar &&
+ git notes show foobar > actual &&
+ test_cmp expect actual
+'
+
+cat > expect << EOF
+commit 2ede89468182a62d0bde2583c736089bcf7d7e92
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:19:13 2005 -0700
+
+ 7th
+
+Notes (other):
+ other note
+EOF
+
+test_expect_success 'create note from other note with "git notes add -C"' '
+ : > a7 &&
+ git add a7 &&
+ test_tick &&
+ git commit -m 7th &&
+ git notes add -C $(git notes list HEAD^) &&
+ git log -1 > actual &&
+ test_cmp expect actual &&
+ test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+'
+
+test_expect_success 'create note from non-existing note with "git notes add -C" fails' '
+ : > a8 &&
+ git add a8 &&
+ test_tick &&
+ git commit -m 8th &&
+ test_must_fail git notes add -C deadbeef &&
+ test_must_fail git notes list HEAD
+'
+
+cat > expect << EOF
+commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:21:13 2005 -0700
+
+ 9th
+
+Notes (other):
+ yet another note
+EOF
+
+test_expect_success 'create note from other note with "git notes add -c"' '
+ : > a9 &&
+ git add a9 &&
+ test_tick &&
+ git commit -m 9th &&
+ MSG="yet another note" git notes add -c $(git notes list HEAD^^) &&
+ git log -1 > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'create note from non-existing note with "git notes add -c" fails' '
+ : > a10 &&
+ git add a10 &&
+ test_tick &&
+ git commit -m 10th &&
+ (
+ MSG="yet another note" &&
+ export MSG &&
+ test_must_fail git notes add -c deadbeef
+ ) &&
+ test_must_fail git notes list HEAD
+'
+
+cat > expect << EOF
+commit 016e982bad97eacdbda0fcbd7ce5b0ba87c81f1b
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:21:13 2005 -0700
+
+ 9th
+
+Notes (other):
+ yet another note
+$whitespace
+ yet another note
+EOF
+
+test_expect_success 'append to note from other note with "git notes append -C"' '
+ git notes append -C $(git notes list HEAD^) HEAD^ &&
+ git log -1 HEAD^ > actual &&
+ test_cmp expect actual
+'
+
+cat > expect << EOF
+commit ffed603236bfa3891c49644257a83598afe8ae5a
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:22:13 2005 -0700
+
+ 10th
+
+Notes (other):
+ other note
+EOF
+
+test_expect_success 'create note from other note with "git notes append -c"' '
+ MSG="other note" git notes append -c $(git notes list HEAD^) &&
+ git log -1 > actual &&
+ test_cmp expect actual
+'
+
+cat > expect << EOF
+commit ffed603236bfa3891c49644257a83598afe8ae5a
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:22:13 2005 -0700
+
+ 10th
+
+Notes (other):
+ other note
+$whitespace
+ yet another note
+EOF
+
+test_expect_success 'append to note from other note with "git notes append -c"' '
+ MSG="yet another note" git notes append -c $(git notes list HEAD) &&
+ git log -1 > actual &&
+ test_cmp expect actual
+'
+
+cat > expect << EOF
+commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:23:13 2005 -0700
+
+ 11th
+
+Notes (other):
+ other note
+$whitespace
+ yet another note
+EOF
+
+test_expect_success 'copy note with "git notes copy"' '
+ : > a11 &&
+ git add a11 &&
+ test_tick &&
+ git commit -m 11th &&
+ git notes copy HEAD^ HEAD &&
+ git log -1 > actual &&
+ test_cmp expect actual &&
+ test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+'
+
+test_expect_success 'prevent overwrite with "git notes copy"' '
+ test_must_fail git notes copy HEAD~2 HEAD &&
+ git log -1 > actual &&
+ test_cmp expect actual &&
+ test "$(git notes list HEAD)" = "$(git notes list HEAD^)"
+'
+
+cat > expect << EOF
+commit 6352c5e33dbcab725fe0579be16aa2ba8eb369be
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:23:13 2005 -0700
+
+ 11th
+
+Notes (other):
+ yet another note
+$whitespace
+ yet another note
+EOF
+
+test_expect_success 'allow overwrite with "git notes copy -f"' '
+ git notes copy -f HEAD~2 HEAD &&
+ git log -1 > actual &&
+ test_cmp expect actual &&
+ test "$(git notes list HEAD)" = "$(git notes list HEAD~2)"
+'
+
+test_expect_success 'cannot copy note from object without notes' '
+ : > a12 &&
+ git add a12 &&
+ test_tick &&
+ git commit -m 12th &&
+ : > a13 &&
+ git add a13 &&
+ test_tick &&
+ git commit -m 13th &&
+ test_must_fail git notes copy HEAD^ HEAD
+'
+
+cat > expect << EOF
+commit e5d4fb5698d564ab8c73551538ecaf2b0c666185
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:25:13 2005 -0700
+
+ 13th
+
+Notes (other):
+ yet another note
+$whitespace
+ yet another note
+
+commit 7038787dfe22a14c3867ce816dbba39845359719
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:24:13 2005 -0700
+
+ 12th
+
+Notes (other):
+ other note
+$whitespace
+ yet another note
+EOF
+
+test_expect_success 'git notes copy --stdin' '
+ (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+ echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+ git notes copy --stdin &&
+ git log -2 > output &&
+ test_cmp expect output &&
+ test "$(git notes list HEAD)" = "$(git notes list HEAD~2)" &&
+ test "$(git notes list HEAD^)" = "$(git notes list HEAD~3)"
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:27:13 2005 -0700
+
+ 15th
+
+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:26:13 2005 -0700
+
+ 14th
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
+ test_commit 14th &&
+ test_commit 15th &&
+ (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+ echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+ git notes copy --for-rewrite=foo &&
+ git log -2 > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:27:13 2005 -0700
+
+ 15th
+
+Notes (other):
+ yet another note
+$whitespace
+ yet another note
+
+commit be28d8b4d9951ad940d229ee3b0b9ee3b1ec273d
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:26:13 2005 -0700
+
+ 14th
+
+Notes (other):
+ other note
+$whitespace
+ yet another note
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (enabled)' '
+ git config notes.rewriteMode overwrite &&
+ git config notes.rewriteRef "refs/notes/*" &&
+ (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+ echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
+ git notes copy --for-rewrite=foo &&
+ git log -2 > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'git notes copy --for-rewrite (disabled)' '
+ git config notes.rewrite.bar false &&
+ echo $(git rev-parse HEAD~3) $(git rev-parse HEAD) |
+ git notes copy --for-rewrite=bar &&
+ git log -2 > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:27:13 2005 -0700
+
+ 15th
+
+Notes (other):
+ a fresh note
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (overwrite)' '
+ git notes add -f -m"a fresh note" HEAD^ &&
+ echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+ git notes copy --for-rewrite=foo &&
+ git log -1 > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'git notes copy --for-rewrite (ignore)' '
+ git config notes.rewriteMode ignore &&
+ echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+ git notes copy --for-rewrite=foo &&
+ git log -1 > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:27:13 2005 -0700
+
+ 15th
+
+Notes (other):
+ a fresh note
+ another fresh note
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (append)' '
+ git notes add -f -m"another fresh note" HEAD^ &&
+ git config notes.rewriteMode concatenate &&
+ echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+ git notes copy --for-rewrite=foo &&
+ git log -1 > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:27:13 2005 -0700
+
+ 15th
+
+Notes (other):
+ a fresh note
+ another fresh note
+ append 1
+ append 2
+EOF
+
+test_expect_success 'git notes copy --for-rewrite (append two to one)' '
+ git notes add -f -m"append 1" HEAD^ &&
+ git notes add -f -m"append 2" HEAD^^ &&
+ (echo $(git rev-parse HEAD^) $(git rev-parse HEAD);
+ echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
+ git notes copy --for-rewrite=foo &&
+ git log -1 > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'git notes copy --for-rewrite (append empty)' '
+ git notes remove HEAD^ &&
+ echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+ git notes copy --for-rewrite=foo &&
+ git log -1 > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:27:13 2005 -0700
+
+ 15th
+
+Notes (other):
+ replacement note 1
+EOF
+
+test_expect_success 'GIT_NOTES_REWRITE_MODE works' '
+ git notes add -f -m"replacement note 1" HEAD^ &&
+ echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+ GIT_NOTES_REWRITE_MODE=overwrite git notes copy --for-rewrite=foo &&
+ git log -1 > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+commit 37a0d4cba38afef96ba54a3ea567e6dac575700b
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:27:13 2005 -0700
+
+ 15th
+
+Notes (other):
+ replacement note 2
+EOF
+
+test_expect_success 'GIT_NOTES_REWRITE_REF works' '
+ git config notes.rewriteMode overwrite &&
+ git notes add -f -m"replacement note 2" HEAD^ &&
+ git config --unset-all notes.rewriteRef &&
+ echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+ GIT_NOTES_REWRITE_REF=refs/notes/commits:refs/notes/other \
+ git notes copy --for-rewrite=foo &&
+ git log -1 > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'GIT_NOTES_REWRITE_REF overrides config' '
+ git config notes.rewriteRef refs/notes/other &&
+ git notes add -f -m"replacement note 3" HEAD^ &&
+ echo $(git rev-parse HEAD^) $(git rev-parse HEAD) |
+ GIT_NOTES_REWRITE_REF= git notes copy --for-rewrite=foo &&
+ git log -1 > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'git notes copy diagnoses too many or too few parameters' '
+ test_must_fail git notes copy &&
+ test_must_fail git notes copy one two three
+'
+
+test_done
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
new file mode 100755
index 0000000..361a10a
--- /dev/null
+++ b/t/t3302-notes-index-expensive.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test commit notes index (expensive!)'
+
+. ./test-lib.sh
+
+test -z "$GIT_NOTES_TIMING_TESTS" && {
+ skip_all="Skipping timing tests"
+ test_done
+ exit
+}
+
+create_repo () {
+ number_of_commits=$1
+ nr=0
+ test -d .git || {
+ git init &&
+ (
+ while [ $nr -lt $number_of_commits ]; do
+ nr=$(($nr+1))
+ mark=$(($nr+$nr))
+ notemark=$(($mark+1))
+ test_tick &&
+ cat <<INPUT_END &&
+commit refs/heads/master
+mark :$mark
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #$nr
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #$nr
+EOF
+
+blob
+mark :$notemark
+data <<EOF
+note for commit #$nr
+EOF
+
+INPUT_END
+
+ echo "N :$notemark :$mark" >> note_commit
+ done &&
+ test_tick &&
+ cat <<INPUT_END &&
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes
+COMMIT
+
+INPUT_END
+
+ cat note_commit
+ ) |
+ git fast-import --quiet &&
+ git config core.notesRef refs/notes/commits
+ }
+}
+
+test_notes () {
+ count=$1 &&
+ git config core.notesRef refs/notes/commits &&
+ git log | grep "^ " > output &&
+ i=$count &&
+ while [ $i -gt 0 ]; do
+ echo " commit #$i" &&
+ echo " note for commit #$i" &&
+ i=$(($i-1));
+ done > expect &&
+ test_cmp expect output
+}
+
+cat > time_notes << \EOF
+ mode=$1
+ i=1
+ while [ $i -lt $2 ]; do
+ case $1 in
+ no-notes)
+ GIT_NOTES_REF=non-existing; export GIT_NOTES_REF
+ ;;
+ notes)
+ unset GIT_NOTES_REF
+ ;;
+ esac
+ git log >/dev/null
+ i=$(($i+1))
+ done
+EOF
+
+time_notes () {
+ for mode in no-notes notes
+ do
+ echo $mode
+ /usr/bin/time sh ../time_notes $mode $1
+ done
+}
+
+for count in 10 100 1000 10000; do
+
+ mkdir $count
+ (cd $count;
+
+ test_expect_success "setup $count" "create_repo $count"
+
+ test_expect_success 'notes work' "test_notes $count"
+
+ test_expect_success 'notes timing' "time_notes 100"
+ )
+done
+
+test_done
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
new file mode 100755
index 0000000..75ec187
--- /dev/null
+++ b/t/t3303-notes-subtrees.sh
@@ -0,0 +1,194 @@
+#!/bin/sh
+
+test_description='Test commit notes organized in subtrees'
+
+. ./test-lib.sh
+
+number_of_commits=100
+
+start_note_commit () {
+ test_tick &&
+ cat <<INPUT_END
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes
+COMMIT
+
+from refs/notes/commits^0
+deleteall
+INPUT_END
+
+}
+
+verify_notes () {
+ git log | grep "^ " > output &&
+ i=$number_of_commits &&
+ while [ $i -gt 0 ]; do
+ echo " commit #$i" &&
+ echo " note for commit #$i" &&
+ i=$(($i-1));
+ done > expect &&
+ test_cmp expect output
+}
+
+test_expect_success "setup: create $number_of_commits commits" '
+
+ (
+ nr=0 &&
+ while [ $nr -lt $number_of_commits ]; do
+ nr=$(($nr+1)) &&
+ test_tick &&
+ cat <<INPUT_END
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #$nr
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #$nr
+EOF
+
+INPUT_END
+
+ done &&
+ test_tick &&
+ cat <<INPUT_END
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+no notes
+COMMIT
+
+deleteall
+
+INPUT_END
+
+ ) |
+ git fast-import --quiet &&
+ git config core.notesRef refs/notes/commits
+'
+
+test_sha1_based () {
+ (
+ start_note_commit &&
+ nr=$number_of_commits &&
+ git rev-list refs/heads/master |
+ while read sha1; do
+ note_path=$(echo "$sha1" | sed "$1")
+ cat <<INPUT_END &&
+M 100644 inline $note_path
+data <<EOF
+note for commit #$nr
+EOF
+
+INPUT_END
+
+ nr=$(($nr-1))
+ done
+ ) |
+ git fast-import --quiet
+}
+
+test_expect_success 'test notes in 2/38-fanout' 'test_sha1_based "s|^..|&/|"'
+test_expect_success 'verify notes in 2/38-fanout' 'verify_notes'
+
+test_expect_success 'test notes in 2/2/36-fanout' 'test_sha1_based "s|^\(..\)\(..\)|\1/\2/|"'
+test_expect_success 'verify notes in 2/2/36-fanout' 'verify_notes'
+
+test_expect_success 'test notes in 2/2/2/34-fanout' 'test_sha1_based "s|^\(..\)\(..\)\(..\)|\1/\2/\3/|"'
+test_expect_success 'verify notes in 2/2/2/34-fanout' 'verify_notes'
+
+test_same_notes () {
+ (
+ start_note_commit &&
+ nr=$number_of_commits &&
+ git rev-list refs/heads/master |
+ while read sha1; do
+ first_note_path=$(echo "$sha1" | sed "$1")
+ second_note_path=$(echo "$sha1" | sed "$2")
+ cat <<INPUT_END &&
+M 100644 inline $second_note_path
+data <<EOF
+note for commit #$nr
+EOF
+
+M 100644 inline $first_note_path
+data <<EOF
+note for commit #$nr
+EOF
+
+INPUT_END
+
+ nr=$(($nr-1))
+ done
+ ) |
+ git fast-import --quiet
+}
+
+test_expect_success 'test same notes in no fanout and 2/38-fanout' 'test_same_notes "s|^..|&/|" ""'
+test_expect_success 'verify same notes in no fanout and 2/38-fanout' 'verify_notes'
+
+test_expect_success 'test same notes in no fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" ""'
+test_expect_success 'verify same notes in no fanout and 2/2/36-fanout' 'verify_notes'
+
+test_expect_success 'test same notes in 2/38-fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^..|&/|"'
+test_expect_success 'verify same notes in 2/38-fanout and 2/2/36-fanout' 'verify_notes'
+
+test_expect_success 'test same notes in 2/2/2/34-fanout and 2/2/36-fanout' 'test_same_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^\(..\)\(..\)\(..\)|\1/\2/\3/|"'
+test_expect_success 'verify same notes in 2/2/2/34-fanout and 2/2/36-fanout' 'verify_notes'
+
+test_concatenated_notes () {
+ (
+ start_note_commit &&
+ nr=$number_of_commits &&
+ git rev-list refs/heads/master |
+ while read sha1; do
+ first_note_path=$(echo "$sha1" | sed "$1")
+ second_note_path=$(echo "$sha1" | sed "$2")
+ cat <<INPUT_END &&
+M 100644 inline $second_note_path
+data <<EOF
+second note for commit #$nr
+EOF
+
+M 100644 inline $first_note_path
+data <<EOF
+first note for commit #$nr
+EOF
+
+INPUT_END
+
+ nr=$(($nr-1))
+ done
+ ) |
+ git fast-import --quiet
+}
+
+verify_concatenated_notes () {
+ git log | grep "^ " > output &&
+ i=$number_of_commits &&
+ while [ $i -gt 0 ]; do
+ echo " commit #$i" &&
+ echo " first note for commit #$i" &&
+ echo " second note for commit #$i" &&
+ i=$(($i-1));
+ done > expect &&
+ test_cmp expect output
+}
+
+test_expect_success 'test notes in no fanout concatenated with 2/38-fanout' 'test_concatenated_notes "s|^..|&/|" ""'
+test_expect_success 'verify notes in no fanout concatenated with 2/38-fanout' 'verify_concatenated_notes'
+
+test_expect_success 'test notes in no fanout concatenated with 2/2/36-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)|\1/\2/|" ""'
+test_expect_success 'verify notes in no fanout concatenated with 2/2/36-fanout' 'verify_concatenated_notes'
+
+test_expect_success 'test notes in 2/38-fanout concatenated with 2/2/36-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)|\1/\2/|" "s|^..|&/|"'
+test_expect_success 'verify notes in 2/38-fanout concatenated with 2/2/36-fanout' 'verify_concatenated_notes'
+
+test_expect_success 'test notes in 2/2/36-fanout concatenated with 2/2/2/34-fanout' 'test_concatenated_notes "s|^\(..\)\(..\)\(..\)|\1/\2/\3/|" "s|^\(..\)\(..\)|\1/\2/|"'
+test_expect_success 'verify notes in 2/2/36-fanout concatenated with 2/2/2/34-fanout' 'verify_concatenated_notes'
+
+test_done
diff --git a/t/t3304-notes-mixed.sh b/t/t3304-notes-mixed.sh
new file mode 100755
index 0000000..1709e8c
--- /dev/null
+++ b/t/t3304-notes-mixed.sh
@@ -0,0 +1,206 @@
+#!/bin/sh
+
+test_description='Test notes trees that also contain non-notes'
+
+. ./test-lib.sh
+
+number_of_commits=100
+
+start_note_commit () {
+ test_tick &&
+ cat <<INPUT_END
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes
+COMMIT
+
+from refs/notes/commits^0
+deleteall
+INPUT_END
+
+}
+
+verify_notes () {
+ git log | grep "^ " > output &&
+ i=$number_of_commits &&
+ while [ $i -gt 0 ]; do
+ echo " commit #$i" &&
+ echo " note for commit #$i" &&
+ i=$(($i-1));
+ done > expect &&
+ test_cmp expect output
+}
+
+test_expect_success "setup: create a couple of commits" '
+
+ test_tick &&
+ cat <<INPUT_END >input &&
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #1
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #1
+EOF
+
+INPUT_END
+
+ test_tick &&
+ cat <<INPUT_END >>input &&
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #2
+COMMIT
+
+M 644 inline file
+data <<EOF
+file in commit #2
+EOF
+
+INPUT_END
+ git fast-import --quiet <input
+'
+
+test_expect_success "create a notes tree with both notes and non-notes" '
+
+ commit1=$(git rev-parse refs/heads/master^) &&
+ commit2=$(git rev-parse refs/heads/master) &&
+ test_tick &&
+ cat <<INPUT_END >input &&
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes commit #1
+COMMIT
+
+N inline $commit1
+data <<EOF
+note for commit #1
+EOF
+
+N inline $commit2
+data <<EOF
+note for commit #2
+EOF
+
+INPUT_END
+ test_tick &&
+ cat <<INPUT_END >>input &&
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes commit #2
+COMMIT
+
+M 644 inline foobar/non-note.txt
+data <<EOF
+A non-note in a notes tree
+EOF
+
+N inline $commit2
+data <<EOF
+edited note for commit #2
+EOF
+
+INPUT_END
+ test_tick &&
+ cat <<INPUT_END >>input &&
+commit refs/notes/commits
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes commit #3
+COMMIT
+
+N inline $commit1
+data <<EOF
+edited note for commit #1
+EOF
+
+M 644 inline deadbeef
+data <<EOF
+non-note with SHA1-like name
+EOF
+
+M 644 inline de/adbeef
+data <<EOF
+another non-note with SHA1-like name
+EOF
+
+M 644 inline de/adbeefdeadbeefdeadbeefdeadbeefdeadbeef
+data <<EOF
+This is actually a valid note, albeit to a non-existing object.
+It is needed in order to trigger the "mishandling" of the dead/beef non-note.
+EOF
+
+M 644 inline dead/beef
+data <<EOF
+yet another non-note with SHA1-like name
+EOF
+
+INPUT_END
+ git fast-import --quiet <input &&
+ git config core.notesRef refs/notes/commits
+'
+
+cat >expect <<EXPECT_END
+ commit #2
+ edited note for commit #2
+ commit #1
+ edited note for commit #1
+EXPECT_END
+
+test_expect_success "verify contents of notes" '
+
+ git log | grep "^ " > actual &&
+ test_cmp expect actual
+'
+
+cat >expect_nn1 <<EXPECT_END
+A non-note in a notes tree
+EXPECT_END
+cat >expect_nn2 <<EXPECT_END
+non-note with SHA1-like name
+EXPECT_END
+cat >expect_nn3 <<EXPECT_END
+another non-note with SHA1-like name
+EXPECT_END
+cat >expect_nn4 <<EXPECT_END
+yet another non-note with SHA1-like name
+EXPECT_END
+
+test_expect_success "verify contents of non-notes" '
+
+ git cat-file -p refs/notes/commits:foobar/non-note.txt > actual_nn1 &&
+ test_cmp expect_nn1 actual_nn1 &&
+ git cat-file -p refs/notes/commits:deadbeef > actual_nn2 &&
+ test_cmp expect_nn2 actual_nn2 &&
+ git cat-file -p refs/notes/commits:de/adbeef > actual_nn3 &&
+ test_cmp expect_nn3 actual_nn3 &&
+ git cat-file -p refs/notes/commits:dead/beef > actual_nn4 &&
+ test_cmp expect_nn4 actual_nn4
+'
+
+test_expect_success "git-notes preserves non-notes" '
+
+ test_tick &&
+ git notes add -f -m "foo bar"
+'
+
+test_expect_success "verify contents of non-notes after git-notes" '
+
+ git cat-file -p refs/notes/commits:foobar/non-note.txt > actual_nn1 &&
+ test_cmp expect_nn1 actual_nn1 &&
+ git cat-file -p refs/notes/commits:deadbeef > actual_nn2 &&
+ test_cmp expect_nn2 actual_nn2 &&
+ git cat-file -p refs/notes/commits:de/adbeef > actual_nn3 &&
+ test_cmp expect_nn3 actual_nn3 &&
+ git cat-file -p refs/notes/commits:dead/beef > actual_nn4 &&
+ test_cmp expect_nn4 actual_nn4
+'
+
+test_done
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
new file mode 100755
index 0000000..b1ea64b
--- /dev/null
+++ b/t/t3305-notes-fanout.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+
+test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
+
+. ./test-lib.sh
+
+test_expect_success 'creating many notes with git-notes' '
+ num_notes=300 &&
+ i=0 &&
+ while test $i -lt $num_notes
+ do
+ i=$(($i + 1)) &&
+ test_tick &&
+ echo "file for commit #$i" > file &&
+ git add file &&
+ git commit -q -m "commit #$i" &&
+ git notes add -m "note #$i" || return 1
+ done
+'
+
+test_expect_success 'many notes created correctly with git-notes' '
+ git log | grep "^ " > output &&
+ i=300 &&
+ while test $i -gt 0
+ do
+ echo " commit #$i" &&
+ echo " note #$i" &&
+ i=$(($i - 1));
+ done > expect &&
+ test_cmp expect output
+'
+
+test_expect_success 'many notes created with git-notes triggers fanout' '
+ # Expect entire notes tree to have a fanout == 1
+ git ls-tree -r --name-only refs/notes/commits |
+ while read path
+ do
+ case "$path" in
+ ??/??????????????????????????????????????)
+ : true
+ ;;
+ *)
+ echo "Invalid path \"$path\"" &&
+ return 1
+ ;;
+ esac
+ done
+'
+
+test_expect_success 'deleting most notes with git-notes' '
+ num_notes=250 &&
+ i=0 &&
+ git rev-list HEAD |
+ while read sha1
+ do
+ i=$(($i + 1)) &&
+ if test $i -gt $num_notes
+ then
+ break
+ fi &&
+ test_tick &&
+ git notes remove "$sha1"
+ done
+'
+
+test_expect_success 'most notes deleted correctly with git-notes' '
+ git log HEAD~250 | grep "^ " > output &&
+ i=50 &&
+ while test $i -gt 0
+ do
+ echo " commit #$i" &&
+ echo " note #$i" &&
+ i=$(($i - 1));
+ done > expect &&
+ test_cmp expect output
+'
+
+test_expect_success 'deleting most notes triggers fanout consolidation' '
+ # Expect entire notes tree to have a fanout == 0
+ git ls-tree -r --name-only refs/notes/commits |
+ while read path
+ do
+ case "$path" in
+ ????????????????????????????????????????)
+ : true
+ ;;
+ *)
+ echo "Invalid path \"$path\"" &&
+ return 1
+ ;;
+ esac
+ done
+'
+
+test_done
diff --git a/t/t3306-notes-prune.sh b/t/t3306-notes-prune.sh
new file mode 100755
index 0000000..b455404
--- /dev/null
+++ b/t/t3306-notes-prune.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+
+test_description='Test git notes prune'
+
+. ./test-lib.sh
+
+test_expect_success 'setup: create a few commits with notes' '
+
+ : > file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m 1st &&
+ git notes add -m "Note #1" &&
+ : > file2 &&
+ git add file2 &&
+ test_tick &&
+ git commit -m 2nd &&
+ git notes add -m "Note #2" &&
+ : > file3 &&
+ git add file3 &&
+ test_tick &&
+ git commit -m 3rd &&
+ git notes add -m "Note #3"
+'
+
+cat > expect <<END_OF_LOG
+commit 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:15:13 2005 -0700
+
+ 3rd
+
+Notes:
+ Note #3
+
+commit 08341ad9e94faa089d60fd3f523affb25c6da189
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:14:13 2005 -0700
+
+ 2nd
+
+Notes:
+ Note #2
+
+commit ab5f302035f2e7aaf04265f08b42034c23256e1f
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:13:13 2005 -0700
+
+ 1st
+
+Notes:
+ Note #1
+END_OF_LOG
+
+test_expect_success 'verify commits and notes' '
+
+ git log > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'remove some commits' '
+
+ git reset --hard HEAD~1 &&
+ git reflog expire --expire=now HEAD &&
+ git gc --prune=now
+'
+
+test_expect_success 'verify that commits are gone' '
+
+ ! git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+ git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_expect_success 'verify that notes are still present' '
+
+ git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+ git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_expect_success 'prune -n does not remove notes' '
+
+ git notes list > expect &&
+ git notes prune -n &&
+ git notes list > actual &&
+ test_cmp expect actual
+'
+
+cat > expect <<EOF
+5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29
+EOF
+
+test_expect_success 'prune -n lists prunable notes' '
+
+
+ git notes prune -n > actual &&
+ test_cmp expect actual
+'
+
+
+test_expect_success 'prune notes' '
+
+ git notes prune
+'
+
+test_expect_success 'verify that notes are gone' '
+
+ ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+ git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_expect_success 'remove some commits' '
+
+ git reset --hard HEAD~1 &&
+ git reflog expire --expire=now HEAD &&
+ git gc --prune=now
+'
+
+cat > expect <<EOF
+08341ad9e94faa089d60fd3f523affb25c6da189
+EOF
+
+test_expect_success 'prune -v notes' '
+
+ git notes prune -v > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'verify that notes are gone' '
+
+ ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ ! git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+ git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+'
+
+test_done
diff --git a/t/t3307-notes-man.sh b/t/t3307-notes-man.sh
new file mode 100755
index 0000000..3269f2e
--- /dev/null
+++ b/t/t3307-notes-man.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='Examples from the git-notes man page
+
+Make sure the manual is not full of lies.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit A &&
+ test_commit B &&
+ test_commit C
+'
+
+test_expect_success 'example 1: notes to add an Acked-by line' '
+ cat <<-\EOF >expect &&
+ B
+
+ Notes:
+ Acked-by: A C Ker <acker@example.com>
+ EOF
+ git notes add -m "Acked-by: A C Ker <acker@example.com>" B &&
+ git show -s B^{commit} >log &&
+ tail -n 4 log >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'example 2: binary notes' '
+ cp "$TEST_DIRECTORY"/test4012.png .
+ git checkout B &&
+ blob=$(git hash-object -w test4012.png) &&
+ git notes --ref=logo add -C "$blob" &&
+ git notes --ref=logo copy B C &&
+ git notes --ref=logo show C >actual &&
+ test_cmp test4012.png actual
+'
+
+test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
new file mode 100755
index 0000000..d98c7b5
--- /dev/null
+++ b/t/t3400-rebase.sh
@@ -0,0 +1,187 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Amos Waterland
+#
+
+test_description='git rebase assorted tests
+
+This test runs git rebase and checks that the author information is not lost
+among other things.
+'
+. ./test-lib.sh
+
+GIT_AUTHOR_NAME=author@name
+GIT_AUTHOR_EMAIL=bogus@email@address
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+
+test_expect_success \
+ 'prepare repository with topic branches' \
+ 'git config core.logAllRefUpdates true &&
+ echo First > A &&
+ git update-index --add A &&
+ git commit -m "Add A." &&
+ git checkout -b my-topic-branch &&
+ echo Second > B &&
+ git update-index --add B &&
+ git commit -m "Add B." &&
+ git checkout -f master &&
+ echo Third >> A &&
+ git update-index A &&
+ git commit -m "Modify A." &&
+ git checkout -b side my-topic-branch &&
+ echo Side >> C &&
+ git add C &&
+ git commit -m "Add C" &&
+ git checkout -b nonlinear my-topic-branch &&
+ echo Edit >> B &&
+ git add B &&
+ git commit -m "Modify B" &&
+ git merge side &&
+ git checkout -b upstream-merged-nonlinear &&
+ git merge master &&
+ git checkout -f my-topic-branch &&
+ git tag topic
+'
+
+test_expect_success 'rebase on dirty worktree' '
+ echo dirty >> A &&
+ test_must_fail git rebase master'
+
+test_expect_success 'rebase on dirty cache' '
+ git add A &&
+ test_must_fail git rebase master'
+
+test_expect_success 'rebase against master' '
+ git reset --hard HEAD &&
+ git rebase master'
+
+test_expect_success 'rebase against master twice' '
+ git rebase master >out &&
+ grep "Current branch my-topic-branch is up to date" out
+'
+
+test_expect_success 'rebase against master twice with --force' '
+ git rebase --force-rebase master >out &&
+ grep "Current branch my-topic-branch is up to date, rebase forced" out
+'
+
+test_expect_success 'rebase against master twice from another branch' '
+ git checkout my-topic-branch^ &&
+ git rebase master my-topic-branch >out &&
+ grep "Current branch my-topic-branch is up to date" out
+'
+
+test_expect_success 'rebase fast-forward to master' '
+ git checkout my-topic-branch^ &&
+ git rebase my-topic-branch >out &&
+ grep "Fast-forwarded HEAD to my-topic-branch" out
+'
+
+test_expect_success \
+ 'the rebase operation should not have destroyed author information' \
+ '! (git log | grep "Author:" | grep "<>")'
+
+test_expect_success \
+ 'the rebase operation should not have destroyed author information (2)' \
+ "git log -1 | grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'"
+
+test_expect_success 'HEAD was detached during rebase' '
+ test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
+'
+
+test_expect_success 'rebase after merge master' '
+ git reset --hard topic &&
+ git merge master &&
+ git rebase master &&
+ ! (git show | grep "^Merge:")
+'
+
+test_expect_success 'rebase of history with merges is linearized' '
+ git checkout nonlinear &&
+ test 4 = $(git rev-list master.. | wc -l) &&
+ git rebase master &&
+ test 3 = $(git rev-list master.. | wc -l)
+'
+
+test_expect_success \
+ 'rebase of history with merges after upstream merge is linearized' '
+ git checkout upstream-merged-nonlinear &&
+ test 5 = $(git rev-list master.. | wc -l) &&
+ git rebase master &&
+ test 3 = $(git rev-list master.. | wc -l)
+'
+
+test_expect_success 'rebase a single mode change' '
+ git checkout master &&
+ echo 1 > X &&
+ git add X &&
+ test_tick &&
+ git commit -m prepare &&
+ git checkout -b modechange HEAD^ &&
+ echo 1 > X &&
+ git add X &&
+ test_chmod +x A &&
+ test_tick &&
+ git commit -m modechange &&
+ GIT_TRACE=1 git rebase master
+'
+
+test_expect_success 'Show verbose error when HEAD could not be detached' '
+ : > B &&
+ test_must_fail git rebase topic 2> output.err > output.out &&
+ grep "Untracked working tree file .B. would be overwritten" output.err
+'
+rm -f B
+
+test_expect_success 'dump usage when upstream arg is missing' '
+ git checkout -b usage topic &&
+ test_must_fail git rebase 2>error1 &&
+ grep "[Uu]sage" error1 &&
+ test_must_fail git rebase --abort 2>error2 &&
+ grep "No rebase in progress" error2 &&
+ test_must_fail git rebase --onto master 2>error3 &&
+ grep "[Uu]sage" error3 &&
+ ! grep "can.t shift" error3
+'
+
+test_expect_success 'rebase -q is quiet' '
+ git checkout -b quiet topic &&
+ git rebase -q master > output.out 2>&1 &&
+ test ! -s output.out
+'
+
+test_expect_success 'Rebase a commit that sprinkles CRs in' '
+ (
+ echo "One"
+ echo "TwoQ"
+ echo "Three"
+ echo "FQur"
+ echo "Five"
+ ) | q_to_cr >CR &&
+ git add CR &&
+ test_tick &&
+ git commit -a -m "A file with a line with CR" &&
+ git tag file-with-cr &&
+ git checkout HEAD^0 &&
+ git rebase --onto HEAD^^ HEAD^ &&
+ git diff --exit-code file-with-cr:CR HEAD:CR
+'
+
+test_expect_success 'rebase can copy notes' '
+ git config notes.rewrite.rebase true &&
+ git config notes.rewriteRef "refs/notes/*" &&
+ test_commit n1 &&
+ test_commit n2 &&
+ test_commit n3 &&
+ git notes add -m"a note" n3 &&
+ git rebase --onto n1 n2 &&
+ test "a note" = "$(git notes show HEAD)"
+'
+
+test_expect_success 'rebase -m can copy notes' '
+ git reset --hard n3 &&
+ git rebase -m --onto n1 n2 &&
+ test "a note" = "$(git notes show HEAD)"
+'
+
+test_done
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
new file mode 100755
index 0000000..aea6685
--- /dev/null
+++ b/t/t3401-rebase-partial.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Yann Dirson, based on t3400 by Amos Waterland
+#
+
+test_description='git rebase should detect patches integrated upstream
+
+This test cherry-picks one local change of two into master branch, and
+checks that git rebase succeeds with only the second patch in the
+local branch.
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'prepare repository with topic branch' \
+ 'echo First > A &&
+ git update-index --add A &&
+ git commit -m "Add A." &&
+
+ git checkout -b my-topic-branch &&
+
+ echo Second > B &&
+ git update-index --add B &&
+ git commit -m "Add B." &&
+
+ echo AnotherSecond > C &&
+ git update-index --add C &&
+ git commit -m "Add C." &&
+
+ git checkout -f master &&
+
+ echo Third >> A &&
+ git update-index A &&
+ git commit -m "Modify A."
+'
+
+test_expect_success \
+ 'pick top patch from topic branch into master' \
+ 'git cherry-pick my-topic-branch^0 &&
+ git checkout -f my-topic-branch &&
+ git branch master-merge master &&
+ git branch my-topic-branch-merge my-topic-branch
+'
+
+test_debug \
+ 'git cherry master &&
+ git format-patch -k --stdout --full-index master >/dev/null &&
+ gitk --all & sleep 1
+'
+
+test_expect_success \
+ 'rebase topic branch against new master and check git am did not get halted' \
+ 'git rebase master && test ! -d .git/rebase-apply'
+
+test_expect_success \
+ 'rebase --merge topic branch that was partially merged upstream' \
+ 'git checkout -f my-topic-branch-merge &&
+ git rebase --merge master-merge &&
+ test ! -d .git/rebase-merge'
+
+test_done
diff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh
new file mode 100755
index 0000000..7b7d072
--- /dev/null
+++ b/t/t3402-rebase-merge.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git rebase --merge test'
+
+. ./test-lib.sh
+
+T="A quick brown fox
+jumps over the lazy dog."
+for i in 1 2 3 4 5 6 7 8 9 10
+do
+ echo "$i $T"
+done >original
+
+test_expect_success setup '
+ git add original &&
+ git commit -m"initial" &&
+ git branch side &&
+ echo "11 $T" >>original &&
+ git commit -a -m"master updates a bit." &&
+
+ echo "12 $T" >>original &&
+ git commit -a -m"master updates a bit more." &&
+
+ git checkout side &&
+ (echo "0 $T" ; cat original) >renamed &&
+ git add renamed &&
+ git update-index --force-remove original &&
+ git commit -a -m"side renames and edits." &&
+
+ tr "[a-z]" "[A-Z]" <original >newfile &&
+ git add newfile &&
+ git commit -a -m"side edits further." &&
+
+ tr "[a-m]" "[A-M]" <original >newfile &&
+ rm -f original &&
+ git commit -a -m"side edits once again." &&
+
+ git branch test-rebase side &&
+ git branch test-rebase-pick side &&
+ git branch test-reference-pick side &&
+ git checkout -b test-merge side
+'
+
+test_expect_success 'reference merge' '
+ git merge -s recursive "reference merge" HEAD master
+'
+
+PRE_REBASE=$(git rev-parse test-rebase)
+test_expect_success rebase '
+ git checkout test-rebase &&
+ GIT_TRACE=1 git rebase --merge master
+'
+
+test_expect_success 'test-rebase@{1} is pre rebase' '
+ test $PRE_REBASE = $(git rev-parse test-rebase@{1})
+'
+
+test_expect_success 'merge and rebase should match' '
+ git diff-tree -r test-rebase test-merge >difference &&
+ if test -s difference
+ then
+ cat difference
+ (exit 1)
+ else
+ echo happy
+ fi
+'
+
+test_expect_success 'rebase the other way' '
+ git reset --hard master &&
+ git rebase --merge side
+'
+
+test_expect_success 'merge and rebase should match' '
+ git diff-tree -r test-rebase test-merge >difference &&
+ if test -s difference
+ then
+ cat difference
+ (exit 1)
+ else
+ echo happy
+ fi
+'
+
+test_expect_success 'picking rebase' '
+ git reset --hard side &&
+ git rebase --merge --onto master side^^ &&
+ mb=$(git merge-base master HEAD) &&
+ if test "$mb" = "$(git rev-parse master)"
+ then
+ echo happy
+ else
+ git show-branch
+ (exit 1)
+ fi &&
+ f=$(git diff-tree --name-only HEAD^ HEAD) &&
+ g=$(git diff-tree --name-only HEAD^^ HEAD^) &&
+ case "$f,$g" in
+ newfile,newfile)
+ echo happy ;;
+ *)
+ echo "$f"
+ echo "$g"
+ (exit 1)
+ esac
+'
+
+test_done
diff --git a/t/t3403-rebase-skip.sh b/t/t3403-rebase-skip.sh
new file mode 100755
index 0000000..64446e3
--- /dev/null
+++ b/t/t3403-rebase-skip.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='git rebase --merge --skip tests'
+
+. ./test-lib.sh
+
+# we assume the default git am -3 --skip strategy is tested independently
+# and always works :)
+
+test_expect_success setup '
+ echo hello > hello &&
+ git add hello &&
+ git commit -m "hello" &&
+ git branch skip-reference &&
+
+ echo world >> hello &&
+ git commit -a -m "hello world" &&
+ echo goodbye >> hello &&
+ git commit -a -m "goodbye" &&
+
+ git checkout -f skip-reference &&
+ echo moo > hello &&
+ git commit -a -m "we should skip this" &&
+ echo moo > cow &&
+ git add cow &&
+ git commit -m "this should not be skipped" &&
+ git branch pre-rebase skip-reference &&
+ git branch skip-merge skip-reference
+ '
+
+test_expect_success 'rebase with git am -3 (default)' '
+ test_must_fail git rebase master
+'
+
+test_expect_success 'rebase --skip with am -3' '
+ git rebase --skip
+ '
+
+test_expect_success 'rebase moves back to skip-reference' '
+ test refs/heads/skip-reference = $(git symbolic-ref HEAD) &&
+ git branch post-rebase &&
+ git reset --hard pre-rebase &&
+ test_must_fail git rebase master &&
+ echo "hello" > hello &&
+ git add hello &&
+ git rebase --continue &&
+ test refs/heads/skip-reference = $(git symbolic-ref HEAD) &&
+ git reset --hard post-rebase
+'
+
+test_expect_success 'checkout skip-merge' 'git checkout -f skip-merge'
+
+test_expect_success 'rebase with --merge' '
+ test_must_fail git rebase --merge master
+'
+
+test_expect_success 'rebase --skip with --merge' '
+ git rebase --skip
+ '
+
+test_expect_success 'merge and reference trees equal' \
+ 'test -z "`git diff-tree skip-merge skip-reference`"'
+
+test_expect_success 'moved back to branch correctly' '
+ test refs/heads/skip-merge = $(git symbolic-ref HEAD)
+'
+
+test_debug 'gitk --all & sleep 1'
+
+test_done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
new file mode 100755
index 0000000..9f03ce6
--- /dev/null
+++ b/t/t3404-rebase-interactive.sh
@@ -0,0 +1,657 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='git rebase interactive
+
+This test runs git rebase "interactively", by faking an edit, and verifies
+that the result still makes sense.
+'
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+# Set up the repository like this:
+#
+# one - two - three - four (conflict-branch)
+# /
+# A - B - C - D - E (master)
+# | \
+# | F - G - H (branch1)
+# | \
+# |\ I (branch2)
+# | \
+# | J - K - L - M (no-conflict-branch)
+# \
+# N - O - P (no-ff-branch)
+#
+# where A, B, D and G all touch file1, and one, two, three, four all
+# touch file "conflict".
+#
+# WARNING: Modifications to the initial repository can change the SHA ID used
+# in the expect2 file for the 'stop on conflicting pick' test.
+
+
+test_expect_success 'setup' '
+ test_commit A file1 &&
+ test_commit B file1 &&
+ test_commit C file2 &&
+ test_commit D file1 &&
+ test_commit E file3 &&
+ git checkout -b branch1 A &&
+ test_commit F file4 &&
+ test_commit G file1 &&
+ test_commit H file5 &&
+ git checkout -b branch2 F &&
+ test_commit I file6
+ git checkout -b conflict-branch A &&
+ for n in one two three four
+ do
+ test_commit $n conflict
+ done &&
+ git checkout -b no-conflict-branch A &&
+ for n in J K L M
+ do
+ test_commit $n file$n
+ done &&
+ git checkout -b no-ff-branch A &&
+ for n in N O P
+ do
+ test_commit $n file$n
+ done
+'
+
+test_expect_success 'no changes are a nop' '
+ git checkout branch2 &&
+ git rebase -i F &&
+ test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
+ test $(git rev-parse I) = $(git rev-parse HEAD)
+'
+
+test_expect_success 'test the [branch] option' '
+ git checkout -b dead-end &&
+ git rm file6 &&
+ git commit -m "stop here" &&
+ git rebase -i F branch2 &&
+ test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
+ test $(git rev-parse I) = $(git rev-parse branch2) &&
+ test $(git rev-parse I) = $(git rev-parse HEAD)
+'
+
+test_expect_success 'test --onto <branch>' '
+ git checkout -b test-onto branch2 &&
+ git rebase -i --onto branch1 F &&
+ test "$(git symbolic-ref -q HEAD)" = "refs/heads/test-onto" &&
+ test $(git rev-parse HEAD^) = $(git rev-parse branch1) &&
+ test $(git rev-parse I) = $(git rev-parse branch2)
+'
+
+test_expect_success 'rebase on top of a non-conflicting commit' '
+ git checkout branch1 &&
+ git tag original-branch1 &&
+ git rebase -i branch2 &&
+ test file6 = $(git diff --name-only original-branch1) &&
+ test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
+ test $(git rev-parse I) = $(git rev-parse branch2) &&
+ test $(git rev-parse I) = $(git rev-parse HEAD~2)
+'
+
+test_expect_success 'reflog for the branch shows state before rebase' '
+ test $(git rev-parse branch1@{1}) = $(git rev-parse original-branch1)
+'
+
+test_expect_success 'exchange two commits' '
+ FAKE_LINES="2 1" git rebase -i HEAD~2 &&
+ test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+ test G = $(git cat-file commit HEAD | sed -ne \$p)
+'
+
+cat > expect << EOF
+diff --git a/file1 b/file1
+index f70f10e..fd79235 100644
+--- a/file1
++++ b/file1
+@@ -1 +1 @@
+-A
++G
+EOF
+
+cat > expect2 << EOF
+<<<<<<< HEAD
+D
+=======
+G
+>>>>>>> 5d18e54... G
+EOF
+
+test_expect_success 'stop on conflicting pick' '
+ git tag new-branch1 &&
+ test_must_fail git rebase -i master &&
+ test "$(git rev-parse HEAD~3)" = "$(git rev-parse master)" &&
+ test_cmp expect .git/rebase-merge/patch &&
+ test_cmp expect2 file1 &&
+ test "$(git diff --name-status |
+ sed -n -e "/^U/s/^U[^a-z]*//p")" = file1 &&
+ test 4 = $(grep -v "^#" < .git/rebase-merge/done | wc -l) &&
+ test 0 = $(grep -c "^[^#]" < .git/rebase-merge/git-rebase-todo)
+'
+
+test_expect_success 'abort' '
+ git rebase --abort &&
+ test $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&
+ test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
+ ! test -d .git/rebase-merge
+'
+
+test_expect_success 'abort with error when new base cannot be checked out' '
+ git rm --cached file1 &&
+ git commit -m "remove file in base" &&
+ test_must_fail git rebase -i master > output 2>&1 &&
+ grep "Untracked working tree file .file1. would be overwritten" \
+ output &&
+ ! test -d .git/rebase-merge &&
+ git reset --hard HEAD^
+'
+
+test_expect_success 'retain authorship' '
+ echo A > file7 &&
+ git add file7 &&
+ test_tick &&
+ GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" &&
+ git tag twerp &&
+ git rebase -i --onto master HEAD^ &&
+ git show HEAD | grep "^Author: Twerp Snog"
+'
+
+test_expect_success 'squash' '
+ git reset --hard twerp &&
+ echo B > file7 &&
+ test_tick &&
+ GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 &&
+ echo "******************************" &&
+ FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \
+ git rebase -i --onto master HEAD~2 &&
+ test B = $(cat file7) &&
+ test $(git rev-parse HEAD^) = $(git rev-parse master)
+'
+
+test_expect_success 'retain authorship when squashing' '
+ git show HEAD | grep "^Author: Twerp Snog"
+'
+
+test_expect_success '-p handles "no changes" gracefully' '
+ HEAD=$(git rev-parse HEAD) &&
+ git rebase -i -p HEAD^ &&
+ git update-index --refresh &&
+ git diff-files --quiet &&
+ git diff-index --quiet --cached HEAD -- &&
+ test $HEAD = $(git rev-parse HEAD)
+'
+
+test_expect_failure 'exchange two commits with -p' '
+ FAKE_LINES="2 1" git rebase -i -p HEAD~2 &&
+ test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
+ test G = $(git cat-file commit HEAD | sed -ne \$p)
+'
+
+test_expect_success 'preserve merges with -p' '
+ git checkout -b to-be-preserved master^ &&
+ : > unrelated-file &&
+ git add unrelated-file &&
+ test_tick &&
+ git commit -m "unrelated" &&
+ git checkout -b another-branch master &&
+ echo B > file1 &&
+ test_tick &&
+ git commit -m J file1 &&
+ test_tick &&
+ git merge to-be-preserved &&
+ echo C > file1 &&
+ test_tick &&
+ git commit -m K file1 &&
+ echo D > file1 &&
+ test_tick &&
+ git commit -m L1 file1 &&
+ git checkout HEAD^ &&
+ echo 1 > unrelated-file &&
+ test_tick &&
+ git commit -m L2 unrelated-file &&
+ test_tick &&
+ git merge another-branch &&
+ echo E > file1 &&
+ test_tick &&
+ git commit -m M file1 &&
+ git checkout -b to-be-rebased &&
+ test_tick &&
+ git rebase -i -p --onto branch1 master &&
+ git update-index --refresh &&
+ git diff-files --quiet &&
+ git diff-index --quiet --cached HEAD -- &&
+ test $(git rev-parse HEAD~6) = $(git rev-parse branch1) &&
+ test $(git rev-parse HEAD~4^2) = $(git rev-parse to-be-preserved) &&
+ test $(git rev-parse HEAD^^2^) = $(git rev-parse HEAD^^^) &&
+ test $(git show HEAD~5:file1) = B &&
+ test $(git show HEAD~3:file1) = C &&
+ test $(git show HEAD:file1) = E &&
+ test $(git show HEAD:unrelated-file) = 1
+'
+
+test_expect_success 'edit ancestor with -p' '
+ FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 &&
+ echo 2 > unrelated-file &&
+ test_tick &&
+ git commit -m L2-modified --amend unrelated-file &&
+ git rebase --continue &&
+ git update-index --refresh &&
+ git diff-files --quiet &&
+ git diff-index --quiet --cached HEAD -- &&
+ test $(git show HEAD:unrelated-file) = 2
+'
+
+test_expect_success '--continue tries to commit' '
+ test_tick &&
+ test_must_fail git rebase -i --onto new-branch1 HEAD^ &&
+ echo resolved > file1 &&
+ git add file1 &&
+ FAKE_COMMIT_MESSAGE="chouette!" git rebase --continue &&
+ test $(git rev-parse HEAD^) = $(git rev-parse new-branch1) &&
+ git show HEAD | grep chouette
+'
+
+test_expect_success 'verbose flag is heeded, even after --continue' '
+ git reset --hard HEAD@{1} &&
+ test_tick &&
+ test_must_fail git rebase -v -i --onto new-branch1 HEAD^ &&
+ echo resolved > file1 &&
+ git add file1 &&
+ git rebase --continue > output &&
+ grep "^ file1 | 2 +-$" output
+'
+
+test_expect_success 'multi-squash only fires up editor once' '
+ base=$(git rev-parse HEAD~4) &&
+ FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
+ EXPECT_HEADER_COUNT=4 \
+ git rebase -i $base &&
+ test $base = $(git rev-parse HEAD^) &&
+ test 1 = $(git show | grep ONCE | wc -l)
+'
+
+test_expect_success 'multi-fixup does not fire up editor' '
+ git checkout -b multi-fixup E &&
+ base=$(git rev-parse HEAD~4) &&
+ FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \
+ git rebase -i $base &&
+ test $base = $(git rev-parse HEAD^) &&
+ test 0 = $(git show | grep NEVER | wc -l) &&
+ git checkout to-be-rebased &&
+ git branch -D multi-fixup
+'
+
+test_expect_success 'commit message used after conflict' '
+ git checkout -b conflict-fixup conflict-branch &&
+ base=$(git rev-parse HEAD~4) &&
+ (
+ FAKE_LINES="1 fixup 3 fixup 4" &&
+ export FAKE_LINES &&
+ test_must_fail git rebase -i $base
+ ) &&
+ echo three > conflict &&
+ git add conflict &&
+ FAKE_COMMIT_AMEND="ONCE" EXPECT_HEADER_COUNT=2 \
+ git rebase --continue &&
+ test $base = $(git rev-parse HEAD^) &&
+ test 1 = $(git show | grep ONCE | wc -l) &&
+ git checkout to-be-rebased &&
+ git branch -D conflict-fixup
+'
+
+test_expect_success 'commit message retained after conflict' '
+ git checkout -b conflict-squash conflict-branch &&
+ base=$(git rev-parse HEAD~4) &&
+ (
+ FAKE_LINES="1 fixup 3 squash 4" &&
+ export FAKE_LINES &&
+ test_must_fail git rebase -i $base
+ ) &&
+ echo three > conflict &&
+ git add conflict &&
+ FAKE_COMMIT_AMEND="TWICE" EXPECT_HEADER_COUNT=2 \
+ git rebase --continue &&
+ test $base = $(git rev-parse HEAD^) &&
+ test 2 = $(git show | grep TWICE | wc -l) &&
+ git checkout to-be-rebased &&
+ git branch -D conflict-squash
+'
+
+cat > expect-squash-fixup << EOF
+B
+
+D
+
+ONCE
+EOF
+
+test_expect_success 'squash and fixup generate correct log messages' '
+ git checkout -b squash-fixup E &&
+ base=$(git rev-parse HEAD~4) &&
+ FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \
+ EXPECT_HEADER_COUNT=4 \
+ git rebase -i $base &&
+ git cat-file commit HEAD | sed -e 1,/^\$/d > actual-squash-fixup &&
+ test_cmp expect-squash-fixup actual-squash-fixup &&
+ git checkout to-be-rebased &&
+ git branch -D squash-fixup
+'
+
+test_expect_success 'squash ignores comments' '
+ git checkout -b skip-comments E &&
+ base=$(git rev-parse HEAD~4) &&
+ FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \
+ EXPECT_HEADER_COUNT=4 \
+ git rebase -i $base &&
+ test $base = $(git rev-parse HEAD^) &&
+ test 1 = $(git show | grep ONCE | wc -l) &&
+ git checkout to-be-rebased &&
+ git branch -D skip-comments
+'
+
+test_expect_success 'squash ignores blank lines' '
+ git checkout -b skip-blank-lines E &&
+ base=$(git rev-parse HEAD~4) &&
+ FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \
+ EXPECT_HEADER_COUNT=4 \
+ git rebase -i $base &&
+ test $base = $(git rev-parse HEAD^) &&
+ test 1 = $(git show | grep ONCE | wc -l) &&
+ git checkout to-be-rebased &&
+ git branch -D skip-blank-lines
+'
+
+test_expect_success 'squash works as expected' '
+ git checkout -b squash-works no-conflict-branch &&
+ one=$(git rev-parse HEAD~3) &&
+ FAKE_LINES="1 squash 3 2" EXPECT_HEADER_COUNT=2 \
+ git rebase -i HEAD~3 &&
+ test $one = $(git rev-parse HEAD~2)
+'
+
+test_expect_success 'interrupted squash works as expected' '
+ git checkout -b interrupted-squash conflict-branch &&
+ one=$(git rev-parse HEAD~3) &&
+ (
+ FAKE_LINES="1 squash 3 2" &&
+ export FAKE_LINES &&
+ test_must_fail git rebase -i HEAD~3
+ ) &&
+ (echo one; echo two; echo four) > conflict &&
+ git add conflict &&
+ test_must_fail git rebase --continue &&
+ echo resolved > conflict &&
+ git add conflict &&
+ git rebase --continue &&
+ test $one = $(git rev-parse HEAD~2)
+'
+
+test_expect_success 'interrupted squash works as expected (case 2)' '
+ git checkout -b interrupted-squash2 conflict-branch &&
+ one=$(git rev-parse HEAD~3) &&
+ (
+ FAKE_LINES="3 squash 1 2" &&
+ export FAKE_LINES &&
+ test_must_fail git rebase -i HEAD~3
+ ) &&
+ (echo one; echo four) > conflict &&
+ git add conflict &&
+ test_must_fail git rebase --continue &&
+ (echo one; echo two; echo four) > conflict &&
+ git add conflict &&
+ test_must_fail git rebase --continue &&
+ echo resolved > conflict &&
+ git add conflict &&
+ git rebase --continue &&
+ test $one = $(git rev-parse HEAD~2)
+'
+
+test_expect_success 'ignore patch if in upstream' '
+ HEAD=$(git rev-parse HEAD) &&
+ git checkout -b has-cherry-picked HEAD^ &&
+ echo unrelated > file7 &&
+ git add file7 &&
+ test_tick &&
+ git commit -m "unrelated change" &&
+ git cherry-pick $HEAD &&
+ EXPECT_COUNT=1 git rebase -i $HEAD &&
+ test $HEAD = $(git rev-parse HEAD^)
+'
+
+test_expect_success '--continue tries to commit, even for "edit"' '
+ parent=$(git rev-parse HEAD^) &&
+ test_tick &&
+ FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+ echo edited > file7 &&
+ git add file7 &&
+ FAKE_COMMIT_MESSAGE="chouette!" git rebase --continue &&
+ test edited = $(git show HEAD:file7) &&
+ git show HEAD | grep chouette &&
+ test $parent = $(git rev-parse HEAD^)
+'
+
+test_expect_success 'aborted --continue does not squash commits after "edit"' '
+ old=$(git rev-parse HEAD) &&
+ test_tick &&
+ FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+ echo "edited again" > file7 &&
+ git add file7 &&
+ (
+ FAKE_COMMIT_MESSAGE=" " &&
+ export FAKE_COMMIT_MESSAGE &&
+ test_must_fail git rebase --continue
+ ) &&
+ test $old = $(git rev-parse HEAD) &&
+ git rebase --abort
+'
+
+test_expect_success 'auto-amend only edited commits after "edit"' '
+ test_tick &&
+ FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+ echo "edited again" > file7 &&
+ git add file7 &&
+ FAKE_COMMIT_MESSAGE="edited file7 again" git commit &&
+ echo "and again" > file7 &&
+ git add file7 &&
+ test_tick &&
+ (
+ FAKE_COMMIT_MESSAGE="and again" &&
+ export FAKE_COMMIT_MESSAGE &&
+ test_must_fail git rebase --continue
+ ) &&
+ git rebase --abort
+'
+
+test_expect_success 'rebase a detached HEAD' '
+ grandparent=$(git rev-parse HEAD~2) &&
+ git checkout $(git rev-parse HEAD) &&
+ test_tick &&
+ FAKE_LINES="2 1" git rebase -i HEAD~2 &&
+ test $grandparent = $(git rev-parse HEAD~2)
+'
+
+test_expect_success 'rebase a commit violating pre-commit' '
+
+ mkdir -p .git/hooks &&
+ PRE_COMMIT=.git/hooks/pre-commit &&
+ echo "#!/bin/sh" > $PRE_COMMIT &&
+ echo "test -z \"\$(git diff --cached --check)\"" >> $PRE_COMMIT &&
+ chmod a+x $PRE_COMMIT &&
+ echo "monde! " >> file1 &&
+ test_tick &&
+ test_must_fail git commit -m doesnt-verify file1 &&
+ git commit -m doesnt-verify --no-verify file1 &&
+ test_tick &&
+ FAKE_LINES=2 git rebase -i HEAD~2
+
+'
+
+test_expect_success 'rebase with a file named HEAD in worktree' '
+
+ rm -fr .git/hooks &&
+ git reset --hard &&
+ git checkout -b branch3 A &&
+
+ (
+ GIT_AUTHOR_NAME="Squashed Away" &&
+ export GIT_AUTHOR_NAME &&
+ >HEAD &&
+ git add HEAD &&
+ git commit -m "Add head" &&
+ >BODY &&
+ git add BODY &&
+ git commit -m "Add body"
+ ) &&
+
+ FAKE_LINES="1 squash 2" git rebase -i to-be-rebased &&
+ test "$(git show -s --pretty=format:%an)" = "Squashed Away"
+
+'
+
+test_expect_success 'do "noop" when there is nothing to cherry-pick' '
+
+ git checkout -b branch4 HEAD &&
+ GIT_EDITOR=: git commit --amend \
+ --author="Somebody else <somebody@else.com>"
+ test $(git rev-parse branch3) != $(git rev-parse branch4) &&
+ git rebase -i branch3 &&
+ test $(git rev-parse branch3) = $(git rev-parse branch4)
+
+'
+
+test_expect_success 'submodule rebase setup' '
+ git checkout A &&
+ mkdir sub &&
+ (
+ cd sub && git init && >elif &&
+ git add elif && git commit -m "submodule initial"
+ ) &&
+ echo 1 >file1 &&
+ git add file1 sub
+ test_tick &&
+ git commit -m "One" &&
+ echo 2 >file1 &&
+ test_tick &&
+ git commit -a -m "Two" &&
+ (
+ cd sub && echo 3 >elif &&
+ git commit -a -m "submodule second"
+ ) &&
+ test_tick &&
+ git commit -a -m "Three changes submodule"
+'
+
+test_expect_success 'submodule rebase -i' '
+ FAKE_LINES="1 squash 2 3" git rebase -i A
+'
+
+test_expect_success 'avoid unnecessary reset' '
+ git checkout master &&
+ test-chmtime =123456789 file3 &&
+ git update-index --refresh &&
+ HEAD=$(git rev-parse HEAD) &&
+ git rebase -i HEAD~4 &&
+ test $HEAD = $(git rev-parse HEAD) &&
+ MTIME=$(test-chmtime -v +0 file3 | sed 's/[^0-9].*$//') &&
+ test 123456789 = $MTIME
+'
+
+test_expect_success 'reword' '
+ git checkout -b reword-branch master &&
+ FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" git rebase -i A &&
+ git show HEAD | grep "E changed" &&
+ test $(git rev-parse master) != $(git rev-parse HEAD) &&
+ test $(git rev-parse master^) = $(git rev-parse HEAD^) &&
+ FAKE_LINES="1 2 reword 3 4" FAKE_COMMIT_MESSAGE="D changed" git rebase -i A &&
+ git show HEAD^ | grep "D changed" &&
+ FAKE_LINES="reword 1 2 3 4" FAKE_COMMIT_MESSAGE="B changed" git rebase -i A &&
+ git show HEAD~3 | grep "B changed" &&
+ FAKE_LINES="1 reword 2 3 4" FAKE_COMMIT_MESSAGE="C changed" git rebase -i A &&
+ git show HEAD~2 | grep "C changed"
+'
+
+test_expect_success 'rebase -i can copy notes' '
+ git config notes.rewrite.rebase true &&
+ git config notes.rewriteRef "refs/notes/*" &&
+ test_commit n1 &&
+ test_commit n2 &&
+ test_commit n3 &&
+ git notes add -m"a note" n3 &&
+ git rebase --onto n1 n2 &&
+ test "a note" = "$(git notes show HEAD)"
+'
+
+cat >expect <<EOF
+an earlier note
+a note
+EOF
+
+test_expect_success 'rebase -i can copy notes over a fixup' '
+ git reset --hard n3 &&
+ git notes add -m"an earlier note" n2 &&
+ GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 &&
+ git notes show > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'rebase while detaching HEAD' '
+ git symbolic-ref HEAD &&
+ grandparent=$(git rev-parse HEAD~2) &&
+ test_tick &&
+ FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 &&
+ test $grandparent = $(git rev-parse HEAD~2) &&
+ test_must_fail git symbolic-ref HEAD
+'
+
+test_tick # Ensure that the rebased commits get a different timestamp.
+test_expect_success 'always cherry-pick with --no-ff' '
+ git checkout no-ff-branch &&
+ git tag original-no-ff-branch &&
+ git rebase -i --no-ff A &&
+ touch empty &&
+ for p in 0 1 2
+ do
+ test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&
+ git diff HEAD~$p original-no-ff-branch~$p > out &&
+ test_cmp empty out
+ done &&
+ test $(git rev-parse HEAD~3) = $(git rev-parse original-no-ff-branch~3) &&
+ git diff HEAD~3 original-no-ff-branch~3 > out &&
+ test_cmp empty out
+'
+
+test_expect_success 'set up commits with funny messages' '
+ git checkout -b funny A &&
+ echo >>file1 &&
+ test_tick &&
+ git commit -a -m "end with slash\\" &&
+ echo >>file1 &&
+ test_tick &&
+ git commit -a -m "something (\000) that looks like octal" &&
+ echo >>file1 &&
+ test_tick &&
+ git commit -a -m "something (\n) that looks like a newline" &&
+ echo >>file1 &&
+ test_tick &&
+ git commit -a -m "another commit"
+'
+
+test_expect_success 'rebase-i history with funny messages' '
+ git rev-list A..funny >expect &&
+ test_tick &&
+ FAKE_LINES="1 2 3 4" git rebase -i A &&
+ git rev-list A.. >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t3405-rebase-malformed.sh b/t/t3405-rebase-malformed.sh
new file mode 100755
index 0000000..e5ad67c
--- /dev/null
+++ b/t/t3405-rebase-malformed.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='rebase should not insist on git message convention'
+
+. ./test-lib.sh
+
+cat >F <<\EOF
+This is an example of a commit log message
+that does not conform to git commit convention.
+
+It has two paragraphs, but its first paragraph is not friendly
+to oneline summary format.
+EOF
+
+test_expect_success setup '
+
+ >file1 &&
+ >file2 &&
+ git add file1 file2 &&
+ test_tick &&
+ git commit -m "Initial commit" &&
+
+ git checkout -b side &&
+ cat F >file2 &&
+ git add file2 &&
+ test_tick &&
+ git commit -F F &&
+
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >F0 &&
+
+ git checkout master &&
+
+ echo One >file1 &&
+ test_tick &&
+ git add file1 &&
+ git commit -m "Second commit"
+'
+
+test_expect_success rebase '
+
+ git rebase master side &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >F1 &&
+
+ test_cmp F0 F1 &&
+ test_cmp F F0
+'
+
+test_done
diff --git a/t/t3406-rebase-message.sh b/t/t3406-rebase-message.sh
new file mode 100755
index 0000000..85fc7c4
--- /dev/null
+++ b/t/t3406-rebase-message.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+test_description='messages from rebase operation'
+
+. ./test-lib.sh
+
+quick_one () {
+ echo "$1" >"file$1" &&
+ git add "file$1" &&
+ test_tick &&
+ git commit -m "$1"
+}
+
+test_expect_success setup '
+ quick_one O &&
+ git branch topic &&
+ quick_one X &&
+ quick_one A &&
+ quick_one B &&
+ quick_one Y &&
+
+ git checkout topic &&
+ quick_one A &&
+ quick_one B &&
+ quick_one Z &&
+ git tag start
+
+'
+
+cat >expect <<\EOF
+Already applied: 0001 A
+Already applied: 0002 B
+Committed: 0003 Z
+EOF
+
+test_expect_success 'rebase -m' '
+
+ git rebase -m master >report &&
+ sed -n -e "/^Already applied: /p" \
+ -e "/^Committed: /p" report >actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'rebase --stat' '
+ git reset --hard start
+ git rebase --stat master >diffstat.txt &&
+ grep "^ fileX | *1 +$" diffstat.txt
+'
+
+test_expect_success 'rebase w/config rebase.stat' '
+ git reset --hard start
+ git config rebase.stat true &&
+ git rebase master >diffstat.txt &&
+ grep "^ fileX | *1 +$" diffstat.txt
+'
+
+test_expect_success 'rebase -n overrides config rebase.stat config' '
+ git reset --hard start
+ git config rebase.stat true &&
+ git rebase -n master >diffstat.txt &&
+ ! grep "^ fileX | *1 +$" diffstat.txt
+'
+
+test_done
diff --git a/t/t3407-rebase-abort.sh b/t/t3407-rebase-abort.sh
new file mode 100755
index 0000000..2999e78
--- /dev/null
+++ b/t/t3407-rebase-abort.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='git rebase --abort tests'
+
+. ./test-lib.sh
+
+### Test that we handle space characters properly
+work_dir="$(pwd)/test dir"
+
+test_expect_success setup '
+ mkdir -p "$work_dir" &&
+ cd "$work_dir" &&
+ git init &&
+ echo a > a &&
+ git add a &&
+ git commit -m a &&
+ git branch to-rebase &&
+
+ echo b > a &&
+ git commit -a -m b &&
+ echo c > a &&
+ git commit -a -m c &&
+
+ git checkout to-rebase &&
+ echo d > a &&
+ git commit -a -m "merge should fail on this" &&
+ echo e > a &&
+ git commit -a -m "merge should fail on this, too" &&
+ git branch pre-rebase
+'
+
+testrebase() {
+ type=$1
+ dotest=$2
+
+ test_expect_success "rebase$type --abort" '
+ cd "$work_dir" &&
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase &&
+ test_must_fail git rebase$type master &&
+ test -d "$dotest" &&
+ git rebase --abort &&
+ test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
+ test ! -d "$dotest"
+ '
+
+ test_expect_success "rebase$type --abort after --skip" '
+ cd "$work_dir" &&
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase &&
+ test_must_fail git rebase$type master &&
+ test -d "$dotest" &&
+ test_must_fail git rebase --skip &&
+ test $(git rev-parse HEAD) = $(git rev-parse master) &&
+ git rebase --abort &&
+ test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
+ test ! -d "$dotest"
+ '
+
+ test_expect_success "rebase$type --abort after --continue" '
+ cd "$work_dir" &&
+ # Clean up the state from the previous one
+ git reset --hard pre-rebase &&
+ test_must_fail git rebase$type master &&
+ test -d "$dotest" &&
+ echo c > a &&
+ echo d >> a &&
+ git add a &&
+ test_must_fail git rebase --continue &&
+ test $(git rev-parse HEAD) != $(git rev-parse master) &&
+ git rebase --abort &&
+ test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
+ test ! -d "$dotest"
+ '
+}
+
+testrebase "" .git/rebase-apply
+testrebase " --merge" .git/rebase-merge
+
+test_done
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
new file mode 100755
index 0000000..2062b85
--- /dev/null
+++ b/t/t3408-rebase-multi-line.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='rebasing a commit with multi-line first paragraph.'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+
+ echo hello >file &&
+ test_tick &&
+ git commit -a -m "A sample commit log message that has a long
+summary that spills over multiple lines.
+
+But otherwise with a sane description."
+
+ git branch side &&
+
+ git reset --hard HEAD^ &&
+ >elif &&
+ git add elif &&
+ test_tick &&
+ git commit -m second
+
+'
+
+test_expect_success rebase '
+
+ git checkout side &&
+ git rebase master &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ git cat-file commit side@{1} | sed -e "1,/^\$/d" >expect &&
+ test_cmp expect actual
+
+'
+
+test_done
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
new file mode 100755
index 0000000..8f785e7
--- /dev/null
+++ b/t/t3409-rebase-preserve-merges.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright(C) 2008 Stephen Habermann & Andreas Ericsson
+#
+test_description='git rebase -p should preserve merges
+
+Run "git rebase -p" and check that merges are properly carried along
+'
+. ./test-lib.sh
+
+GIT_AUTHOR_EMAIL=bogus_email_address
+export GIT_AUTHOR_EMAIL
+
+# Clone 1 (trivial merge):
+#
+# A1--A2 <-- origin/master
+# \ \
+# B1--M <-- topic
+# \
+# B2 <-- origin/topic
+#
+# Clone 2 (conflicting merge):
+#
+# A1--A2--B3 <-- origin/master
+# \ \
+# B1------M <-- topic
+# \
+# B2 <-- origin/topic
+#
+# In both cases, 'topic' is rebased onto 'origin/topic'.
+
+test_expect_success 'setup for merge-preserving rebase' \
+ 'echo First > A &&
+ git add A &&
+ git commit -m "Add A1" &&
+ git checkout -b topic &&
+ echo Second > B &&
+ git add B &&
+ git commit -m "Add B1" &&
+ git checkout -f master &&
+ echo Third >> A &&
+ git commit -a -m "Modify A2" &&
+
+ git clone ./. clone1 &&
+ cd clone1 &&
+ git checkout -b topic origin/topic &&
+ git merge origin/master &&
+ cd .. &&
+
+ echo Fifth > B &&
+ git add B &&
+ git commit -m "Add different B" &&
+
+ git clone ./. clone2 &&
+ cd clone2 &&
+ git checkout -b topic origin/topic &&
+ test_must_fail git merge origin/master &&
+ echo Resolved > B &&
+ git add B &&
+ git commit -m "Merge origin/master into topic" &&
+ cd .. &&
+
+ git checkout topic &&
+ echo Fourth >> B &&
+ git commit -a -m "Modify B2"
+'
+
+test_expect_success 'rebase -p fakes interactive rebase' '
+ (
+ cd clone1 &&
+ git fetch &&
+ git rebase -p origin/topic &&
+ test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
+ test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote branch " | wc -l)
+ )
+'
+
+test_expect_success '--continue works after a conflict' '
+ (
+ cd clone2 &&
+ git fetch &&
+ test_must_fail git rebase -p origin/topic &&
+ test 2 = $(git ls-files B | wc -l) &&
+ echo Resolved again > B &&
+ test_must_fail git rebase --continue &&
+ grep "^@@@ " .git/rebase-merge/patch &&
+ git add B &&
+ git rebase --continue &&
+ test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
+ test 1 = $(git rev-list --all --pretty=oneline | grep "Add different" | wc -l) &&
+ test 1 = $(git rev-list --all --pretty=oneline | grep "Merge origin" | wc -l)
+ )
+'
+
+test_done
diff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh
new file mode 100755
index 0000000..c49143a
--- /dev/null
+++ b/t/t3410-rebase-preserve-dropped-merges.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Stephen Haberman
+#
+
+test_description='git rebase preserve merges
+
+This test runs git rebase with preserve merges and ensures commits
+dropped by the --cherry-pick flag have their childrens parents
+rewritten.
+'
+. ./test-lib.sh
+
+# set up two branches like this:
+#
+# A - B - C - D - E
+# \
+# F - G - H
+# \
+# I
+#
+# where B, D and G touch the same file.
+
+test_expect_success 'setup' '
+ test_commit A file1 &&
+ test_commit B file1 1 &&
+ test_commit C file2 &&
+ test_commit D file1 2 &&
+ test_commit E file3 &&
+ git checkout A &&
+ test_commit F file4 &&
+ test_commit G file1 3 &&
+ test_commit H file5 &&
+ git checkout F &&
+ test_commit I file6
+'
+
+# A - B - C - D - E
+# \ \ \
+# F - G - H -- L \ --> L
+# \ | \
+# I -- G2 -- J -- K I -- K
+# G2 = same changes as G
+test_expect_success 'skip same-resolution merges with -p' '
+ git checkout H &&
+ ! git merge E &&
+ test_commit L file1 23 &&
+ git checkout I &&
+ test_commit G2 file1 3 &&
+ ! git merge E &&
+ test_commit J file1 23 &&
+ test_commit K file7 file7 &&
+ git rebase -i -p L &&
+ test $(git rev-parse HEAD^^) = $(git rev-parse L) &&
+ test "23" = "$(cat file1)" &&
+ test "I" = "$(cat file6)" &&
+ test "file7" = "$(cat file7)"
+'
+
+# A - B - C - D - E
+# \ \ \
+# F - G - H -- L2 \ --> L2
+# \ | \
+# I -- G3 --- J2 -- K2 I -- G3 -- K2
+# G2 = different changes as G
+test_expect_success 'keep different-resolution merges with -p' '
+ git checkout H &&
+ ! git merge E &&
+ test_commit L2 file1 23 &&
+ git checkout I &&
+ test_commit G3 file1 4 &&
+ ! git merge E &&
+ test_commit J2 file1 24 &&
+ test_commit K2 file7 file7 &&
+ test_must_fail git rebase -i -p L2 &&
+ echo 234 > file1 &&
+ git add file1 &&
+ git rebase --continue &&
+ test $(git rev-parse HEAD^^^) = $(git rev-parse L2) &&
+ test "234" = "$(cat file1)" &&
+ test "I" = "$(cat file6)" &&
+ test "file7" = "$(cat file7)"
+'
+
+test_done
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
new file mode 100755
index 0000000..14a23cd
--- /dev/null
+++ b/t/t3411-rebase-preserve-around-merges.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Stephen Haberman
+#
+
+test_description='git rebase preserve merges
+
+This test runs git rebase with -p and tries to squash a commit from after
+a merge to before the merge.
+'
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+# set up two branches like this:
+#
+# A1 - B1 - D1 - E1 - F1
+# \ /
+# -- C1 --
+
+test_expect_success 'setup' '
+ test_commit A1 &&
+ test_commit B1 &&
+ test_commit C1 &&
+ git reset --hard B1 &&
+ test_commit D1 &&
+ test_merge E1 C1 &&
+ test_commit F1
+'
+
+# Should result in:
+#
+# A1 - B1 - D2 - E2
+# \ /
+# -- C1 --
+#
+test_expect_success 'squash F1 into D1' '
+ FAKE_LINES="1 squash 3 2" git rebase -i -p B1 &&
+ test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" &&
+ test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" &&
+ git tag E2
+'
+
+# Start with:
+#
+# A1 - B1 - D2 - E2
+# \
+# G1 ---- L1 ---- M1
+# \ /
+# H1 -- J1 -- K1
+# \ /
+# -- I1 --
+#
+# And rebase G1..M1 onto E2
+
+test_expect_success 'rebase two levels of merge' '
+ test_commit G1 &&
+ test_commit H1 &&
+ test_commit I1 &&
+ git checkout -b branch3 H1 &&
+ test_commit J1 &&
+ test_merge K1 I1 &&
+ git checkout -b branch2 G1 &&
+ test_commit L1 &&
+ test_merge M1 K1 &&
+ GIT_EDITOR=: git rebase -i -p E2 &&
+ test "$(git rev-parse HEAD~3)" = "$(git rev-parse E2)" &&
+ test "$(git rev-parse HEAD~2)" = "$(git rev-parse HEAD^2^2~2)" &&
+ test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse HEAD^2^2^1)"
+'
+
+test_done
diff --git a/t/t3412-rebase-root.sh b/t/t3412-rebase-root.sh
new file mode 100755
index 0000000..5869061
--- /dev/null
+++ b/t/t3412-rebase-root.sh
@@ -0,0 +1,280 @@
+#!/bin/sh
+
+test_description='git rebase --root
+
+Tests if git rebase --root --onto <newparent> can rebase the root commit.
+'
+. ./test-lib.sh
+
+log_with_names () {
+ git rev-list --topo-order --parents --pretty="tformat:%s" HEAD |
+ git name-rev --stdin --name-only --refs=refs/heads/$1
+}
+
+
+test_expect_success 'prepare repository' '
+ test_commit 1 A &&
+ test_commit 2 A &&
+ git symbolic-ref HEAD refs/heads/other &&
+ rm .git/index &&
+ test_commit 3 B &&
+ test_commit 1b A 1 &&
+ test_commit 4 B
+'
+
+test_expect_success 'rebase --root expects --onto' '
+ test_must_fail git rebase --root
+'
+
+test_expect_success 'setup pre-rebase hook' '
+ mkdir -p .git/hooks &&
+ cat >.git/hooks/pre-rebase <<EOF &&
+#!$SHELL_PATH
+echo "\$1,\$2" >.git/PRE-REBASE-INPUT
+EOF
+ chmod +x .git/hooks/pre-rebase
+'
+cat > expect <<EOF
+4
+3
+2
+1
+EOF
+
+test_expect_success 'rebase --root --onto <newbase>' '
+ git checkout -b work &&
+ git rebase --root --onto master &&
+ git log --pretty=tformat:"%s" > rebased &&
+ test_cmp expect rebased
+'
+
+test_expect_success 'pre-rebase got correct input (1)' '
+ test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,
+'
+
+test_expect_success 'rebase --root --onto <newbase> <branch>' '
+ git branch work2 other &&
+ git rebase --root --onto master work2 &&
+ git log --pretty=tformat:"%s" > rebased2 &&
+ test_cmp expect rebased2
+'
+
+test_expect_success 'pre-rebase got correct input (2)' '
+ test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work2
+'
+
+test_expect_success 'rebase -i --root --onto <newbase>' '
+ git checkout -b work3 other &&
+ git rebase -i --root --onto master &&
+ git log --pretty=tformat:"%s" > rebased3 &&
+ test_cmp expect rebased3
+'
+
+test_expect_success 'pre-rebase got correct input (3)' '
+ test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,
+'
+
+test_expect_success 'rebase -i --root --onto <newbase> <branch>' '
+ git branch work4 other &&
+ git rebase -i --root --onto master work4 &&
+ git log --pretty=tformat:"%s" > rebased4 &&
+ test_cmp expect rebased4
+'
+
+test_expect_success 'pre-rebase got correct input (4)' '
+ test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,work4
+'
+
+test_expect_success 'rebase -i -p with linear history' '
+ git checkout -b work5 other &&
+ git rebase -i -p --root --onto master &&
+ git log --pretty=tformat:"%s" > rebased5 &&
+ test_cmp expect rebased5
+'
+
+test_expect_success 'pre-rebase got correct input (5)' '
+ test "z$(cat .git/PRE-REBASE-INPUT)" = z--root,
+'
+
+test_expect_success 'set up merge history' '
+ git checkout other^ &&
+ git checkout -b side &&
+ test_commit 5 C &&
+ git checkout other &&
+ git merge side
+'
+
+cat > expect-side <<'EOF'
+commit work6 work6~1 work6^2
+Merge branch 'side' into other
+commit work6^2 work6~2
+5
+commit work6~1 work6~2
+4
+commit work6~2 work6~3
+3
+commit work6~3 work6~4
+2
+commit work6~4
+1
+EOF
+
+test_expect_success 'rebase -i -p with merge' '
+ git checkout -b work6 other &&
+ git rebase -i -p --root --onto master &&
+ log_with_names work6 > rebased6 &&
+ test_cmp expect-side rebased6
+'
+
+test_expect_success 'set up second root and merge' '
+ git symbolic-ref HEAD refs/heads/third &&
+ rm .git/index &&
+ rm A B C &&
+ test_commit 6 D &&
+ git checkout other &&
+ git merge third
+'
+
+cat > expect-third <<'EOF'
+commit work7 work7~1 work7^2
+Merge branch 'third' into other
+commit work7^2 work7~4
+6
+commit work7~1 work7~2 work7~1^2
+Merge branch 'side' into other
+commit work7~1^2 work7~3
+5
+commit work7~2 work7~3
+4
+commit work7~3 work7~4
+3
+commit work7~4 work7~5
+2
+commit work7~5
+1
+EOF
+
+test_expect_success 'rebase -i -p with two roots' '
+ git checkout -b work7 other &&
+ git rebase -i -p --root --onto master &&
+ log_with_names work7 > rebased7 &&
+ test_cmp expect-third rebased7
+'
+
+test_expect_success 'setup pre-rebase hook that fails' '
+ mkdir -p .git/hooks &&
+ cat >.git/hooks/pre-rebase <<EOF &&
+#!$SHELL_PATH
+false
+EOF
+ chmod +x .git/hooks/pre-rebase
+'
+
+test_expect_success 'pre-rebase hook stops rebase' '
+ git checkout -b stops1 other &&
+ test_must_fail git rebase --root --onto master &&
+ test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1
+ test 0 = $(git rev-list other...stops1 | wc -l)
+'
+
+test_expect_success 'pre-rebase hook stops rebase -i' '
+ git checkout -b stops2 other &&
+ test_must_fail git rebase --root --onto master &&
+ test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2
+ test 0 = $(git rev-list other...stops2 | wc -l)
+'
+
+test_expect_success 'remove pre-rebase hook' '
+ rm -f .git/hooks/pre-rebase
+'
+
+test_expect_success 'set up a conflict' '
+ git checkout master &&
+ echo conflict > B &&
+ git add B &&
+ git commit -m conflict
+'
+
+test_expect_success 'rebase --root with conflict (first part)' '
+ git checkout -b conflict1 other &&
+ test_must_fail git rebase --root --onto master &&
+ git ls-files -u | grep "B$"
+'
+
+test_expect_success 'fix the conflict' '
+ echo 3 > B &&
+ git add B
+'
+
+cat > expect-conflict <<EOF
+6
+5
+4
+3
+conflict
+2
+1
+EOF
+
+test_expect_success 'rebase --root with conflict (second part)' '
+ git rebase --continue &&
+ git log --pretty=tformat:"%s" > conflict1 &&
+ test_cmp expect-conflict conflict1
+'
+
+test_expect_success 'rebase -i --root with conflict (first part)' '
+ git checkout -b conflict2 other &&
+ test_must_fail git rebase -i --root --onto master &&
+ git ls-files -u | grep "B$"
+'
+
+test_expect_success 'fix the conflict' '
+ echo 3 > B &&
+ git add B
+'
+
+test_expect_success 'rebase -i --root with conflict (second part)' '
+ git rebase --continue &&
+ git log --pretty=tformat:"%s" > conflict2 &&
+ test_cmp expect-conflict conflict2
+'
+
+cat >expect-conflict-p <<\EOF
+commit conflict3 conflict3~1 conflict3^2
+Merge branch 'third' into other
+commit conflict3^2 conflict3~4
+6
+commit conflict3~1 conflict3~2 conflict3~1^2
+Merge branch 'side' into other
+commit conflict3~1^2 conflict3~3
+5
+commit conflict3~2 conflict3~3
+4
+commit conflict3~3 conflict3~4
+3
+commit conflict3~4 conflict3~5
+conflict
+commit conflict3~5 conflict3~6
+2
+commit conflict3~6
+1
+EOF
+
+test_expect_success 'rebase -i -p --root with conflict (first part)' '
+ git checkout -b conflict3 other &&
+ test_must_fail git rebase -i -p --root --onto master &&
+ git ls-files -u | grep "B$"
+'
+
+test_expect_success 'fix the conflict' '
+ echo 3 > B &&
+ git add B
+'
+
+test_expect_success 'rebase -i -p --root with conflict (second part)' '
+ git rebase --continue &&
+ log_with_names conflict3 >out &&
+ test_cmp expect-conflict-p out
+'
+
+test_done
diff --git a/t/t3413-rebase-hook.sh b/t/t3413-rebase-hook.sh
new file mode 100755
index 0000000..098b755
--- /dev/null
+++ b/t/t3413-rebase-hook.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+
+test_description='git rebase with its hook(s)'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo hello >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ echo goodbye >file &&
+ git add file &&
+ test_tick &&
+ git commit -m second &&
+ git checkout -b side HEAD^ &&
+ echo world >git &&
+ git add git &&
+ test_tick &&
+ git commit -m side &&
+ git checkout master &&
+ git log --pretty=oneline --abbrev-commit --graph --all &&
+ git branch test side
+'
+
+test_expect_success 'rebase' '
+ git checkout test &&
+ git reset --hard side &&
+ git rebase master &&
+ test "z$(cat git)" = zworld
+'
+
+test_expect_success 'rebase -i' '
+ git checkout test &&
+ git reset --hard side &&
+ EDITOR=true git rebase -i master &&
+ test "z$(cat git)" = zworld
+'
+
+test_expect_success 'setup pre-rebase hook' '
+ mkdir -p .git/hooks &&
+ cat >.git/hooks/pre-rebase <<EOF &&
+#!$SHELL_PATH
+echo "\$1,\$2" >.git/PRE-REBASE-INPUT
+EOF
+ chmod +x .git/hooks/pre-rebase
+'
+
+test_expect_success 'pre-rebase hook gets correct input (1)' '
+ git checkout test &&
+ git reset --hard side &&
+ git rebase master &&
+ test "z$(cat git)" = zworld &&
+ test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,
+
+'
+
+test_expect_success 'pre-rebase hook gets correct input (2)' '
+ git checkout test &&
+ git reset --hard side &&
+ git rebase master test &&
+ test "z$(cat git)" = zworld &&
+ test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+'
+
+test_expect_success 'pre-rebase hook gets correct input (3)' '
+ git checkout test &&
+ git reset --hard side &&
+ git checkout master &&
+ git rebase master test &&
+ test "z$(cat git)" = zworld &&
+ test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+'
+
+test_expect_success 'pre-rebase hook gets correct input (4)' '
+ git checkout test &&
+ git reset --hard side &&
+ EDITOR=true git rebase -i master &&
+ test "z$(cat git)" = zworld &&
+ test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,
+
+'
+
+test_expect_success 'pre-rebase hook gets correct input (5)' '
+ git checkout test &&
+ git reset --hard side &&
+ EDITOR=true git rebase -i master test &&
+ test "z$(cat git)" = zworld &&
+ test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+'
+
+test_expect_success 'pre-rebase hook gets correct input (6)' '
+ git checkout test &&
+ git reset --hard side &&
+ git checkout master &&
+ EDITOR=true git rebase -i master test &&
+ test "z$(cat git)" = zworld &&
+ test "z$(cat .git/PRE-REBASE-INPUT)" = zmaster,test
+'
+
+test_expect_success 'setup pre-rebase hook that fails' '
+ mkdir -p .git/hooks &&
+ cat >.git/hooks/pre-rebase <<EOF &&
+#!$SHELL_PATH
+false
+EOF
+ chmod +x .git/hooks/pre-rebase
+'
+
+test_expect_success 'pre-rebase hook stops rebase (1)' '
+ git checkout test &&
+ git reset --hard side &&
+ test_must_fail git rebase master &&
+ test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+ test 0 = $(git rev-list HEAD...side | wc -l)
+'
+
+test_expect_success 'pre-rebase hook stops rebase (2)' '
+ git checkout test &&
+ git reset --hard side &&
+ (
+ EDITOR=:
+ export EDITOR
+ test_must_fail git rebase -i master
+ ) &&
+ test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+ test 0 = $(git rev-list HEAD...side | wc -l)
+'
+
+test_expect_success 'rebase --no-verify overrides pre-rebase (1)' '
+ git checkout test &&
+ git reset --hard side &&
+ git rebase --no-verify master &&
+ test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+ test "z$(cat git)" = zworld
+'
+
+test_expect_success 'rebase --no-verify overrides pre-rebase (2)' '
+ git checkout test &&
+ git reset --hard side &&
+ EDITOR=true git rebase --no-verify -i master &&
+ test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+ test "z$(cat git)" = zworld
+'
+
+test_done
diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh
new file mode 100755
index 0000000..ee0a6cc
--- /dev/null
+++ b/t/t3414-rebase-preserve-onto.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Greg Price
+#
+
+test_description='git rebase -p should respect --onto
+
+In a rebase with --onto, we should rewrite all the commits that
+aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM.
+'
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+# Set up branches like this:
+# A1---B1---E1---F1---G1
+# \ \ /
+# \ \--C1---D1--/
+# H1
+
+test_expect_success 'setup' '
+ test_commit A1 &&
+ test_commit B1 &&
+ test_commit C1 &&
+ test_commit D1 &&
+ git reset --hard B1 &&
+ test_commit E1 &&
+ test_commit F1 &&
+ test_merge G1 D1 &&
+ git reset --hard A1 &&
+ test_commit H1
+'
+
+# Now rebase merge G1 from both branches' base B1, both should move:
+# A1---B1---E1---F1---G1
+# \ \ /
+# \ \--C1---D1--/
+# \
+# H1---E2---F2---G2
+# \ /
+# \--C2---D2--/
+
+test_expect_success 'rebase from B1 onto H1' '
+ git checkout G1 &&
+ git rebase -p --onto H1 B1 &&
+ test "$(git rev-parse HEAD^1^1^1)" = "$(git rev-parse H1)" &&
+ test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse H1)"
+'
+
+# On the other hand if rebase from E1 which is within one branch,
+# then the other branch stays:
+# A1---B1---E1---F1---G1
+# \ \ /
+# \ \--C1---D1--/
+# \ \
+# H1-----F3-----G3
+
+test_expect_success 'rebase from E1 onto H1' '
+ git checkout G1 &&
+ git rebase -p --onto H1 E1 &&
+ test "$(git rev-parse HEAD^1^1)" = "$(git rev-parse H1)" &&
+ test "$(git rev-parse HEAD^2)" = "$(git rev-parse D1)"
+'
+
+# And the same if we rebase from a commit in the second-parent branch.
+# A1---B1---E1---F1----G1
+# \ \ \ /
+# \ \--C1---D1-\-/
+# \ \
+# H1------D3------G4
+
+test_expect_success 'rebase from C1 onto H1' '
+ git checkout G1 &&
+ git rev-list --first-parent --pretty=oneline C1..G1 &&
+ git rebase -p --onto H1 C1 &&
+ test "$(git rev-parse HEAD^2^1)" = "$(git rev-parse H1)" &&
+ test "$(git rev-parse HEAD^1)" = "$(git rev-parse F1)"
+'
+
+test_done
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
new file mode 100755
index 0000000..b63f4e2
--- /dev/null
+++ b/t/t3415-rebase-autosquash.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='auto squash'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo 0 >file0 &&
+ git add . &&
+ test_tick &&
+ git commit -m "initial commit" &&
+ echo 0 >file1 &&
+ echo 2 >file2 &&
+ git add . &&
+ test_tick &&
+ git commit -m "first commit" &&
+ echo 3 >file3 &&
+ git add . &&
+ test_tick &&
+ git commit -m "second commit" &&
+ git tag base
+'
+
+test_expect_success 'auto fixup' '
+ git reset --hard base &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "fixup! first"
+
+ git tag final-fixup &&
+ test_tick &&
+ git rebase --autosquash -i HEAD^^^ &&
+ git log --oneline >actual &&
+ test 3 = $(wc -l <actual) &&
+ git diff --exit-code final-fixup &&
+ test 1 = "$(git cat-file blob HEAD^:file1)" &&
+ test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+'
+
+test_expect_success 'auto squash' '
+ git reset --hard base &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "squash! first"
+
+ git tag final-squash &&
+ test_tick &&
+ git rebase --autosquash -i HEAD^^^ &&
+ git log --oneline >actual &&
+ test 3 = $(wc -l <actual) &&
+ git diff --exit-code final-squash &&
+ test 1 = "$(git cat-file blob HEAD^:file1)" &&
+ test 2 = $(git cat-file commit HEAD^ | grep first | wc -l)
+'
+
+test_expect_success 'misspelled auto squash' '
+ git reset --hard base &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "squash! forst"
+ git tag final-missquash &&
+ test_tick &&
+ git rebase --autosquash -i HEAD^^^ &&
+ git log --oneline >actual &&
+ test 4 = $(wc -l <actual) &&
+ git diff --exit-code final-missquash &&
+ test 0 = $(git rev-list final-missquash...HEAD | wc -l)
+'
+
+test_done
diff --git a/t/t3416-rebase-onto-threedots.sh b/t/t3416-rebase-onto-threedots.sh
new file mode 100755
index 0000000..ddf2f64
--- /dev/null
+++ b/t/t3416-rebase-onto-threedots.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='git rebase --onto A...B'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-rebase.sh"
+
+# Rebase only the tip commit of "topic" on merge base between "master"
+# and "topic". Cannot do this for "side" with "master" because there
+# is no single merge base.
+#
+#
+# F---G topic G'
+# / /
+# A---B---C---D---E master --> A---B---C---D---E
+# \ \ /
+# \ x
+# \ / \
+# H---I---J---K side
+
+test_expect_success setup '
+ test_commit A &&
+ test_commit B &&
+ git branch side &&
+ test_commit C &&
+ git branch topic &&
+ git checkout side &&
+ test_commit H &&
+ git checkout master &&
+ test_tick &&
+ git merge H &&
+ git tag D &&
+ test_commit E &&
+ git checkout topic &&
+ test_commit F &&
+ test_commit G &&
+ git checkout side &&
+ test_tick &&
+ git merge C &&
+ git tag I &&
+ test_commit J &&
+ test_commit K
+'
+
+test_expect_success 'rebase --onto master...topic' '
+ git reset --hard &&
+ git checkout topic &&
+ git reset --hard G &&
+
+ git rebase --onto master...topic F &&
+ git rev-parse HEAD^1 >actual &&
+ git rev-parse C^0 >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --onto master...' '
+ git reset --hard &&
+ git checkout topic &&
+ git reset --hard G &&
+
+ git rebase --onto master... F &&
+ git rev-parse HEAD^1 >actual &&
+ git rev-parse C^0 >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase --onto master...side' '
+ git reset --hard &&
+ git checkout side &&
+ git reset --hard K &&
+
+ test_must_fail git rebase --onto master...side J
+'
+
+test_expect_success 'rebase -i --onto master...topic' '
+ git reset --hard &&
+ git checkout topic &&
+ git reset --hard G &&
+ set_fake_editor &&
+ EXPECT_COUNT=1 git rebase -i --onto master...topic F &&
+ git rev-parse HEAD^1 >actual &&
+ git rev-parse C^0 >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase -i --onto master...' '
+ git reset --hard &&
+ git checkout topic &&
+ git reset --hard G &&
+ set_fake_editor &&
+ EXPECT_COUNT=1 git rebase -i --onto master... F &&
+ git rev-parse HEAD^1 >actual &&
+ git rev-parse C^0 >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rebase -i --onto master...side' '
+ git reset --hard &&
+ git checkout side &&
+ git reset --hard K &&
+
+ test_must_fail git rebase -i --onto master...side J
+'
+
+test_done
diff --git a/t/t3417-rebase-whitespace-fix.sh b/t/t3417-rebase-whitespace-fix.sh
new file mode 100755
index 0000000..220a740
--- /dev/null
+++ b/t/t3417-rebase-whitespace-fix.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='git rebase --whitespace=fix
+
+This test runs git rebase --whitespace=fix and make sure that it works.
+'
+
+. ./test-lib.sh
+
+# prepare initial revision of "file" with a blank line at the end
+cat >file <<EOF
+a
+b
+c
+
+EOF
+
+# expected contents in "file" after rebase
+cat >expect-first <<EOF
+a
+b
+c
+EOF
+
+# prepare second revision of "file"
+cat >second <<EOF
+a
+b
+c
+
+d
+e
+f
+
+
+
+
+EOF
+
+# expected contents in second revision after rebase
+cat >expect-second <<EOF
+a
+b
+c
+
+d
+e
+f
+EOF
+
+test_expect_success 'blank line at end of file; extend at end of file' '
+ git commit --allow-empty -m "Initial empty commit" &&
+ git add file && git commit -m first &&
+ mv second file &&
+ git add file && git commit -m second &&
+ git rebase --whitespace=fix HEAD^^ &&
+ git diff --exit-code HEAD^:file expect-first &&
+ test_cmp file expect-second
+'
+
+# prepare third revision of "file"
+sed -e's/Z//' >third <<EOF
+a
+b
+c
+
+d
+e
+f
+ Z
+ Z
+h
+i
+j
+k
+l
+EOF
+
+sed -e's/ //g' <third >expect-third
+
+test_expect_success 'two blanks line at end of file; extend at end of file' '
+ cp third file && git add file && git commit -m third &&
+ git rebase --whitespace=fix HEAD^^ &&
+ git diff --exit-code HEAD^:file expect-second &&
+ test_cmp file expect-third
+'
+
+test_expect_success 'same, but do not remove trailing spaces' '
+ git config core.whitespace "-blank-at-eol" &&
+ git reset --hard HEAD^ &&
+ cp third file && git add file && git commit -m third &&
+ git rebase --whitespace=fix HEAD^^
+ git diff --exit-code HEAD^:file expect-second &&
+ test_cmp file third
+'
+
+sed -e's/Z//' >beginning <<EOF
+a
+ Z
+ Z
+EOF
+
+cat >expect-beginning <<EOF
+a
+
+
+1
+2
+3
+4
+5
+EOF
+
+test_expect_success 'at beginning of file' '
+ git config core.whitespace "blank-at-eol" &&
+ cp beginning file &&
+ git commit -m beginning file &&
+ for i in 1 2 3 4 5; do
+ echo $i
+ done >> file &&
+ git commit -m more file &&
+ git rebase --whitespace=fix HEAD^^ &&
+ test_cmp file expect-beginning
+'
+
+test_done
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
new file mode 100755
index 0000000..3b0d273
--- /dev/null
+++ b/t/t3418-rebase-continue.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='git rebase --continue tests'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+test_expect_success 'setup' '
+ test_commit "commit-new-file-F1" F1 1 &&
+ test_commit "commit-new-file-F2" F2 2 &&
+
+ git checkout -b topic HEAD^ &&
+ test_commit "commit-new-file-F2-on-topic-branch" F2 22 &&
+
+ git checkout master
+'
+
+test_expect_success 'interactive rebase --continue works with touched file' '
+ rm -fr .git/rebase-* &&
+ git reset --hard &&
+ git checkout master &&
+
+ FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+ test-chmtime =-60 F1 &&
+ git rebase --continue
+'
+
+test_expect_success 'non-interactive rebase --continue works with touched file' '
+ rm -fr .git/rebase-* &&
+ git reset --hard &&
+ git checkout master &&
+
+ test_must_fail git rebase --onto master master topic &&
+ echo "Resolved" >F2 &&
+ git add F2 &&
+ test-chmtime =-60 F1 &&
+ git rebase --continue
+'
+
+test_done
diff --git a/t/t3500-cherry.sh b/t/t3500-cherry.sh
new file mode 100755
index 0000000..f038f34
--- /dev/null
+++ b/t/t3500-cherry.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Yann Dirson, based on t3400 by Amos Waterland
+#
+
+test_description='git cherry should detect patches integrated upstream
+
+This test cherry-picks one local change of two into master branch, and
+checks that git cherry only returns the second patch in the local branch
+'
+. ./test-lib.sh
+
+GIT_AUTHOR_EMAIL=bogus_email_address
+export GIT_AUTHOR_EMAIL
+
+test_expect_success \
+ 'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
+ 'echo First > A &&
+ git update-index --add A &&
+ test_tick &&
+ git commit -m "Add A." &&
+
+ git checkout -b my-topic-branch &&
+
+ echo Second > B &&
+ git update-index --add B &&
+ test_tick &&
+ git commit -m "Add B." &&
+
+ echo AnotherSecond > C &&
+ git update-index --add C &&
+ test_tick &&
+ git commit -m "Add C." &&
+
+ git checkout -f master &&
+ rm -f B C &&
+
+ echo Third >> A &&
+ git update-index A &&
+ test_tick &&
+ git commit -m "Modify A." &&
+
+ expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* + .*"
+'
+
+test_expect_success \
+ 'check that cherry with limit returns only the top patch'\
+ 'expr "$(echo $(git cherry master my-topic-branch my-topic-branch^1) )" : "+ [^ ]*"
+'
+
+test_expect_success \
+ 'cherry-pick one of the 2 patches, and check cherry recognized one and only one as new' \
+ 'git cherry-pick my-topic-branch^0 &&
+ echo $(git cherry master my-topic-branch) &&
+ expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* - .*"
+'
+
+test_done
diff --git a/t/t3501-revert-cherry-pick.sh b/t/t3501-revert-cherry-pick.sh
new file mode 100755
index 0000000..bc7aedd
--- /dev/null
+++ b/t/t3501-revert-cherry-pick.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='test cherry-pick and revert with renames
+
+ --
+ + rename2: renames oops to opos
+ + rename1: renames oops to spoo
+ + added: adds extra line to oops
+ ++ initial: has lines in oops
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ for l in a b c d e f g h i j k l m n o
+ do
+ echo $l$l$l$l$l$l$l$l$l
+ done >oops &&
+
+ test_tick &&
+ git add oops &&
+ git commit -m initial &&
+ git tag initial &&
+
+ test_tick &&
+ echo "Add extra line at the end" >>oops &&
+ git commit -a -m added &&
+ git tag added &&
+
+ test_tick &&
+ git mv oops spoo &&
+ git commit -m rename1 &&
+ git tag rename1 &&
+
+ test_tick &&
+ git checkout -b side initial &&
+ git mv oops opos &&
+ git commit -m rename2 &&
+ git tag rename2
+'
+
+test_expect_success 'cherry-pick --nonsense' '
+
+ pos=$(git rev-parse HEAD) &&
+ git diff --exit-code HEAD &&
+ test_must_fail git cherry-pick --nonsense 2>msg &&
+ git diff --exit-code HEAD "$pos" &&
+ grep '[Uu]sage:' msg
+'
+
+test_expect_success 'revert --nonsense' '
+
+ pos=$(git rev-parse HEAD) &&
+ git diff --exit-code HEAD &&
+ test_must_fail git revert --nonsense 2>msg &&
+ git diff --exit-code HEAD "$pos" &&
+ grep '[Uu]sage:' msg
+'
+
+test_expect_success 'cherry-pick after renaming branch' '
+
+ git checkout rename2 &&
+ git cherry-pick added &&
+ test $(git rev-parse HEAD^) = $(git rev-parse rename2) &&
+ test -f opos &&
+ grep "Add extra line at the end" opos &&
+ git reflog -1 | grep cherry-pick
+
+'
+
+test_expect_success 'revert after renaming branch' '
+
+ git checkout rename1 &&
+ git revert added &&
+ test $(git rev-parse HEAD^) = $(git rev-parse rename1) &&
+ test -f spoo &&
+ ! grep "Add extra line at the end" spoo &&
+ git reflog -1 | grep revert
+
+'
+
+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 &&
+ grep "Your local changes would be overwritten by " errors
+
+'
+
+test_done
diff --git a/t/t3502-cherry-pick-merge.sh b/t/t3502-cherry-pick-merge.sh
new file mode 100755
index 0000000..0ab52da
--- /dev/null
+++ b/t/t3502-cherry-pick-merge.sh
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+test_description='cherry picking and reverting a merge
+
+ b---c
+ / /
+ initial---a
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >A &&
+ >B &&
+ git add A B &&
+ git commit -m "Initial" &&
+ git tag initial &&
+ git branch side &&
+ echo new line >A &&
+ git commit -m "add line to A" A &&
+ git tag a &&
+ git checkout side &&
+ echo new line >B &&
+ git commit -m "add line to B" B &&
+ git tag b &&
+ git checkout master &&
+ git merge side &&
+ git tag c
+
+'
+
+test_expect_success 'cherry-pick a non-merge with -m should fail' '
+
+ git reset --hard &&
+ git checkout a^0 &&
+ test_must_fail git cherry-pick -m 1 b &&
+ git diff --exit-code a --
+
+'
+
+test_expect_success 'cherry pick a merge without -m should fail' '
+
+ git reset --hard &&
+ git checkout a^0 &&
+ test_must_fail git cherry-pick c &&
+ git diff --exit-code a --
+
+'
+
+test_expect_success 'cherry pick a merge (1)' '
+
+ git reset --hard &&
+ git checkout a^0 &&
+ git cherry-pick -m 1 c &&
+ git diff --exit-code c
+
+'
+
+test_expect_success 'cherry pick a merge (2)' '
+
+ git reset --hard &&
+ git checkout b^0 &&
+ git cherry-pick -m 2 c &&
+ git diff --exit-code c
+
+'
+
+test_expect_success 'cherry pick a merge relative to nonexistent parent should fail' '
+
+ git reset --hard &&
+ git checkout b^0 &&
+ test_must_fail git cherry-pick -m 3 c
+
+'
+
+test_expect_success 'revert a non-merge with -m should fail' '
+
+ git reset --hard &&
+ git checkout c^0 &&
+ test_must_fail git revert -m 1 b &&
+ git diff --exit-code c
+
+'
+
+test_expect_success 'revert a merge without -m should fail' '
+
+ git reset --hard &&
+ git checkout c^0 &&
+ test_must_fail git revert c &&
+ git diff --exit-code c
+
+'
+
+test_expect_success 'revert a merge (1)' '
+
+ git reset --hard &&
+ git checkout c^0 &&
+ git revert -m 1 c &&
+ git diff --exit-code a --
+
+'
+
+test_expect_success 'revert a merge (2)' '
+
+ git reset --hard &&
+ git checkout c^0 &&
+ git revert -m 2 c &&
+ git diff --exit-code b --
+
+'
+
+test_expect_success 'revert a merge relative to nonexistent parent should fail' '
+
+ git reset --hard &&
+ git checkout c^0 &&
+ test_must_fail git revert -m 3 c &&
+ git diff --exit-code c
+
+'
+
+test_done
diff --git a/t/t3503-cherry-pick-root.sh b/t/t3503-cherry-pick-root.sh
new file mode 100755
index 0000000..b0faa29
--- /dev/null
+++ b/t/t3503-cherry-pick-root.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='test cherry-picking a root commit'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ echo first > file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m "first" &&
+
+ git symbolic-ref HEAD refs/heads/second &&
+ rm .git/index file1 &&
+ echo second > file2 &&
+ git add file2 &&
+ test_tick &&
+ git commit -m "second"
+
+'
+
+test_expect_success 'cherry-pick a root commit' '
+
+ git cherry-pick master &&
+ test first = $(cat file1)
+
+'
+
+test_done
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh
new file mode 100755
index 0000000..f7b3518
--- /dev/null
+++ b/t/t3504-cherry-pick-rerere.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='cherry-pick should rerere for conflicts'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo foo >foo &&
+ git add foo && test_tick && git commit -q -m 1 &&
+ echo foo-master >foo &&
+ git add foo && test_tick && git commit -q -m 2 &&
+
+ git checkout -b dev HEAD^ &&
+ echo foo-dev >foo &&
+ git add foo && test_tick && git commit -q -m 3 &&
+ git config rerere.enabled true
+'
+
+test_expect_success 'conflicting merge' '
+ test_must_fail git merge master
+'
+
+test_expect_success 'fixup' '
+ echo foo-dev >foo &&
+ git add foo && test_tick && git commit -q -m 4 &&
+ git reset --hard HEAD^
+ echo foo-dev >expect
+'
+
+test_expect_success 'cherry-pick conflict' '
+ test_must_fail git cherry-pick master &&
+ test_cmp expect foo
+'
+
+test_expect_success 'reconfigure' '
+ git config rerere.enabled false
+ git reset --hard
+'
+
+test_expect_success 'cherry-pick conflict without rerere' '
+ test_must_fail git cherry-pick master &&
+ test_must_fail test_cmp expect foo
+'
+
+test_done
diff --git a/t/t3505-cherry-pick-empty.sh b/t/t3505-cherry-pick-empty.sh
new file mode 100755
index 0000000..e51e505
--- /dev/null
+++ b/t/t3505-cherry-pick-empty.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test_description='test cherry-picking an empty commit'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ echo first > file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m "first" &&
+
+ git checkout -b empty-branch &&
+ test_tick &&
+ git commit --allow-empty -m "empty"
+
+'
+
+test_expect_success 'cherry-pick an empty commit' '
+ git checkout master && {
+ git cherry-pick empty-branch
+ test "$?" = 1
+ }
+'
+
+test_expect_success 'index lockfile was removed' '
+
+ test ! -f .git/index.lock
+
+'
+
+test_done
diff --git a/t/t3506-cherry-pick-ff.sh b/t/t3506-cherry-pick-ff.sh
new file mode 100755
index 0000000..e17ae71
--- /dev/null
+++ b/t/t3506-cherry-pick-ff.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='test cherry-picking with --ff option'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo first > file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m "first" &&
+ git tag first &&
+
+ git checkout -b other &&
+ echo second >> file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m "second" &&
+ git tag second
+'
+
+test_expect_success 'cherry-pick using --ff fast forwards' '
+ git checkout master &&
+ git reset --hard first &&
+ test_tick &&
+ git cherry-pick --ff second &&
+ test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify second)"
+'
+
+test_expect_success 'cherry-pick not using --ff does not fast forwards' '
+ git checkout master &&
+ git reset --hard first &&
+ test_tick &&
+ git cherry-pick second &&
+ test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify second)"
+'
+
+#
+# We setup the following graph:
+#
+# B---C
+# / /
+# first---A
+#
+# (This has been taken from t3502-cherry-pick-merge.sh)
+#
+test_expect_success 'merge setup' '
+ git checkout master &&
+ git reset --hard first &&
+ echo new line >A &&
+ git add A &&
+ test_tick &&
+ git commit -m "add line to A" A &&
+ git tag A &&
+ git checkout -b side first &&
+ echo new line >B &&
+ git add B &&
+ test_tick &&
+ git commit -m "add line to B" B &&
+ git tag B &&
+ git checkout master &&
+ git merge side &&
+ git tag C &&
+ git checkout -b new A
+'
+
+test_expect_success 'cherry-pick a non-merge with --ff and -m should fail' '
+ git reset --hard A -- &&
+ test_must_fail git cherry-pick --ff -m 1 B &&
+ git diff --exit-code A --
+'
+
+test_expect_success 'cherry pick a merge with --ff but without -m should fail' '
+ git reset --hard A -- &&
+ test_must_fail git cherry-pick --ff C &&
+ git diff --exit-code A --
+'
+
+test_expect_success 'cherry pick with --ff a merge (1)' '
+ git reset --hard A -- &&
+ git cherry-pick --ff -m 1 C &&
+ git diff --exit-code C &&
+ test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify C)"
+'
+
+test_expect_success 'cherry pick with --ff a merge (2)' '
+ git reset --hard B -- &&
+ git cherry-pick --ff -m 2 C &&
+ git diff --exit-code C &&
+ test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify C)"
+'
+
+test_expect_success 'cherry pick a merge relative to nonexistent parent with --ff should fail' '
+ git reset --hard B -- &&
+ test_must_fail git cherry-pick --ff -m 3 C
+'
+
+test_done
diff --git a/t/t3507-cherry-pick-conflict.sh b/t/t3507-cherry-pick-conflict.sh
new file mode 100755
index 0000000..e25cf80
--- /dev/null
+++ b/t/t3507-cherry-pick-conflict.sh
@@ -0,0 +1,198 @@
+#!/bin/sh
+
+test_description='test cherry-pick and revert with conflicts
+
+ -
+ + picked: rewrites foo to c
+ + base: rewrites foo to b
+ + initial: writes foo as a, unrelated as unrelated
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ echo unrelated >unrelated &&
+ git add unrelated &&
+ test_commit initial foo a &&
+ test_commit base foo b &&
+ test_commit picked foo c &&
+ git config advice.detachedhead false
+
+'
+
+test_expect_success 'failed cherry-pick does not advance HEAD' '
+
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ head=$(git rev-parse HEAD) &&
+ test_must_fail git cherry-pick picked &&
+ newhead=$(git rev-parse HEAD) &&
+
+ test "$head" = "$newhead"
+'
+
+test_expect_success 'failed cherry-pick produces dirty index' '
+
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git cherry-pick picked &&
+
+ test_must_fail git update-index --refresh -q &&
+ test_must_fail git diff-index --exit-code HEAD
+'
+
+test_expect_success 'failed cherry-pick registers participants in index' '
+
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ {
+ git checkout base -- foo &&
+ git ls-files --stage foo &&
+ git checkout initial -- foo &&
+ git ls-files --stage foo &&
+ git checkout picked -- foo &&
+ git ls-files --stage foo
+ } > stages &&
+ sed "
+ 1 s/ 0 / 1 /
+ 2 s/ 0 / 2 /
+ 3 s/ 0 / 3 /
+ " < stages > expected &&
+ git checkout -f initial^0 &&
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git cherry-pick picked &&
+ git ls-files --stage --unmerged > actual &&
+
+ test_cmp expected actual
+'
+
+test_expect_success 'failed cherry-pick describes conflict in work tree' '
+
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ cat <<-EOF > expected &&
+ <<<<<<< HEAD
+ a
+ =======
+ c
+ >>>>>>> objid picked
+ EOF
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git cherry-pick picked &&
+
+ sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'diff3 -m style' '
+
+ git config merge.conflictstyle diff3 &&
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ cat <<-EOF > expected &&
+ <<<<<<< HEAD
+ a
+ ||||||| parent of objid picked
+ b
+ =======
+ c
+ >>>>>>> objid picked
+ EOF
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git cherry-pick picked &&
+
+ sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'revert also handles conflicts sanely' '
+
+ git config --unset merge.conflictstyle &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ cat <<-EOF > expected &&
+ <<<<<<< HEAD
+ a
+ =======
+ b
+ >>>>>>> parent of objid picked
+ EOF
+ {
+ git checkout picked -- foo &&
+ git ls-files --stage foo &&
+ git checkout initial -- foo &&
+ git ls-files --stage foo &&
+ git checkout base -- foo &&
+ git ls-files --stage foo
+ } > stages &&
+ sed "
+ 1 s/ 0 / 1 /
+ 2 s/ 0 / 2 /
+ 3 s/ 0 / 3 /
+ " < stages > expected-stages &&
+ git checkout -f initial^0 &&
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ head=$(git rev-parse HEAD) &&
+ test_must_fail git revert picked &&
+ newhead=$(git rev-parse HEAD) &&
+ git ls-files --stage --unmerged > actual-stages &&
+
+ test "$head" = "$newhead" &&
+ test_must_fail git update-index --refresh -q &&
+ test_must_fail git diff-index --exit-code HEAD &&
+ test_cmp expected-stages actual-stages &&
+ sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'revert conflict, diff3 -m style' '
+ git config merge.conflictstyle diff3 &&
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ cat <<-EOF > expected &&
+ <<<<<<< HEAD
+ a
+ ||||||| objid picked
+ c
+ =======
+ b
+ >>>>>>> parent of objid picked
+ EOF
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git revert picked &&
+
+ sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t3508-cherry-pick-many-commits.sh b/t/t3508-cherry-pick-many-commits.sh
new file mode 100755
index 0000000..f90ed3d
--- /dev/null
+++ b/t/t3508-cherry-pick-many-commits.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='test cherry-picking many commits'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo first > file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m "first" &&
+ git tag first &&
+
+ git checkout -b other &&
+ for val in second third fourth
+ do
+ echo $val >> file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m "$val" &&
+ git tag $val
+ done
+'
+
+test_expect_success 'cherry-pick first..fourth works' '
+ git checkout -f master &&
+ git reset --hard first &&
+ test_tick &&
+ git cherry-pick first..fourth &&
+ git diff --quiet other &&
+ git diff --quiet HEAD other &&
+ test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify fourth)"
+'
+
+test_expect_success 'cherry-pick --ff first..fourth works' '
+ git checkout -f master &&
+ git reset --hard first &&
+ test_tick &&
+ git cherry-pick --ff first..fourth &&
+ git diff --quiet other &&
+ git diff --quiet HEAD other &&
+ test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify fourth)"
+'
+
+test_expect_success 'cherry-pick -n first..fourth works' '
+ git checkout -f master &&
+ git reset --hard first &&
+ test_tick &&
+ git cherry-pick -n first..fourth &&
+ git diff --quiet other &&
+ git diff --cached --quiet other &&
+ git diff --quiet HEAD first
+'
+
+test_expect_success 'revert first..fourth works' '
+ git checkout -f master &&
+ git reset --hard fourth &&
+ test_tick &&
+ git revert first..fourth &&
+ git diff --quiet first &&
+ git diff --cached --quiet first &&
+ git diff --quiet HEAD first
+'
+
+test_expect_success 'revert ^first fourth works' '
+ git checkout -f master &&
+ git reset --hard fourth &&
+ test_tick &&
+ git revert ^first fourth &&
+ git diff --quiet first &&
+ git diff --cached --quiet first &&
+ git diff --quiet HEAD first
+'
+
+test_expect_success 'revert fourth fourth~1 fourth~2 works' '
+ git checkout -f master &&
+ git reset --hard fourth &&
+ test_tick &&
+ git revert fourth fourth~1 fourth~2 &&
+ git diff --quiet first &&
+ git diff --cached --quiet first &&
+ git diff --quiet HEAD first
+'
+
+test_expect_success 'cherry-pick -3 fourth works' '
+ git checkout -f master &&
+ git reset --hard first &&
+ test_tick &&
+ git cherry-pick -3 fourth &&
+ git diff --quiet other &&
+ git diff --quiet HEAD other &&
+ test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify fourth)"
+'
+
+test_expect_success 'cherry-pick --stdin works' '
+ git checkout -f master &&
+ git reset --hard first &&
+ test_tick &&
+ git rev-list --reverse first..fourth | git cherry-pick --stdin &&
+ git diff --quiet other &&
+ git diff --quiet HEAD other &&
+ test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify fourth)"
+'
+
+test_done
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
new file mode 100755
index 0000000..b514cbb
--- /dev/null
+++ b/t/t3600-rm.sh
@@ -0,0 +1,282 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Carl D. Worth
+#
+
+test_description='Test of the various options to git rm.'
+
+. ./test-lib.sh
+
+# Setup some files to be removed, some with funny characters
+test_expect_success \
+ 'Initialize test directory' \
+ "touch -- foo bar baz 'space embedded' -q &&
+ git add -- foo bar baz 'space embedded' -q &&
+ git commit -m 'add normal files'"
+
+if touch -- 'tab embedded' 'newline
+embedded' 2>/dev/null
+then
+ test_set_prereq FUNNYNAMES
+else
+ say 'Your filesystem does not allow tabs in filenames.'
+fi
+
+test_expect_success FUNNYNAMES 'add files with funny names' "
+ git add -- 'tab embedded' 'newline
+embedded' &&
+ git commit -m 'add files with tabs and newlines'
+"
+
+# Determine rm behavior
+# Later we will try removing an unremovable path to make sure
+# git rm barfs, but if the test is run as root that cannot be
+# arranged.
+: >test-file
+chmod a-w .
+rm -f test-file 2>/dev/null
+if test -f test-file
+then
+ test_set_prereq RO_DIR
+else
+ skip_all='skipping removal failure test (perhaps running as root?)'
+fi
+chmod 775 .
+rm -f test-file
+
+test_expect_success \
+ 'Pre-check that foo exists and is in index before git rm foo' \
+ '[ -f foo ] && git ls-files --error-unmatch foo'
+
+test_expect_success \
+ 'Test that git rm foo succeeds' \
+ 'git rm --cached foo'
+
+test_expect_success \
+ 'Test that git rm --cached foo succeeds if the index matches the file' \
+ 'echo content > foo
+ git add foo
+ git rm --cached foo'
+
+test_expect_success \
+ 'Test that git rm --cached foo succeeds if the index matches the file' \
+ 'echo content > foo
+ git add foo
+ git commit -m foo
+ echo "other content" > foo
+ git rm --cached foo'
+
+test_expect_success \
+ 'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' '
+ echo content > foo
+ git add foo
+ git commit -m foo
+ echo "other content" > foo
+ git add foo
+ echo "yet another content" > foo
+ test_must_fail git rm --cached foo
+'
+
+test_expect_success \
+ 'Test that git rm --cached -f foo works in case where --cached only did not' \
+ 'echo content > foo
+ git add foo
+ git commit -m foo
+ echo "other content" > foo
+ git add foo
+ echo "yet another content" > foo
+ git rm --cached -f foo'
+
+test_expect_success \
+ 'Post-check that foo exists but is not in index after git rm foo' \
+ '[ -f foo ] && test_must_fail git ls-files --error-unmatch foo'
+
+test_expect_success \
+ 'Pre-check that bar exists and is in index before "git rm bar"' \
+ '[ -f bar ] && git ls-files --error-unmatch bar'
+
+test_expect_success \
+ 'Test that "git rm bar" succeeds' \
+ 'git rm bar'
+
+test_expect_success \
+ 'Post-check that bar does not exist and is not in index after "git rm -f bar"' \
+ '! [ -f bar ] && test_must_fail git ls-files --error-unmatch bar'
+
+test_expect_success \
+ 'Test that "git rm -- -q" succeeds (remove a file that looks like an option)' \
+ 'git rm -- -q'
+
+test_expect_success FUNNYNAMES \
+ "Test that \"git rm -f\" succeeds with embedded space, tab, or newline characters." \
+ "git rm -f 'space embedded' 'tab embedded' 'newline
+embedded'"
+
+test_expect_success RO_DIR 'Test that "git rm -f" fails if its rm fails' '
+ chmod a-w . &&
+ test_must_fail git rm -f baz &&
+ chmod 775 .
+'
+
+test_expect_success \
+ 'When the rm in "git rm -f" fails, it should not remove the file from the index' \
+ 'git ls-files --error-unmatch baz'
+
+test_expect_success 'Remove nonexistent file with --ignore-unmatch' '
+ git rm --ignore-unmatch nonexistent
+'
+
+test_expect_success '"rm" command printed' '
+ echo frotz > test-file &&
+ git add test-file &&
+ git commit -m "add file for rm test" &&
+ 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 &&
+ 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 &&
+ rm -f test-file rm-output &&
+ git commit -m "remove file from rm --quiet test"
+'
+
+# Now, failure cases.
+test_expect_success 'Re-add foo and baz' '
+ git add foo baz &&
+ git ls-files --error-unmatch foo baz
+'
+
+test_expect_success 'Modify foo -- rm should refuse' '
+ echo >>foo &&
+ test_must_fail git rm foo baz &&
+ test -f foo &&
+ test -f baz &&
+ git ls-files --error-unmatch foo baz
+'
+
+test_expect_success 'Modified foo -- rm -f should work' '
+ git rm -f foo baz &&
+ test ! -f foo &&
+ test ! -f baz &&
+ test_must_fail git ls-files --error-unmatch foo &&
+ test_must_fail git ls-files --error-unmatch bar
+'
+
+test_expect_success 'Re-add foo and baz for HEAD tests' '
+ echo frotz >foo &&
+ git checkout HEAD -- baz &&
+ git add foo baz &&
+ git ls-files --error-unmatch foo baz
+'
+
+test_expect_success 'foo is different in index from HEAD -- rm should refuse' '
+ test_must_fail git rm foo baz &&
+ test -f foo &&
+ test -f baz &&
+ git ls-files --error-unmatch foo baz
+'
+
+test_expect_success 'but with -f it should work.' '
+ git rm -f foo baz &&
+ test ! -f foo &&
+ test ! -f baz &&
+ test_must_fail git ls-files --error-unmatch foo
+ test_must_fail git ls-files --error-unmatch baz
+'
+
+test_expect_success 'refuse to remove cached empty file with modifications' '
+ >empty &&
+ git add empty &&
+ echo content >empty &&
+ test_must_fail git rm --cached empty
+'
+
+test_expect_success 'remove intent-to-add file without --force' '
+ echo content >intent-to-add &&
+ git add -N intent-to-add
+ git rm --cached intent-to-add
+'
+
+test_expect_success 'Recursive test setup' '
+ mkdir -p frotz &&
+ echo qfwfq >frotz/nitfol &&
+ git add frotz &&
+ git commit -m "subdir test"
+'
+
+test_expect_success 'Recursive without -r fails' '
+ test_must_fail git rm frotz &&
+ test -d frotz &&
+ test -f frotz/nitfol
+'
+
+test_expect_success 'Recursive with -r but dirty' '
+ echo qfwfq >>frotz/nitfol
+ test_must_fail git rm -r frotz &&
+ test -d frotz &&
+ test -f frotz/nitfol
+'
+
+test_expect_success 'Recursive with -r -f' '
+ git rm -f -r frotz &&
+ ! test -f frotz/nitfol &&
+ ! test -d frotz
+'
+
+test_expect_success 'Remove nonexistent file returns nonzero exit status' '
+ test_must_fail git rm nonexistent
+'
+
+test_expect_success 'Call "rm" from outside the work tree' '
+ mkdir repo &&
+ (cd repo &&
+ git init &&
+ echo something > somefile &&
+ git add somefile &&
+ git commit -m "add a file" &&
+ (cd .. &&
+ git --git-dir=repo/.git --work-tree=repo rm somefile) &&
+ test_must_fail git ls-files --error-unmatch somefile)
+'
+
+test_expect_success 'refresh index before checking if it is up-to-date' '
+
+ git reset --hard &&
+ test-chmtime -86400 frotz/nitfol &&
+ git rm frotz/nitfol &&
+ test ! -f frotz/nitfol
+
+'
+
+test_expect_success 'choking "git rm" should not let it die with cruft' '
+ git reset -q --hard &&
+ H=0000000000000000000000000000000000000000 &&
+ i=0 &&
+ while test $i -lt 12000
+ do
+ echo "100644 $H 0 some-file-$i"
+ i=$(( $i + 1 ))
+ done | git update-index --index-info &&
+ git rm -n "some-file-*" | :;
+ test -f .git/index.lock
+ status=$?
+ rm -f .git/index.lock
+ git reset -q --hard
+ test "$status" != 0
+'
+
+test_expect_success 'rm removes subdirectories recursively' '
+ mkdir -p dir/subdir/subsubdir &&
+ echo content >dir/subdir/subsubdir/file &&
+ git add dir/subdir/subsubdir/file &&
+ git rm -f dir/subdir/subsubdir/file &&
+ ! test -d dir
+'
+
+test_done
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
new file mode 100755
index 0000000..7d7140d
--- /dev/null
+++ b/t/t3700-add.sh
@@ -0,0 +1,291 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Carl D. Worth
+#
+
+test_description='Test of git add, including the -- option.'
+
+. ./test-lib.sh
+
+test_expect_success \
+ 'Test of git add' \
+ 'touch foo && git add foo'
+
+test_expect_success \
+ 'Post-check that foo is in the index' \
+ 'git ls-files foo | grep foo'
+
+test_expect_success \
+ 'Test that "git add -- -q" works' \
+ 'touch -- -q && git add -- -q'
+
+test_expect_success \
+ 'git add: Test that executable bit is not used if core.filemode=0' \
+ 'git config core.filemode 0 &&
+ echo foo >xfoo1 &&
+ chmod 755 xfoo1 &&
+ git add xfoo1 &&
+ case "`git ls-files --stage xfoo1`" in
+ 100644" "*xfoo1) echo pass;;
+ *) echo fail; git ls-files --stage xfoo1; (exit 1);;
+ esac'
+
+test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
+ rm -f xfoo1 &&
+ ln -s foo xfoo1 &&
+ git add xfoo1 &&
+ case "`git ls-files --stage xfoo1`" in
+ 120000" "*xfoo1) echo pass;;
+ *) echo fail; git ls-files --stage xfoo1; (exit 1);;
+ esac
+'
+
+test_expect_success \
+ 'git update-index --add: Test that executable bit is not used...' \
+ 'git config core.filemode 0 &&
+ echo foo >xfoo2 &&
+ chmod 755 xfoo2 &&
+ git update-index --add xfoo2 &&
+ case "`git ls-files --stage xfoo2`" in
+ 100644" "*xfoo2) echo pass;;
+ *) echo fail; git ls-files --stage xfoo2; (exit 1);;
+ esac'
+
+test_expect_success SYMLINKS 'git add: filemode=0 should not get confused by symlink' '
+ rm -f xfoo2 &&
+ ln -s foo xfoo2 &&
+ git update-index --add xfoo2 &&
+ case "`git ls-files --stage xfoo2`" in
+ 120000" "*xfoo2) echo pass;;
+ *) echo fail; git ls-files --stage xfoo2; (exit 1);;
+ esac
+'
+
+test_expect_success SYMLINKS \
+ 'git update-index --add: Test that executable bit is not used...' \
+ 'git config core.filemode 0 &&
+ ln -s xfoo2 xfoo3 &&
+ git update-index --add xfoo3 &&
+ case "`git ls-files --stage xfoo3`" in
+ 120000" "*xfoo3) echo pass;;
+ *) echo fail; git ls-files --stage xfoo3; (exit 1);;
+ esac'
+
+test_expect_success '.gitignore test setup' '
+ echo "*.ig" >.gitignore &&
+ mkdir c.if d.ig &&
+ >a.ig && >b.if &&
+ >c.if/c.if && >c.if/c.ig &&
+ >d.ig/d.if && >d.ig/d.ig
+'
+
+test_expect_success '.gitignore is honored' '
+ git add . &&
+ ! (git ls-files | grep "\\.ig")
+'
+
+test_expect_success 'error out when attempting to add ignored ones without -f' '
+ test_must_fail git add a.?? &&
+ ! (git ls-files | grep "\\.ig")
+'
+
+test_expect_success 'error out when attempting to add ignored ones without -f' '
+ test_must_fail git add d.?? &&
+ ! (git ls-files | grep "\\.ig")
+'
+
+test_expect_success 'add ignored ones with -f' '
+ git add -f a.?? &&
+ git ls-files --error-unmatch a.ig
+'
+
+test_expect_success 'add ignored ones with -f' '
+ git add -f d.??/* &&
+ git ls-files --error-unmatch d.ig/d.if d.ig/d.ig
+'
+
+test_expect_success 'add ignored ones with -f' '
+ rm -f .git/index &&
+ git add -f d.?? &&
+ git ls-files --error-unmatch d.ig/d.if d.ig/d.ig
+'
+
+test_expect_success '.gitignore with subdirectory' '
+
+ rm -f .git/index &&
+ mkdir -p sub/dir &&
+ echo "!dir/a.*" >sub/.gitignore &&
+ >sub/a.ig &&
+ >sub/dir/a.ig &&
+ git add sub/dir &&
+ git ls-files --error-unmatch sub/dir/a.ig &&
+ rm -f .git/index &&
+ (
+ cd sub/dir &&
+ git add .
+ ) &&
+ git ls-files --error-unmatch sub/dir/a.ig
+'
+
+mkdir 1 1/2 1/3
+touch 1/2/a 1/3/b 1/2/c
+test_expect_success 'check correct prefix detection' '
+ rm -f .git/index &&
+ git add 1/2/a 1/3/b 1/2/c
+'
+
+test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries' '
+ for s in 1 2 3
+ do
+ echo $s > stage$s
+ echo "100755 $(git hash-object -w stage$s) $s file"
+ echo "120000 $(printf $s | git hash-object -w -t blob --stdin) $s symlink"
+ done | git update-index --index-info &&
+ git config core.filemode 0 &&
+ git config core.symlinks 0 &&
+ echo new > file &&
+ echo new > symlink &&
+ git add file symlink &&
+ git ls-files --stage | grep "^100755 .* 0 file$" &&
+ git ls-files --stage | grep "^120000 .* 0 symlink$"
+'
+
+test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over stage 1' '
+ git rm --cached -f file symlink &&
+ (
+ echo "100644 $(git hash-object -w stage1) 1 file"
+ echo "100755 $(git hash-object -w stage2) 2 file"
+ echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink"
+ echo "120000 $(printf 2 | git hash-object -w -t blob --stdin) 2 symlink"
+ ) | git update-index --index-info &&
+ git config core.filemode 0 &&
+ git config core.symlinks 0 &&
+ echo new > file &&
+ echo new > symlink &&
+ git add file symlink &&
+ git ls-files --stage | grep "^100755 .* 0 file$" &&
+ git ls-files --stage | grep "^120000 .* 0 symlink$"
+'
+
+test_expect_success 'git add --refresh' '
+ >foo && git add foo && git commit -a -m "commit all" &&
+ test -z "`git diff-index HEAD -- foo`" &&
+ git read-tree HEAD &&
+ case "`git diff-index HEAD -- foo`" in
+ :100644" "*"M foo") echo pass;;
+ *) echo fail; (exit 1);;
+ esac &&
+ git add --refresh -- foo &&
+ test -z "`git diff-index HEAD -- foo`"
+'
+
+test_expect_success POSIXPERM 'git add should fail atomically upon an unreadable file' '
+ git reset --hard &&
+ date >foo1 &&
+ date >foo2 &&
+ chmod 0 foo2 &&
+ test_must_fail git add --verbose . &&
+ ! ( git ls-files foo1 | grep foo1 )
+'
+
+rm -f foo2
+
+test_expect_success POSIXPERM 'git add --ignore-errors' '
+ git reset --hard &&
+ date >foo1 &&
+ date >foo2 &&
+ chmod 0 foo2 &&
+ test_must_fail git add --verbose --ignore-errors . &&
+ git ls-files foo1 | grep foo1
+'
+
+rm -f foo2
+
+test_expect_success POSIXPERM 'git add (add.ignore-errors)' '
+ git config add.ignore-errors 1 &&
+ git reset --hard &&
+ date >foo1 &&
+ date >foo2 &&
+ chmod 0 foo2 &&
+ test_must_fail git add --verbose . &&
+ git ls-files foo1 | grep foo1
+'
+rm -f foo2
+
+test_expect_success POSIXPERM 'git add (add.ignore-errors = false)' '
+ git config add.ignore-errors 0 &&
+ git reset --hard &&
+ date >foo1 &&
+ date >foo2 &&
+ chmod 0 foo2 &&
+ test_must_fail git add --verbose . &&
+ ! ( git ls-files foo1 | grep foo1 )
+'
+rm -f foo2
+
+test_expect_success POSIXPERM '--no-ignore-errors overrides config' '
+ git config add.ignore-errors 1 &&
+ git reset --hard &&
+ date >foo1 &&
+ date >foo2 &&
+ chmod 0 foo2 &&
+ test_must_fail git add --verbose --no-ignore-errors . &&
+ ! ( git ls-files foo1 | grep foo1 ) &&
+ git config add.ignore-errors 0
+'
+rm -f foo2
+
+test_expect_success BSLASHPSPEC "git add 'fo\\[ou\\]bar' ignores foobar" '
+ git reset --hard &&
+ touch fo\[ou\]bar foobar &&
+ git add '\''fo\[ou\]bar'\'' &&
+ git ls-files fo\[ou\]bar | fgrep fo\[ou\]bar &&
+ ! ( git ls-files foobar | grep foobar )
+'
+
+test_expect_success 'git add to resolve conflicts on otherwise ignored path' '
+ git reset --hard &&
+ H=$(git rev-parse :1/2/a) &&
+ (
+ echo "100644 $H 1 track-this"
+ echo "100644 $H 3 track-this"
+ ) | git update-index --index-info &&
+ echo track-this >>.gitignore &&
+ echo resolved >track-this &&
+ git add track-this
+'
+
+test_expect_success '"add non-existent" should fail' '
+ test_must_fail git add non-existent &&
+ ! (git ls-files | grep "non-existent")
+'
+
+test_expect_success 'git add --dry-run of existing changed file' "
+ echo new >>track-this &&
+ git add --dry-run track-this >actual 2>&1 &&
+ echo \"add 'track-this'\" | test_cmp - actual
+"
+
+test_expect_success 'git add --dry-run of non-existing file' "
+ echo ignored-file >>.gitignore &&
+ test_must_fail git add --dry-run track-this ignored-file >actual 2>&1 &&
+ echo \"fatal: pathspec 'ignored-file' did not match any files\" | test_cmp - actual
+"
+
+cat >expect.err <<\EOF
+The following paths are ignored by one of your .gitignore files:
+ignored-file
+Use -f if you really want to add them.
+fatal: no files added
+EOF
+cat >expect.out <<\EOF
+add 'track-this'
+EOF
+
+test_expect_success 'git add --dry-run --ignore-missing of non-existing file' '
+ test_must_fail git add --dry-run --ignore-missing track-this ignored-file >actual.out 2>actual.err &&
+ test_cmp expect.out actual.out &&
+ test_cmp expect.err actual.err
+'
+
+test_done
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
new file mode 100755
index 0000000..7ad8465
--- /dev/null
+++ b/t/t3701-add-interactive.sh
@@ -0,0 +1,268 @@
+#!/bin/sh
+
+test_description='add -i basic tests'
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping git add -i tests, perl not available'
+ test_done
+fi
+
+test_expect_success 'setup (initial)' '
+ echo content >file &&
+ git add file &&
+ echo more >>file &&
+ echo lines >>file
+'
+test_expect_success 'status works (initial)' '
+ git add -i </dev/null >output &&
+ grep "+1/-0 *+2/-0 file" output
+'
+cat >expected <<EOF
+new file mode 100644
+index 0000000..d95f3ad
+--- /dev/null
++++ b/file
+@@ -0,0 +1 @@
++content
+EOF
+test_expect_success 'diff works (initial)' '
+ (echo d; echo 1) | git add -i >output &&
+ sed -ne "/new file/,/content/p" <output >diff &&
+ test_cmp expected diff
+'
+test_expect_success 'revert works (initial)' '
+ git add file &&
+ (echo r; echo 1) | git add -i &&
+ git ls-files >output &&
+ ! grep . output
+'
+
+test_expect_success 'setup (commit)' '
+ echo baseline >file &&
+ git add file &&
+ git commit -m commit &&
+ echo content >>file &&
+ git add file &&
+ echo more >>file &&
+ echo lines >>file
+'
+test_expect_success 'status works (commit)' '
+ git add -i </dev/null >output &&
+ grep "+1/-0 *+2/-0 file" output
+'
+cat >expected <<EOF
+index 180b47c..b6f2c08 100644
+--- a/file
++++ b/file
+@@ -1 +1,2 @@
+ baseline
++content
+EOF
+test_expect_success 'diff works (commit)' '
+ (echo d; echo 1) | git add -i >output &&
+ sed -ne "/^index/,/content/p" <output >diff &&
+ test_cmp expected diff
+'
+test_expect_success 'revert works (commit)' '
+ git add file &&
+ (echo r; echo 1) | git add -i &&
+ git add -i </dev/null >output &&
+ grep "unchanged *+3/-0 file" output
+'
+
+cat >expected <<EOF
+EOF
+cat >fake_editor.sh <<EOF
+EOF
+chmod a+x fake_editor.sh
+test_set_editor "$(pwd)/fake_editor.sh"
+test_expect_success 'dummy edit works' '
+ (echo e; echo a) | git add -p &&
+ git diff > diff &&
+ test_cmp expected diff
+'
+
+cat >patch <<EOF
+@@ -1,1 +1,4 @@
+ this
++patch
+-doesn't
+ apply
+EOF
+echo "#!$SHELL_PATH" >fake_editor.sh
+cat >>fake_editor.sh <<\EOF
+mv -f "$1" oldpatch &&
+mv -f patch "$1"
+EOF
+chmod a+x fake_editor.sh
+test_set_editor "$(pwd)/fake_editor.sh"
+test_expect_success 'bad edit rejected' '
+ git reset &&
+ (echo e; echo n; echo d) | git add -p >output &&
+ grep "hunk does not apply" output
+'
+
+cat >patch <<EOF
+this patch
+is garbage
+EOF
+test_expect_success 'garbage edit rejected' '
+ git reset &&
+ (echo e; echo n; echo d) | git add -p >output &&
+ grep "hunk does not apply" output
+'
+
+cat >patch <<EOF
+@@ -1,0 +1,0 @@
+ baseline
++content
++newcontent
++lines
+EOF
+cat >expected <<EOF
+diff --git a/file b/file
+index b5dd6c9..f910ae9 100644
+--- a/file
++++ b/file
+@@ -1,4 +1,4 @@
+ baseline
+ content
+-newcontent
++more
+ lines
+EOF
+test_expect_success 'real edit works' '
+ (echo e; echo n; echo d) | git add -p &&
+ git diff >output &&
+ test_cmp expected output
+'
+
+test_expect_success 'skip files similarly as commit -a' '
+ git reset &&
+ echo file >.gitignore &&
+ echo changed >file &&
+ echo y | git add -p file &&
+ git diff >output &&
+ git reset &&
+ git commit -am commit &&
+ git diff >expected &&
+ test_cmp expected output &&
+ git reset --hard HEAD^
+'
+rm -f .gitignore
+
+if test "$(git config --bool core.filemode)" = false
+then
+ say '# skipping filemode tests (filesystem does not properly support modes)'
+else
+ test_set_prereq FILEMODE
+fi
+
+test_expect_success FILEMODE 'patch does not affect mode' '
+ git reset --hard &&
+ echo content >>file &&
+ chmod +x file &&
+ printf "n\\ny\\n" | git add -p &&
+ git show :file | grep content &&
+ git diff file | grep "new mode"
+'
+
+test_expect_success FILEMODE 'stage mode but not hunk' '
+ git reset --hard &&
+ echo content >>file &&
+ chmod +x file &&
+ printf "y\\nn\\n" | git add -p &&
+ git diff --cached file | grep "new mode" &&
+ git diff file | grep "+content"
+'
+
+
+test_expect_success FILEMODE 'stage mode and hunk' '
+ git reset --hard &&
+ echo content >>file &&
+ chmod +x file &&
+ printf "y\\ny\\n" | git add -p &&
+ git diff --cached file | grep "new mode" &&
+ git diff --cached file | grep "+content" &&
+ test -z "$(git diff file)"
+'
+
+# end of tests disabled when filemode is not usable
+
+test_expect_success 'setup again' '
+ git reset --hard &&
+ test_chmod +x file &&
+ echo content >>file
+'
+
+# Write the patch file with a new line at the top and bottom
+cat >patch <<EOF
+index 180b47c..b6f2c08 100644
+--- a/file
++++ b/file
+@@ -1,2 +1,4 @@
++firstline
+ baseline
+ content
++lastline
+EOF
+# Expected output, similar to the patch but w/ diff at the top
+cat >expected <<EOF
+diff --git a/file b/file
+index b6f2c08..61b9053 100755
+--- a/file
++++ b/file
+@@ -1,2 +1,4 @@
++firstline
+ baseline
+ content
++lastline
+EOF
+# Test splitting the first patch, then adding both
+test_expect_success 'add first line works' '
+ git commit -am "clear local changes" &&
+ git apply patch &&
+ (echo s; echo y; echo y) | git add -p file &&
+ git diff --cached > diff &&
+ test_cmp expected diff
+'
+
+cat >expected <<EOF
+diff --git a/non-empty b/non-empty
+deleted file mode 100644
+index d95f3ad..0000000
+--- a/non-empty
++++ /dev/null
+@@ -1 +0,0 @@
+-content
+EOF
+test_expect_success 'deleting a non-empty file' '
+ git reset --hard &&
+ echo content >non-empty &&
+ git add non-empty &&
+ git commit -m non-empty &&
+ rm non-empty &&
+ echo y | git add -p non-empty &&
+ git diff --cached >diff &&
+ test_cmp expected diff
+'
+
+cat >expected <<EOF
+diff --git a/empty b/empty
+deleted file mode 100644
+index e69de29..0000000
+EOF
+
+test_expect_success 'deleting an empty file' '
+ git reset --hard &&
+ > empty &&
+ git add empty &&
+ git commit -m empty &&
+ rm empty &&
+ echo y | git add -p empty &&
+ git diff --cached >diff &&
+ test_cmp expected diff
+'
+
+test_done
diff --git a/t/t3702-add-edit.sh b/t/t3702-add-edit.sh
new file mode 100755
index 0000000..4ee47cc
--- /dev/null
+++ b/t/t3702-add-edit.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='add -e basic tests'
+. ./test-lib.sh
+
+
+cat > file << EOF
+LO, praise of the prowess of people-kings
+of spear-armed Danes, in days long sped,
+we have heard, and what honor the athelings won!
+Oft Scyld the Scefing from squadroned foes,
+from many a tribe, the mead-bench tore,
+awing the earls. Since erst he lay
+friendless, a foundling, fate repaid him:
+for he waxed under welkin, in wealth he throve,
+till before him the folk, both far and near,
+who house by the whale-path, heard his mandate,
+gave him gifts: a good king he!
+EOF
+
+cat > second-part << EOF
+To him an heir was afterward born,
+a son in his halls, whom heaven sent
+to favor the folk, feeling their woe
+that erst they had lacked an earl for leader
+so long a while; the Lord endowed him,
+the Wielder of Wonder, with world's renown.
+EOF
+
+test_expect_success 'setup' '
+
+ git add file &&
+ test_tick &&
+ git commit -m initial file
+
+'
+
+cat > expected-patch << EOF
+diff --git a/file b/file
+index b9834b5..9020acb 100644
+--- a/file
++++ b/file
+@@ -1,11 +1,6 @@
+-LO, praise of the prowess of people-kings
+-of spear-armed Danes, in days long sped,
+-we have heard, and what honor the athelings won!
+-Oft Scyld the Scefing from squadroned foes,
+-from many a tribe, the mead-bench tore,
+-awing the earls. Since erst he lay
+-friendless, a foundling, fate repaid him:
+-for he waxed under welkin, in wealth he throve,
+-till before him the folk, both far and near,
+-who house by the whale-path, heard his mandate,
+-gave him gifts: a good king he!
++To him an heir was afterward born,
++a son in his halls, whom heaven sent
++to favor the folk, feeling their woe
++that erst they had lacked an earl for leader
++so long a while; the Lord endowed him,
++the Wielder of Wonder, with world's renown.
+EOF
+
+cat > patch << EOF
+diff --git a/file b/file
+index b9834b5..ef6e94c 100644
+--- a/file
++++ b/file
+@@ -3,1 +3,333 @@ of spear-armed Danes, in days long sped,
+ we have heard, and what honor the athelings won!
++
+ Oft Scyld the Scefing from squadroned foes,
+@@ -2,7 +1,5 @@ awing the earls. Since erst he lay
+ friendless, a foundling, fate repaid him:
++
+ for he waxed under welkin, in wealth he throve,
+EOF
+
+cat > expected << EOF
+diff --git a/file b/file
+index b9834b5..ef6e94c 100644
+--- a/file
++++ b/file
+@@ -1,10 +1,12 @@
+ LO, praise of the prowess of people-kings
+ of spear-armed Danes, in days long sped,
+ we have heard, and what honor the athelings won!
++
+ Oft Scyld the Scefing from squadroned foes,
+ from many a tribe, the mead-bench tore,
+ awing the earls. Since erst he lay
+ friendless, a foundling, fate repaid him:
++
+ for he waxed under welkin, in wealth he throve,
+ till before him the folk, both far and near,
+ who house by the whale-path, heard his mandate,
+EOF
+
+echo "#!$SHELL_PATH" >fake-editor.sh
+cat >> fake-editor.sh <<\EOF
+mv -f "$1" orig-patch &&
+mv -f patch "$1"
+EOF
+
+test_set_editor "$(pwd)/fake-editor.sh"
+chmod a+x fake-editor.sh
+
+test_expect_success 'add -e' '
+
+ cp second-part file &&
+ git add -e &&
+ test_cmp second-part file &&
+ test_cmp orig-patch expected-patch &&
+ git diff --cached > out &&
+ test_cmp out expected
+
+'
+
+test_done
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
new file mode 100755
index 0000000..8eb4794
--- /dev/null
+++ b/t/t3800-mktag.sh
@@ -0,0 +1,365 @@
+#!/bin/sh
+#
+#
+
+test_description='git mktag: tag object verify test'
+
+. ./test-lib.sh
+
+###########################################################
+# check the tag.sig file, expecting verify_tag() to fail,
+# and checking that the error message matches the pattern
+# given in the expect.pat file.
+
+check_verify_failure () {
+ expect="$2"
+ test_expect_success "$1" '
+ ( test_must_fail git mktag <tag.sig 2>message ) &&
+ grep "$expect" message
+ '
+}
+
+###########################################################
+# first create a commit, so we have a valid object/type
+# for the tag.
+test_expect_success 'setup' '
+ echo Hello >A &&
+ git update-index --add A &&
+ git commit -m "Initial commit" &&
+ head=$(git rev-parse --verify HEAD)
+'
+
+############################################################
+# 1. length check
+
+cat >tag.sig <<EOF
+too short for a tag
+EOF
+
+check_verify_failure 'Tag object length check' \
+ '^error: .*size wrong.*$'
+
+############################################################
+# 2. object line label check
+
+cat >tag.sig <<EOF
+xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895
+type tag
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure '"object" line label check' '^error: char0: .*"object "$'
+
+############################################################
+# 3. object line SHA1 check
+
+cat >tag.sig <<EOF
+object zz9e9b33986b1c2670fff52c5067603117b3e895
+type tag
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure '"object" line SHA1 check' '^error: char7: .*SHA1 hash$'
+
+############################################################
+# 4. type line label check
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+xxxx tag
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure '"type" line label check' '^error: char47: .*"\\ntype "$'
+
+############################################################
+# 5. type line eol check
+
+echo "object 779e9b33986b1c2670fff52c5067603117b3e895" >tag.sig
+printf "type tagsssssssssssssssssssssssssssssss" >>tag.sig
+
+check_verify_failure '"type" line eol check' '^error: char48: .*"\\n"$'
+
+############################################################
+# 6. tag line label check #1
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+type tag
+xxx mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure '"tag" line label check #1' \
+ '^error: char57: no "tag " found$'
+
+############################################################
+# 7. tag line label check #2
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+type taggggggggggggggggggggggggggggggg
+tag
+EOF
+
+check_verify_failure '"tag" line label check #2' \
+ '^error: char87: no "tag " found$'
+
+############################################################
+# 8. type line type-name length check
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+type taggggggggggggggggggggggggggggggg
+tag mytag
+EOF
+
+check_verify_failure '"type" line type-name length check' \
+ '^error: char53: type too long$'
+
+############################################################
+# 9. verify object (SHA1/type) check
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+type tagggg
+tag mytag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify object (SHA1/type) check' \
+ '^error: char7: could not verify object.*$'
+
+############################################################
+# 10. verify tag-name check
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag my tag
+tagger . <> 0 +0000
+
+EOF
+
+check_verify_failure 'verify tag-name check' \
+ '^error: char67: could not verify tag name$'
+
+############################################################
+# 11. tagger line label check #1
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+
+This is filler
+EOF
+
+check_verify_failure '"tagger" line label check #1' \
+ '^error: char70: could not find "tagger "$'
+
+############################################################
+# 12. tagger line label check #2
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger
+
+This is filler
+EOF
+
+check_verify_failure '"tagger" line label check #2' \
+ '^error: char70: could not find "tagger "$'
+
+############################################################
+# 13. disallow missing tag author name
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger <> 0 +0000
+
+This is filler
+EOF
+
+check_verify_failure 'disallow missing tag author name' \
+ '^error: char77: missing tagger name$'
+
+############################################################
+# 14. disallow missing tag author name
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <
+ > 0 +0000
+
+EOF
+
+check_verify_failure 'disallow malformed tagger' \
+ '^error: char77: malformed tagger field$'
+
+############################################################
+# 15. allow empty tag email
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <> 0 +0000
+
+EOF
+
+test_expect_success \
+ 'allow empty tag email' \
+ 'git mktag <tag.sig >.git/refs/tags/mytag 2>message'
+
+############################################################
+# 16. disallow spaces in tag email
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tag ger@example.com> 0 +0000
+
+EOF
+
+check_verify_failure 'disallow spaces in tag email' \
+ '^error: char77: malformed tagger field$'
+
+############################################################
+# 17. disallow missing tag timestamp
+
+tr '_' ' ' >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com>__
+
+EOF
+
+check_verify_failure 'disallow missing tag timestamp' \
+ '^error: char107: missing tag timestamp$'
+
+############################################################
+# 18. detect invalid tag timestamp1
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008
+
+EOF
+
+check_verify_failure 'detect invalid tag timestamp1' \
+ '^error: char107: missing tag timestamp$'
+
+############################################################
+# 19. detect invalid tag timestamp2
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500
+
+EOF
+
+check_verify_failure 'detect invalid tag timestamp2' \
+ '^error: char111: malformed tag timestamp$'
+
+############################################################
+# 20. detect invalid tag timezone1
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 GMT
+
+EOF
+
+check_verify_failure 'detect invalid tag timezone1' \
+ '^error: char118: malformed tag timezone$'
+
+############################################################
+# 21. detect invalid tag timezone2
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 + 30
+
+EOF
+
+check_verify_failure 'detect invalid tag timezone2' \
+ '^error: char118: malformed tag timezone$'
+
+############################################################
+# 22. detect invalid tag timezone3
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -1430
+
+EOF
+
+check_verify_failure 'detect invalid tag timezone3' \
+ '^error: char118: malformed tag timezone$'
+
+############################################################
+# 23. detect invalid header entry
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+this line should not be here
+
+EOF
+
+check_verify_failure 'detect invalid header entry' \
+ '^error: char124: trailing garbage in tag header$'
+
+############################################################
+# 24. create valid tag
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger T A Gger <tagger@example.com> 1206478233 -0500
+
+EOF
+
+test_expect_success \
+ 'create valid tag' \
+ 'git mktag <tag.sig >.git/refs/tags/mytag 2>message'
+
+############################################################
+# 25. check mytag
+
+test_expect_success \
+ 'check mytag' \
+ 'git tag -l | grep mytag'
+
+
+test_done
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
new file mode 100755
index 0000000..256c4c9
--- /dev/null
+++ b/t/t3900-i18n-commit.sh
@@ -0,0 +1,136 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='commit and log output encodings'
+
+. ./test-lib.sh
+
+compare_with () {
+ git show -s $1 | sed -e '1,/^$/d' -e 's/^ //' >current &&
+ case "$3" in
+ '')
+ test_cmp "$2" current ;;
+ ?*)
+ iconv -f "$3" -t UTF-8 >current.utf8 <current &&
+ iconv -f "$3" -t UTF-8 >expect.utf8 <"$2" &&
+ test_cmp expect.utf8 current.utf8
+ ;;
+ esac
+}
+
+test_expect_success setup '
+ : >F &&
+ git add F &&
+ T=$(git write-tree) &&
+ C=$(git commit-tree $T <"$TEST_DIRECTORY"/t3900/1-UTF-8.txt) &&
+ git update-ref HEAD $C &&
+ git tag C0
+'
+
+test_expect_success 'no encoding header for base case' '
+ E=$(git cat-file commit C0 | sed -ne "s/^encoding //p") &&
+ test z = "z$E"
+'
+
+for H in ISO8859-1 eucJP ISO-2022-JP
+do
+ test_expect_success "$H setup" '
+ git config i18n.commitencoding $H &&
+ git checkout -b $H C0 &&
+ echo $H >F &&
+ git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt
+ '
+done
+
+for H in ISO8859-1 eucJP ISO-2022-JP
+do
+ test_expect_success "check encoding header for $H" '
+ E=$(git cat-file commit '$H' | sed -ne "s/^encoding //p") &&
+ test "z$E" = "z'$H'"
+ '
+done
+
+test_expect_success 'config to remove customization' '
+ git config --unset-all i18n.commitencoding &&
+ if Z=$(git config --get-all i18n.commitencoding)
+ then
+ echo Oops, should have failed.
+ false
+ else
+ test z = "z$Z"
+ fi &&
+ git config i18n.commitencoding UTF-8
+'
+
+test_expect_success 'ISO8859-1 should be shown in UTF-8 now' '
+ compare_with ISO8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
+'
+
+for H in eucJP ISO-2022-JP
+do
+ test_expect_success "$H should be shown in UTF-8 now" '
+ compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
+ '
+done
+
+test_expect_success 'config to add customization' '
+ git config --unset-all i18n.commitencoding &&
+ if Z=$(git config --get-all i18n.commitencoding)
+ then
+ echo Oops, should have failed.
+ false
+ else
+ test z = "z$Z"
+ fi
+'
+
+for H in ISO8859-1 eucJP ISO-2022-JP
+do
+ test_expect_success "$H should be shown in itself now" '
+ git config i18n.commitencoding '$H' &&
+ compare_with '$H' "$TEST_DIRECTORY"/t3900/'$H'.txt
+ '
+done
+
+test_expect_success 'config to tweak customization' '
+ git config i18n.logoutputencoding UTF-8
+'
+
+test_expect_success 'ISO8859-1 should be shown in UTF-8 now' '
+ compare_with ISO8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
+'
+
+for H in eucJP ISO-2022-JP
+do
+ test_expect_success "$H should be shown in UTF-8 now" '
+ compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
+ '
+done
+
+for J in eucJP ISO-2022-JP
+do
+ if test "$J" = ISO-2022-JP
+ then
+ ICONV=$J
+ else
+ ICONV=
+ fi
+ git config i18n.logoutputencoding $J
+ for H in eucJP ISO-2022-JP
+ do
+ test_expect_success "$H should be shown in $J now" '
+ compare_with '$H' "$TEST_DIRECTORY"/t3900/'$J'.txt $ICONV
+ '
+ done
+done
+
+for H in ISO8859-1 eucJP ISO-2022-JP
+do
+ test_expect_success "No conversion with $H" '
+ compare_with "--encoding=none '$H'" "$TEST_DIRECTORY"/t3900/'$H'.txt
+ '
+done
+
+test_done
diff --git a/t/t3900/1-UTF-8.txt b/t/t3900/1-UTF-8.txt
new file mode 100644
index 0000000..ee31e19
--- /dev/null
+++ b/t/t3900/1-UTF-8.txt
@@ -0,0 +1,3 @@
+ÄËÑÃÖ
+
+Ãbçdèfg
diff --git a/t/t3900/2-UTF-8.txt b/t/t3900/2-UTF-8.txt
new file mode 100644
index 0000000..63f4f8f
--- /dev/null
+++ b/t/t3900/2-UTF-8.txt
@@ -0,0 +1,4 @@
+ã¯ã‚Œã²ã»ãµ
+
+ã—ã¦ã„ã‚‹ã®ãŒã€ã„ã‚‹ã®ã§ã€‚
+濱浜ã»ã‚Œã·ã‚Šã½ã‚Œã¾ã³ãã‚Šã‚ã¸ã€‚
diff --git a/t/t3900/ISO-2022-JP.txt b/t/t3900/ISO-2022-JP.txt
new file mode 100644
index 0000000..74b5330
--- /dev/null
+++ b/t/t3900/ISO-2022-JP.txt
@@ -0,0 +1,4 @@
+$B$O$l$R$[$U(B
+
+$B$7$F$$$k$N$,!"$$$k$N$G!#(B
+$B_@IM$[$l$W$j$]$l$^$S$0$j$m$X!#(B
diff --git a/t/t3900/ISO8859-1.txt b/t/t3900/ISO8859-1.txt
new file mode 100644
index 0000000..7cbef0e
--- /dev/null
+++ b/t/t3900/ISO8859-1.txt
@@ -0,0 +1,3 @@
+ÄËÑÏÖ
+
+Ábçdèfg
diff --git a/t/t3900/eucJP.txt b/t/t3900/eucJP.txt
new file mode 100644
index 0000000..546f2aa
--- /dev/null
+++ b/t/t3900/eucJP.txt
@@ -0,0 +1,4 @@
+¤Ï¤ì¤Ò¤Û¤Õ
+
+¤·¤Æ¤¤¤ë¤Î¤¬¡¢¤¤¤ë¤Î¤Ç¡£
+ßÀÉͤۤì¤×¤ê¤Ý¤ì¤Þ¤Ó¤°¤ê¤í¤Ø¡£
diff --git a/t/t3901-8859-1.txt b/t/t3901-8859-1.txt
new file mode 100755
index 0000000..38c21a6
--- /dev/null
+++ b/t/t3901-8859-1.txt
@@ -0,0 +1,4 @@
+: to be sourced in t3901 -- this is latin-1
+GIT_AUTHOR_NAME="Áéí óú" &&
+GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
+export GIT_AUTHOR_NAME GIT_COMMITTER_NAME
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
new file mode 100755
index 0000000..31a5770
--- /dev/null
+++ b/t/t3901-i18n-patch.sh
@@ -0,0 +1,255 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='i18n settings and format-patch | am pipe'
+
+. ./test-lib.sh
+
+check_encoding () {
+ # Make sure characters are not corrupted
+ cnt="$1" header="$2" i=1 j=0 bad=0
+ while test "$i" -le $cnt
+ do
+ git format-patch --encoding=UTF-8 --stdout HEAD~$i..HEAD~$j |
+ grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD=20=C3=B3=C3=BA?=" &&
+ git cat-file commit HEAD~$j |
+ case "$header" in
+ 8859)
+ grep "^encoding ISO8859-1" ;;
+ *)
+ grep "^encoding ISO8859-1"; test "$?" != 0 ;;
+ esac || {
+ bad=1
+ break
+ }
+ j=$i
+ i=$(($i+1))
+ done
+ (exit $bad)
+}
+
+test_expect_success setup '
+ git config i18n.commitencoding UTF-8 &&
+
+ # use UTF-8 in author and committer name to match the
+ # i18n.commitencoding settings
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+
+ test_tick &&
+ echo "$GIT_AUTHOR_NAME" >mine &&
+ git add mine &&
+ git commit -s -m "Initial commit" &&
+
+ test_tick &&
+ echo Hello world >mine &&
+ git add mine &&
+ git commit -s -m "Second on main" &&
+
+ # the first commit on the side branch is UTF-8
+ test_tick &&
+ git checkout -b side master^ &&
+ echo Another file >yours &&
+ git add yours &&
+ git commit -s -m "Second on side" &&
+
+ # the second one on the side branch is ISO-8859-1
+ git config i18n.commitencoding ISO8859-1 &&
+ # use author and committer name in ISO-8859-1 to match it.
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+ test_tick &&
+ echo Yet another >theirs &&
+ git add theirs &&
+ git commit -s -m "Third on side" &&
+
+ # Back to default
+ git config i18n.commitencoding UTF-8
+'
+
+test_expect_success 'format-patch output (ISO-8859-1)' '
+ git config i18n.logoutputencoding ISO8859-1 &&
+
+ git format-patch --stdout master..HEAD^ >out-l1 &&
+ git format-patch --stdout HEAD^ >out-l2 &&
+ grep "^Content-Type: text/plain; charset=ISO8859-1" out-l1 &&
+ grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l1 &&
+ grep "^Content-Type: text/plain; charset=ISO8859-1" out-l2 &&
+ grep "^From: =?ISO8859-1?q?=C1=E9=ED=20=F3=FA?=" out-l2
+'
+
+test_expect_success 'format-patch output (UTF-8)' '
+ git config i18n.logoutputencoding UTF-8 &&
+
+ git format-patch --stdout master..HEAD^ >out-u1 &&
+ git format-patch --stdout HEAD^ >out-u2 &&
+ grep "^Content-Type: text/plain; charset=UTF-8" out-u1 &&
+ grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD=20=C3=B3=C3=BA?=" out-u1 &&
+ grep "^Content-Type: text/plain; charset=UTF-8" out-u2 &&
+ grep "^From: =?UTF-8?q?=C3=81=C3=A9=C3=AD=20=C3=B3=C3=BA?=" out-u2
+'
+
+test_expect_success 'rebase (U/U)' '
+ # We want the result of rebase in UTF-8
+ git config i18n.commitencoding UTF-8 &&
+
+ # The test is about logoutputencoding not affecting the
+ # final outcome -- it is used internally to generate the
+ # patch and the log.
+
+ git config i18n.logoutputencoding UTF-8 &&
+
+ # The result will be committed by GIT_COMMITTER_NAME --
+ # we want UTF-8 encoded name.
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+ git checkout -b test &&
+ git rebase master &&
+
+ check_encoding 2
+'
+
+test_expect_success 'rebase (U/L)' '
+ git config i18n.commitencoding UTF-8 &&
+ git config i18n.logoutputencoding ISO8859-1 &&
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+
+ git reset --hard side &&
+ git rebase master &&
+
+ check_encoding 2
+'
+
+test_expect_success 'rebase (L/L)' '
+ # In this test we want ISO-8859-1 encoded commits as the result
+ git config i18n.commitencoding ISO8859-1 &&
+ git config i18n.logoutputencoding ISO8859-1 &&
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+
+ git reset --hard side &&
+ git rebase master &&
+
+ check_encoding 2 8859
+'
+
+test_expect_success 'rebase (L/U)' '
+ # This is pathological -- use UTF-8 as intermediate form
+ # to get ISO-8859-1 results.
+ git config i18n.commitencoding ISO8859-1 &&
+ git config i18n.logoutputencoding UTF-8 &&
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+
+ git reset --hard side &&
+ git rebase master &&
+
+ check_encoding 2 8859
+'
+
+test_expect_success 'cherry-pick(U/U)' '
+ # Both the commitencoding and logoutputencoding is set to UTF-8.
+
+ git config i18n.commitencoding UTF-8 &&
+ git config i18n.logoutputencoding UTF-8 &&
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+
+ git reset --hard master &&
+ git cherry-pick side^ &&
+ git cherry-pick side &&
+ git revert HEAD &&
+
+ check_encoding 3
+'
+
+test_expect_success 'cherry-pick(L/L)' '
+ # Both the commitencoding and logoutputencoding is set to ISO-8859-1
+
+ git config i18n.commitencoding ISO8859-1 &&
+ git config i18n.logoutputencoding ISO8859-1 &&
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+
+ git reset --hard master &&
+ git cherry-pick side^ &&
+ git cherry-pick side &&
+ git revert HEAD &&
+
+ check_encoding 3 8859
+'
+
+test_expect_success 'cherry-pick(U/L)' '
+ # Commitencoding is set to UTF-8 but logoutputencoding is ISO-8859-1
+
+ git config i18n.commitencoding UTF-8 &&
+ git config i18n.logoutputencoding ISO8859-1 &&
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+
+ git reset --hard master &&
+ git cherry-pick side^ &&
+ git cherry-pick side &&
+ git revert HEAD &&
+
+ check_encoding 3
+'
+
+test_expect_success 'cherry-pick(L/U)' '
+ # Again, the commitencoding is set to ISO-8859-1 but
+ # logoutputencoding is set to UTF-8.
+
+ git config i18n.commitencoding ISO8859-1 &&
+ git config i18n.logoutputencoding UTF-8 &&
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+
+ git reset --hard master &&
+ git cherry-pick side^ &&
+ git cherry-pick side &&
+ git revert HEAD &&
+
+ check_encoding 3 8859
+'
+
+test_expect_success 'rebase --merge (U/U)' '
+ git config i18n.commitencoding UTF-8 &&
+ git config i18n.logoutputencoding UTF-8 &&
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+
+ git reset --hard side &&
+ git rebase --merge master &&
+
+ check_encoding 2
+'
+
+test_expect_success 'rebase --merge (U/L)' '
+ git config i18n.commitencoding UTF-8 &&
+ git config i18n.logoutputencoding ISO8859-1 &&
+ . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+
+ git reset --hard side &&
+ git rebase --merge master &&
+
+ check_encoding 2
+'
+
+test_expect_success 'rebase --merge (L/L)' '
+ # In this test we want ISO-8859-1 encoded commits as the result
+ git config i18n.commitencoding ISO8859-1 &&
+ git config i18n.logoutputencoding ISO8859-1 &&
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+
+ git reset --hard side &&
+ git rebase --merge master &&
+
+ check_encoding 2 8859
+'
+
+test_expect_success 'rebase --merge (L/U)' '
+ # This is pathological -- use UTF-8 as intermediate form
+ # to get ISO-8859-1 results.
+ git config i18n.commitencoding ISO8859-1 &&
+ git config i18n.logoutputencoding UTF-8 &&
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+
+ git reset --hard side &&
+ git rebase --merge master &&
+
+ check_encoding 2 8859
+'
+
+test_done
diff --git a/t/t3901-utf8.txt b/t/t3901-utf8.txt
new file mode 100755
index 0000000..5f5205c
--- /dev/null
+++ b/t/t3901-utf8.txt
@@ -0,0 +1,4 @@
+: to be sourced in t3901 -- this is utf8
+GIT_AUTHOR_NAME="Ãéí óú" &&
+GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
+export GIT_AUTHOR_NAME GIT_COMMITTER_NAME
diff --git a/t/t3902-quoted.sh b/t/t3902-quoted.sh
new file mode 100755
index 0000000..147e634
--- /dev/null
+++ b/t/t3902-quoted.sh
@@ -0,0 +1,150 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='quoted output'
+
+. ./test-lib.sh
+
+FN='濱野'
+GN='ç´”'
+HT=' '
+LF='
+'
+DQ='"'
+
+echo foo 2>/dev/null > "Name and an${HT}HT"
+test -f "Name and an${HT}HT" || {
+ # since FAT/NTFS does not allow tabs in filenames, skip this test
+ skip_all='Your filesystem does not allow tabs in filenames, test skipped.'
+ test_done
+}
+
+for_each_name () {
+ for name in \
+ Name "Name and a${LF}LF" "Name and an${HT}HT" "Name${DQ}" \
+ "$FN$HT$GN" "$FN$LF$GN" "$FN $GN" "$FN$GN" "$FN$DQ$GN" \
+ "With SP in it" "$FN/file"
+ do
+ eval "$1"
+ done
+}
+
+test_expect_success setup '
+
+ mkdir "$FN" &&
+ for_each_name "echo initial >\"\$name\""
+ git add . &&
+ git commit -q -m Initial &&
+
+ for_each_name "echo second >\"\$name\"" &&
+ git commit -a -m Second
+
+ for_each_name "echo modified >\"\$name\""
+
+'
+
+cat >expect.quoted <<\EOF
+Name
+"Name and a\nLF"
+"Name and an\tHT"
+"Name\""
+With SP in it
+"\346\277\261\351\207\216\t\347\264\224"
+"\346\277\261\351\207\216\n\347\264\224"
+"\346\277\261\351\207\216 \347\264\224"
+"\346\277\261\351\207\216\"\347\264\224"
+"\346\277\261\351\207\216/file"
+"\346\277\261\351\207\216\347\264\224"
+EOF
+
+cat >expect.raw <<\EOF
+Name
+"Name and a\nLF"
+"Name and an\tHT"
+"Name\""
+With SP in it
+"濱野\t純"
+"濱野\n純"
+濱野 純
+"濱野\"純"
+濱野/file
+濱野純
+EOF
+
+test_expect_success 'check fully quoted output from ls-files' '
+
+ git ls-files >current && test_cmp expect.quoted current
+
+'
+
+test_expect_success 'check fully quoted output from diff-files' '
+
+ git diff --name-only >current &&
+ test_cmp expect.quoted current
+
+'
+
+test_expect_success 'check fully quoted output from diff-index' '
+
+ git diff --name-only HEAD >current &&
+ test_cmp expect.quoted current
+
+'
+
+test_expect_success 'check fully quoted output from diff-tree' '
+
+ git diff --name-only HEAD^ HEAD >current &&
+ test_cmp expect.quoted current
+
+'
+
+test_expect_success 'check fully quoted output from ls-tree' '
+
+ git ls-tree --name-only -r HEAD >current &&
+ test_cmp expect.quoted current
+
+'
+
+test_expect_success 'setting core.quotepath' '
+
+ git config --bool core.quotepath false
+
+'
+
+test_expect_success 'check fully quoted output from ls-files' '
+
+ git ls-files >current && test_cmp expect.raw current
+
+'
+
+test_expect_success 'check fully quoted output from diff-files' '
+
+ git diff --name-only >current &&
+ test_cmp expect.raw current
+
+'
+
+test_expect_success 'check fully quoted output from diff-index' '
+
+ git diff --name-only HEAD >current &&
+ test_cmp expect.raw current
+
+'
+
+test_expect_success 'check fully quoted output from diff-tree' '
+
+ git diff --name-only HEAD^ HEAD >current &&
+ test_cmp expect.raw current
+
+'
+
+test_expect_success 'check fully quoted output from ls-tree' '
+
+ git ls-tree --name-only -r HEAD >current &&
+ test_cmp expect.raw current
+
+'
+
+test_done
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
new file mode 100755
index 0000000..62e208a
--- /dev/null
+++ b/t/t3903-stash.sh
@@ -0,0 +1,381 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E Schindelin
+#
+
+test_description='Test git stash'
+
+. ./test-lib.sh
+
+test_expect_success 'stash some dirty working directory' '
+ echo 1 > file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ echo 2 > file &&
+ git add file &&
+ echo 3 > file &&
+ test_tick &&
+ git stash &&
+ git diff-files --quiet &&
+ git diff-index --cached --quiet HEAD
+'
+
+cat > expect << EOF
+diff --git a/file b/file
+index 0cfbf08..00750ed 100644
+--- a/file
++++ b/file
+@@ -1 +1 @@
+-2
++3
+EOF
+
+test_expect_success 'parents of stash' '
+ test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
+ git diff stash^2..stash > output &&
+ test_cmp output expect
+'
+
+test_expect_success 'apply needs clean working directory' '
+ echo 4 > other-file &&
+ git add other-file &&
+ echo 5 > other-file &&
+ test_must_fail git stash apply
+'
+
+test_expect_success 'apply stashed changes' '
+ git add other-file &&
+ test_tick &&
+ git commit -m other-file &&
+ git stash apply &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'apply stashed changes (including index)' '
+ git reset --hard HEAD^ &&
+ echo 6 > other-file &&
+ git add other-file &&
+ test_tick &&
+ git commit -m other-file &&
+ git stash apply --index &&
+ test 3 = $(cat file) &&
+ test 2 = $(git show :file) &&
+ test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'unstashing in a subdirectory' '
+ git reset --hard HEAD &&
+ mkdir subdir &&
+ cd subdir &&
+ git stash apply &&
+ cd ..
+'
+
+test_expect_success 'drop top stash' '
+ git reset --hard &&
+ git stash list > stashlist1 &&
+ echo 7 > file &&
+ git stash &&
+ git stash drop &&
+ git stash list > stashlist2 &&
+ test_cmp stashlist1 stashlist2 &&
+ git stash apply &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file)
+'
+
+test_expect_success 'drop middle stash' '
+ git reset --hard &&
+ echo 8 > file &&
+ git stash &&
+ echo 9 > file &&
+ git stash &&
+ git stash drop stash@{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 &&
+ test 3 = $(cat file) &&
+ test 1 = $(git show :file) &&
+ test 1 = $(git show HEAD:file) &&
+ test 0 = $(git stash list | wc -l)
+'
+
+cat > expect << EOF
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..1fe912c
+--- /dev/null
++++ b/file2
+@@ -0,0 +1 @@
++bar2
+EOF
+
+cat > expect1 << EOF
+diff --git a/file b/file
+index 257cc56..5716ca5 100644
+--- a/file
++++ b/file
+@@ -1 +1 @@
+-foo
++bar
+EOF
+
+cat > expect2 << EOF
+diff --git a/file b/file
+index 7601807..5716ca5 100644
+--- a/file
++++ b/file
+@@ -1 +1 @@
+-baz
++bar
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..1fe912c
+--- /dev/null
++++ b/file2
+@@ -0,0 +1 @@
++bar2
+EOF
+
+test_expect_success 'stash branch' '
+ echo foo > file &&
+ git commit file -m first
+ echo bar > file &&
+ echo bar2 > file2 &&
+ git add file2 &&
+ git stash &&
+ echo baz > file &&
+ git commit file -m second &&
+ git stash branch stashbranch &&
+ test refs/heads/stashbranch = $(git symbolic-ref HEAD) &&
+ test $(git rev-parse HEAD) = $(git rev-parse master^) &&
+ git diff --cached > output &&
+ test_cmp output expect &&
+ git diff > output &&
+ test_cmp output expect1 &&
+ git add file &&
+ git commit -m alternate\ second &&
+ git diff master..stashbranch > output &&
+ test_cmp output expect2 &&
+ test 0 = $(git stash list | wc -l)
+'
+
+test_expect_success 'apply -q is quiet' '
+ echo foo > file &&
+ git stash &&
+ git stash apply -q > output.out 2>&1 &&
+ test ! -s output.out
+'
+
+test_expect_success 'save -q is quiet' '
+ git stash save --quiet > output.out 2>&1 &&
+ test ! -s output.out
+'
+
+test_expect_success 'pop -q is quiet' '
+ git stash pop -q > output.out 2>&1 &&
+ test ! -s output.out
+'
+
+test_expect_success 'pop -q --index works and is quiet' '
+ echo foo > file &&
+ git add file &&
+ git stash save --quiet &&
+ git stash pop -q --index > output.out 2>&1 &&
+ test foo = "$(git show :file)" &&
+ test ! -s output.out
+'
+
+test_expect_success 'drop -q is quiet' '
+ git stash &&
+ git stash drop -q > output.out 2>&1 &&
+ test ! -s output.out
+'
+
+test_expect_success 'stash -k' '
+ echo bar3 > file &&
+ echo bar4 > file2 &&
+ git add file2 &&
+ git stash -k &&
+ test bar,bar4 = $(cat file),$(cat file2)
+'
+
+test_expect_success 'stash --invalid-option' '
+ echo bar5 > file &&
+ echo bar6 > file2 &&
+ git add file2 &&
+ test_must_fail git stash --invalid-option &&
+ test_must_fail git stash save --invalid-option &&
+ test bar5,bar6 = $(cat file),$(cat file2) &&
+ git stash -- -message-starting-with-dash &&
+ test bar,bar2 = $(cat file),$(cat file2)
+'
+
+test_expect_success 'stash an added file' '
+ git reset --hard &&
+ echo new >file3 &&
+ git add file3 &&
+ git stash save "added file" &&
+ ! test -r file3 &&
+ git stash apply &&
+ test new = "$(cat file3)"
+'
+
+test_expect_success 'stash rm then recreate' '
+ git reset --hard &&
+ git rm file &&
+ echo bar7 >file &&
+ git stash save "rm then recreate" &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ test bar7 = "$(cat file)"
+'
+
+test_expect_success 'stash rm and ignore' '
+ git reset --hard &&
+ git rm file &&
+ echo file >.gitignore &&
+ git stash save "rm and ignore" &&
+ test bar = "$(cat file)" &&
+ test file = "$(cat .gitignore)"
+ git stash apply &&
+ ! test -r file &&
+ test file = "$(cat .gitignore)"
+'
+
+test_expect_success 'stash rm and ignore (stage .gitignore)' '
+ git reset --hard &&
+ git rm file &&
+ echo file >.gitignore &&
+ git add .gitignore &&
+ git stash save "rm and ignore (stage .gitignore)" &&
+ test bar = "$(cat file)" &&
+ ! test -r .gitignore
+ git stash apply &&
+ ! test -r file &&
+ test file = "$(cat .gitignore)"
+'
+
+test_expect_success SYMLINKS 'stash file to symlink' '
+ git reset --hard &&
+ rm file &&
+ ln -s file2 file &&
+ git stash save "file to symlink" &&
+ test -f file &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+test_expect_success SYMLINKS 'stash file to symlink (stage rm)' '
+ git reset --hard &&
+ git rm file &&
+ ln -s file2 file &&
+ git stash save "file to symlink (stage rm)" &&
+ test -f file &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+test_expect_success SYMLINKS 'stash file to symlink (full stage)' '
+ git reset --hard &&
+ rm file &&
+ ln -s file2 file &&
+ git add file &&
+ git stash save "file to symlink (full stage)" &&
+ test -f file &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ case "$(ls -l file)" in *" file -> file2") :;; *) false;; esac
+'
+
+# This test creates a commit with a symlink used for the following tests
+
+test_expect_success SYMLINKS 'stash symlink to file' '
+ git reset --hard &&
+ ln -s file filelink &&
+ git add filelink &&
+ git commit -m "Add symlink" &&
+ rm filelink &&
+ cp file filelink &&
+ git stash save "symlink to file" &&
+ test -h filelink &&
+ case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+ git stash apply &&
+ ! test -h filelink &&
+ test bar = "$(cat file)"
+'
+
+test_expect_success SYMLINKS 'stash symlink to file (stage rm)' '
+ git reset --hard &&
+ git rm filelink &&
+ cp file filelink &&
+ git stash save "symlink to file (stage rm)" &&
+ test -h filelink &&
+ case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+ git stash apply &&
+ ! test -h filelink &&
+ test bar = "$(cat file)"
+'
+
+test_expect_success SYMLINKS 'stash symlink to file (full stage)' '
+ git reset --hard &&
+ rm filelink &&
+ cp file filelink &&
+ git add filelink &&
+ git stash save "symlink to file (full stage)" &&
+ test -h filelink &&
+ case "$(ls -l filelink)" in *" filelink -> file") :;; *) false;; esac &&
+ git stash apply &&
+ ! test -h filelink &&
+ test bar = "$(cat file)"
+'
+
+test_expect_failure 'stash directory to file' '
+ git reset --hard &&
+ mkdir dir &&
+ echo foo >dir/file &&
+ git add dir/file &&
+ git commit -m "Add file in dir" &&
+ rm -fr dir &&
+ echo bar >dir &&
+ git stash save "directory to file" &&
+ test -d dir &&
+ test foo = "$(cat dir/file)" &&
+ test_must_fail git stash apply &&
+ test bar = "$(cat dir)" &&
+ git reset --soft HEAD^
+'
+
+test_expect_failure 'stash file to directory' '
+ git reset --hard &&
+ rm file &&
+ mkdir file &&
+ echo foo >file/file &&
+ git stash save "file to directory" &&
+ test -f file &&
+ test bar = "$(cat file)" &&
+ git stash apply &&
+ test -f file/file &&
+ test foo = "$(cat file/file)"
+'
+
+test_done
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
new file mode 100755
index 0000000..f37e3bc
--- /dev/null
+++ b/t/t3904-stash-patch.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='git checkout --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+ mkdir dir &&
+ echo parent > dir/foo &&
+ echo dummy > bar &&
+ git add bar dir/foo &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ echo index > dir/foo &&
+ git add dir/foo &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+# note: bar sorts before dir, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+ set_state dir/foo work index
+ (echo n; echo n) | test_must_fail git stash save -p &&
+ verify_state dir/foo work index &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git stash -p' '
+ (echo n; echo y) | git stash save -p &&
+ verify_state dir/foo head index &&
+ verify_saved_state bar &&
+ git reset --hard &&
+ git stash apply &&
+ verify_state dir/foo work head &&
+ verify_state bar dummy dummy
+'
+
+test_expect_success 'git stash -p --no-keep-index' '
+ set_state dir/foo work index &&
+ set_state bar bar_work bar_index &&
+ (echo n; echo y) | git stash save -p --no-keep-index &&
+ verify_state dir/foo head head &&
+ verify_state bar bar_work dummy &&
+ git reset --hard &&
+ git stash apply --index &&
+ verify_state dir/foo work index &&
+ verify_state bar dummy bar_index
+'
+
+test_expect_success 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+test_done
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
new file mode 100755
index 0000000..6ddd469
--- /dev/null
+++ b/t/t4000-diff-format.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test built-in diff output engine.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+echo >path0 'Line 1
+Line 2
+line 3'
+cat path0 >path1
+chmod +x path1
+
+test_expect_success \
+ 'update-index --add two files with and without +x.' \
+ 'git update-index --add path0 path1'
+
+mv path0 path0-
+sed -e 's/line/Line/' <path0- >path0
+chmod +x path0
+rm -f path1
+test_expect_success \
+ 'git diff-files -p after editing work tree.' \
+ 'git diff-files -p >current'
+
+# that's as far as it comes
+if [ "$(git config --get core.filemode)" = false ]
+then
+ say 'filemode disabled on the filesystem'
+ test_done
+fi
+
+cat >expected <<\EOF
+diff --git a/path0 b/path0
+old mode 100644
+new mode 100755
+--- a/path0
++++ b/path0
+@@ -1,3 +1,3 @@
+ Line 1
+ Line 2
+-line 3
++Line 3
+diff --git a/path1 b/path1
+deleted file mode 100755
+--- a/path1
++++ /dev/null
+@@ -1,3 +0,0 @@
+-Line 1
+-Line 2
+-line 3
+EOF
+
+test_expect_success \
+ 'validate git diff-files -p output.' \
+ 'compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
new file mode 100755
index 0000000..71bac83
--- /dev/null
+++ b/t/t4001-diff-rename.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test rename detection in diff engine.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+echo >path0 'Line 1
+Line 2
+Line 3
+Line 4
+Line 5
+Line 6
+Line 7
+Line 8
+Line 9
+Line 10
+line 11
+Line 12
+Line 13
+Line 14
+Line 15
+'
+
+test_expect_success \
+ 'update-index --add a file.' \
+ 'git update-index --add path0'
+
+test_expect_success \
+ 'write that tree.' \
+ 'tree=$(git write-tree) && echo $tree'
+
+sed -e 's/line/Line/' <path0 >path1
+rm -f path0
+test_expect_success \
+ 'renamed and edited the file.' \
+ 'git update-index --add --remove path0 path1'
+
+test_expect_success \
+ 'git diff-index -p -M after rename and editing.' \
+ 'git diff-index -p -M $tree >current'
+cat >expected <<\EOF
+diff --git a/path0 b/path1
+rename from path0
+rename to path1
+--- a/path0
++++ b/path1
+@@ -8,7 +8,7 @@ Line 7
+ Line 8
+ Line 9
+ Line 10
+-line 11
++Line 11
+ Line 12
+ Line 13
+ Line 14
+EOF
+
+test_expect_success \
+ 'validate the output.' \
+ 'compare_diff_patch current expected'
+
+test_expect_success 'favour same basenames over different ones' '
+ cp path1 another-path &&
+ git add another-path &&
+ git commit -m 1 &&
+ git rm path1 &&
+ mkdir subdir &&
+ git mv another-path subdir/path1 &&
+ git status | grep "renamed: .*path1 -> subdir/path1"'
+
+test_expect_success 'favour same basenames even with minor differences' '
+ git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
+ git status | grep "renamed: .*path1 -> subdir/path1"'
+
+test_done
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
new file mode 100755
index 0000000..73441a5
--- /dev/null
+++ b/t/t4002-diff-basic.sh
@@ -0,0 +1,269 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test diff raw-output.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
+
+cat >.test-plain-OA <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A AA
+:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A AN
+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD
+:000000 040000 0000000000000000000000000000000000000000 6d50f65d3bdab91c63444294d38f08aeff328e42 A DF
+:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D DM
+:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D DN
+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL
+:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M MD
+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M MM
+:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M MN
+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS
+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M TT
+:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 5e5f22072bb39f6e12cf663a57cb634c76eefb49 M Z
+EOF
+
+cat >.test-recursive-OA <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A AA
+:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A AN
+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD
+:000000 100644 0000000000000000000000000000000000000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 A DF/DF
+:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D DM
+:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D DN
+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL
+:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M MD
+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M MM
+:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M MN
+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS
+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M TT
+:000000 100644 0000000000000000000000000000000000000000 8acb8e9750e3f644bf323fcf3d338849db106c77 A Z/AA
+:000000 100644 0000000000000000000000000000000000000000 087494262084cefee7ed484d20c8dc0580791272 A Z/AN
+:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D Z/DD
+:100644 000000 9b541b2275c06e3a7b13f28badf5294e2ae63df4 0000000000000000000000000000000000000000 D Z/DM
+:100644 000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a 0000000000000000000000000000000000000000 D Z/DN
+:100644 100644 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 M Z/MD
+:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 61422ba9c2c873416061a88cd40a59a35b576474 M Z/MM
+:100644 100644 b16d7b25b869f2beb124efa53467d8a1550ad694 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd M Z/MN
+EOF
+cat >.test-plain-OB <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A AA
+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD
+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF
+:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M DM
+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL
+:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D MD
+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM
+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA
+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND
+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM
+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS
+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT
+:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 1ba523955d5160681af65cb776411f574c1e8155 M Z
+EOF
+cat >.test-recursive-OB <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A AA
+:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D DD
+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF
+:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M DM
+:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A LL
+:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D MD
+:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM
+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA
+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND
+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM
+:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M SS
+:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT
+:000000 100644 0000000000000000000000000000000000000000 6c0b99286d0bce551ac4a7b3dff8b706edff3715 A Z/AA
+:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D Z/DD
+:100644 100644 9b541b2275c06e3a7b13f28badf5294e2ae63df4 d77371d15817fcaa57eeec27f770c505ba974ec1 M Z/DM
+:100644 000000 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 0000000000000000000000000000000000000000 D Z/MD
+:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 697aad7715a1e7306ca76290a3dd4208fbaeddfa M Z/MM
+:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A Z/NA
+:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D Z/ND
+:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M Z/NM
+EOF
+cat >.test-plain-AB <<\EOF
+:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M AA
+:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D AN
+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF
+:040000 000000 6d50f65d3bdab91c63444294d38f08aeff328e42 0000000000000000000000000000000000000000 D DF
+:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A DM
+:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A DN
+:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D MD
+:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM
+:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M MN
+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA
+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND
+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM
+:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT
+:040000 040000 5e5f22072bb39f6e12cf663a57cb634c76eefb49 1ba523955d5160681af65cb776411f574c1e8155 M Z
+EOF
+cat >.test-recursive-AB <<\EOF
+:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M AA
+:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D AN
+:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A DF
+:100644 000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 0000000000000000000000000000000000000000 D DF/DF
+:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A DM
+:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A DN
+:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D MD
+:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M MM
+:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M MN
+:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A NA
+:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D ND
+:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M NM
+:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M TT
+:100644 100644 8acb8e9750e3f644bf323fcf3d338849db106c77 6c0b99286d0bce551ac4a7b3dff8b706edff3715 M Z/AA
+:100644 000000 087494262084cefee7ed484d20c8dc0580791272 0000000000000000000000000000000000000000 D Z/AN
+:000000 100644 0000000000000000000000000000000000000000 d77371d15817fcaa57eeec27f770c505ba974ec1 A Z/DM
+:000000 100644 0000000000000000000000000000000000000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a A Z/DN
+:100644 000000 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 0000000000000000000000000000000000000000 D Z/MD
+:100644 100644 61422ba9c2c873416061a88cd40a59a35b576474 697aad7715a1e7306ca76290a3dd4208fbaeddfa M Z/MM
+:100644 100644 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd b16d7b25b869f2beb124efa53467d8a1550ad694 M Z/MN
+:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A Z/NA
+:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D Z/ND
+:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M Z/NM
+EOF
+
+x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+x40="$x40$x40$x40$x40$x40$x40$x40$x40"
+z40='0000000000000000000000000000000000000000'
+cmp_diff_files_output () {
+ # diff-files never reports additions. Also it does not fill in the
+ # object ID for the changed files because it wants you to look at the
+ # filesystem.
+ sed <"$2" >.test-tmp \
+ -e '/^:000000 /d;s/'$x40'\( [MCRNDU][0-9]*\) /'$z40'\1 /' &&
+ test_cmp "$1" .test-tmp
+}
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git diff-tree $tree_O $tree_A >.test-a &&
+ cmp -s .test-a .test-plain-OA'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git diff-tree -r $tree_O $tree_A >.test-a &&
+ cmp -s .test-a .test-recursive-OA'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git diff-tree $tree_O $tree_B >.test-a &&
+ cmp -s .test-a .test-plain-OB'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git diff-tree -r $tree_O $tree_B >.test-a &&
+ cmp -s .test-a .test-recursive-OB'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git diff-tree $tree_A $tree_B >.test-a &&
+ cmp -s .test-a .test-plain-AB'
+
+test_expect_success \
+ 'diff-tree of known trees.' \
+ 'git diff-tree -r $tree_A $tree_B >.test-a &&
+ cmp -s .test-a .test-recursive-AB'
+
+test_expect_success \
+ 'diff-tree --stdin of known trees.' \
+ 'echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
+ echo $tree_A $tree_B > .test-plain-ABx &&
+ cat .test-plain-AB >> .test-plain-ABx &&
+ cmp -s .test-a .test-plain-ABx'
+
+test_expect_success \
+ 'diff-tree --stdin of known trees.' \
+ 'echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
+ echo $tree_A $tree_B > .test-recursive-ABx &&
+ cat .test-recursive-AB >> .test-recursive-ABx &&
+ cmp -s .test-a .test-recursive-ABx'
+
+test_expect_success \
+ 'diff-cache O with A in cache' \
+ 'git read-tree $tree_A &&
+ git diff-index --cached $tree_O >.test-a &&
+ cmp -s .test-a .test-recursive-OA'
+
+test_expect_success \
+ 'diff-cache O with B in cache' \
+ 'git read-tree $tree_B &&
+ git diff-index --cached $tree_O >.test-a &&
+ cmp -s .test-a .test-recursive-OB'
+
+test_expect_success \
+ 'diff-cache A with B in cache' \
+ 'git read-tree $tree_B &&
+ git diff-index --cached $tree_A >.test-a &&
+ cmp -s .test-a .test-recursive-AB'
+
+test_expect_success \
+ 'diff-files with O in cache and A checked out' \
+ 'rm -fr Z [A-Z][A-Z] &&
+ git read-tree $tree_A &&
+ git checkout-index -f -a &&
+ git read-tree --reset $tree_O || return 1
+ git update-index --refresh >/dev/null ;# this can exit non-zero
+ git diff-files >.test-a &&
+ cmp_diff_files_output .test-a .test-recursive-OA'
+
+test_expect_success \
+ 'diff-files with O in cache and B checked out' \
+ 'rm -fr Z [A-Z][A-Z] &&
+ git read-tree $tree_B &&
+ git checkout-index -f -a &&
+ git read-tree --reset $tree_O || return 1
+ git update-index --refresh >/dev/null ;# this can exit non-zero
+ git diff-files >.test-a &&
+ cmp_diff_files_output .test-a .test-recursive-OB'
+
+test_expect_success \
+ 'diff-files with A in cache and B checked out' \
+ 'rm -fr Z [A-Z][A-Z] &&
+ git read-tree $tree_B &&
+ git checkout-index -f -a &&
+ git read-tree --reset $tree_A || return 1
+ git update-index --refresh >/dev/null ;# this can exit non-zero
+ git diff-files >.test-a &&
+ cmp_diff_files_output .test-a .test-recursive-AB'
+
+################################################################
+# Now we have established the baseline, we do not have to
+# rely on individual object ID values that much.
+
+test_expect_success \
+ 'diff-tree O A == diff-tree -R A O' \
+ 'git diff-tree $tree_O $tree_A >.test-a &&
+ git diff-tree -R $tree_A $tree_O >.test-b &&
+ cmp -s .test-a .test-b'
+
+test_expect_success \
+ 'diff-tree -r O A == diff-tree -r -R A O' \
+ 'git diff-tree -r $tree_O $tree_A >.test-a &&
+ git diff-tree -r -R $tree_A $tree_O >.test-b &&
+ cmp -s .test-a .test-b'
+
+test_expect_success \
+ 'diff-tree B A == diff-tree -R A B' \
+ 'git diff-tree $tree_B $tree_A >.test-a &&
+ git diff-tree -R $tree_A $tree_B >.test-b &&
+ cmp -s .test-a .test-b'
+
+test_expect_success \
+ 'diff-tree -r B A == diff-tree -r -R A B' \
+ 'git diff-tree -r $tree_B $tree_A >.test-a &&
+ git diff-tree -r -R $tree_A $tree_B >.test-b &&
+ cmp -s .test-a .test-b'
+
+test_expect_success \
+ 'diff can read from stdin' \
+ 'test_must_fail git diff --no-index -- MN - < NN |
+ grep -v "^index" | sed "s#/-#/NN#" >.test-a &&
+ test_must_fail git diff --no-index -- MN NN |
+ grep -v "^index" >.test-b &&
+ test_cmp .test-a .test-b'
+
+test_done
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
new file mode 100755
index 0000000..c6130c4
--- /dev/null
+++ b/t/t4003-diff-rename-1.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='More rename detection
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ echo frotz >rezrov &&
+ git update-index --add COPYING rezrov &&
+ tree=$(git write-tree) &&
+ echo $tree'
+
+test_expect_success \
+ 'prepare work tree' \
+ 'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+ sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+ rm -f COPYING &&
+ git update-index --add --remove COPYING COPYING.?'
+
+# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
+# both are slightly edited, and unchanged rezrov. So we say you
+# copy-and-edit one, and rename-and-edit the other. We do not say
+# anything about rezrov.
+
+GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current
+cat >expected <<\EOF
+diff --git a/COPYING b/COPYING.1
+copy from COPYING
+copy to COPYING.1
+--- a/COPYING
++++ b/COPYING.1
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ However, in order to allow a migration to GPLv3 if that seems like
+diff --git a/COPYING b/COPYING.2
+rename from COPYING
+rename to COPYING.2
+--- a/COPYING
++++ b/COPYING.2
+@@ -2 +2 @@
+- Note that the only valid version of the GPL as far as this project
++ Note that the only valid version of the G.P.L as far as this project
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ HOWEVER, in order to allow a migration to G.P.Lv3 if that seems like
+@@ -12 +12 @@
+- This file is licensed under the GPL v2, or a later version
++ This file is licensed under the G.P.L v2, or a later version
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#1)' \
+ 'compare_diff_patch current expected'
+
+test_expect_success \
+ 'prepare work tree again' \
+ 'mv COPYING.2 COPYING &&
+ git update-index --add --remove COPYING COPYING.1 COPYING.2'
+
+# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
+# both are slightly edited, and unchanged rezrov. So we say you
+# edited one, and copy-and-edit the other. We do not say
+# anything about rezrov.
+
+GIT_DIFF_OPTS=--unified=0 git diff-index -C -p $tree >current
+cat >expected <<\EOF
+diff --git a/COPYING b/COPYING
+--- a/COPYING
++++ b/COPYING
+@@ -2 +2 @@
+- Note that the only valid version of the GPL as far as this project
++ Note that the only valid version of the G.P.L as far as this project
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ HOWEVER, in order to allow a migration to G.P.Lv3 if that seems like
+@@ -12 +12 @@
+- This file is licensed under the GPL v2, or a later version
++ This file is licensed under the G.P.L v2, or a later version
+diff --git a/COPYING b/COPYING.1
+copy from COPYING
+copy to COPYING.1
+--- a/COPYING
++++ b/COPYING.1
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ However, in order to allow a migration to GPLv3 if that seems like
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#2)' \
+ 'compare_diff_patch current expected'
+
+test_expect_success \
+ 'prepare work tree once again' \
+ 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ git update-index --add --remove COPYING COPYING.1'
+
+# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
+# but COPYING is not edited. We say you copy-and-edit COPYING.1; this
+# is only possible because -C mode now reports the unmodified file to
+# the diff-core. Unchanged rezrov, although being fed to
+# git diff-index as well, should not be mentioned.
+
+GIT_DIFF_OPTS=--unified=0 \
+ git diff-index -C --find-copies-harder -p $tree >current
+cat >expected <<\EOF
+diff --git a/COPYING b/COPYING.1
+copy from COPYING
+copy to COPYING.1
+--- a/COPYING
++++ b/COPYING.1
+@@ -6 +6 @@
+- HOWEVER, in order to allow a migration to GPLv3 if that seems like
++ However, in order to allow a migration to GPLv3 if that seems like
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#3)' \
+ 'compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
new file mode 100755
index 0000000..1a09e8d
--- /dev/null
+++ b/t/t4004-diff-rename-symlink.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='More rename detection tests.
+
+The rename detection logic should be able to detect pure rename or
+copy of symbolic links, but should not produce rename/copy followed
+by an edit for them.
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='Symbolic links not supported, skipping tests.'
+ test_done
+fi
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'echo xyzzy | tr -d '\\\\'012 >yomin &&
+ ln -s xyzzy frotz &&
+ git update-index --add frotz yomin &&
+ tree=$(git write-tree) &&
+ echo $tree'
+
+test_expect_success \
+ 'prepare work tree' \
+ 'mv frotz rezrov &&
+ rm -f yomin &&
+ ln -s xyzzy nitfol &&
+ ln -s xzzzy bozbar &&
+ git update-index --add --remove frotz rezrov nitfol bozbar yomin'
+
+# tree has frotz pointing at xyzzy, and yomin that contains xyzzy to
+# confuse things. work tree has rezrov (xyzzy) nitfol (xyzzy) and
+# bozbar (xzzzy).
+# rezrov and nitfol are rename/copy of frotz and bozbar should be
+# a new creation.
+
+GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree >current
+cat >expected <<\EOF
+diff --git a/bozbar b/bozbar
+new file mode 120000
+--- /dev/null
++++ b/bozbar
+@@ -0,0 +1 @@
++xzzzy
+\ No newline at end of file
+diff --git a/frotz b/nitfol
+similarity index 100%
+copy from frotz
+copy to nitfol
+diff --git a/frotz b/rezrov
+similarity index 100%
+rename from frotz
+rename to rezrov
+diff --git a/yomin b/yomin
+deleted file mode 100644
+--- a/yomin
++++ /dev/null
+@@ -1 +0,0 @@
+-xyzzy
+\ No newline at end of file
+EOF
+
+test_expect_success \
+ 'validate diff output' \
+ 'compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
new file mode 100755
index 0000000..1ba359d
--- /dev/null
+++ b/t/t4005-diff-rename-2.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Same rename detection as t4003 but testing diff-raw.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ echo frotz >rezrov &&
+ git update-index --add COPYING rezrov &&
+ tree=$(git write-tree) &&
+ echo $tree'
+
+test_expect_success \
+ 'prepare work tree' \
+ 'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+ sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+ rm -f COPYING &&
+ git update-index --add --remove COPYING COPYING.?'
+
+# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
+# both are slightly edited, and unchanged rezrov. We say COPYING.1
+# and COPYING.2 are based on COPYING, and do not say anything about
+# rezrov.
+
+git diff-index -M $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234 COPYING COPYING.2
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#1)' \
+ 'compare_diff_raw current expected'
+
+################################################################
+
+test_expect_success \
+ 'prepare work tree again' \
+ 'mv COPYING.2 COPYING &&
+ git update-index --add --remove COPYING COPYING.1 COPYING.2'
+
+# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
+# both are slightly edited, and unchanged rezrov. We say COPYING.1
+# is based on COPYING and COPYING is still there, and do not say anything
+# about rezrov.
+
+git diff-index -C $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M COPYING
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#2)' \
+ 'compare_diff_raw current expected'
+
+################################################################
+
+# tree has COPYING and rezrov. work tree has the same COPYING and
+# copy-edited COPYING.1, and unchanged rezrov. We should not say
+# anything about rezrov nor COPYING, since the revised again diff-raw
+# nows how to say Copy.
+
+test_expect_success \
+ 'prepare work tree once again' \
+ 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ git update-index --add --remove COPYING COPYING.1'
+
+git diff-index -C --find-copies-harder $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234 COPYING COPYING.1
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#3)' \
+ 'compare_diff_raw current expected'
+
+test_done
diff --git a/t/t4006-diff-mode.sh b/t/t4006-diff-mode.sh
new file mode 100755
index 0000000..ff8c2f7
--- /dev/null
+++ b/t/t4006-diff-mode.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test mode change diffs.
+
+'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'echo frotz >rezrov &&
+ git update-index --add rezrov &&
+ tree=`git write-tree` &&
+ echo $tree'
+
+test_expect_success \
+ 'chmod' \
+ 'test_chmod +x rezrov &&
+ git diff-index $tree >current'
+
+sed -e 's/\(:100644 100755\) \('"$_x40"'\) \2 /\1 X X /' <current >check
+echo ":100644 100755 X X M rezrov" >expected
+
+test_expect_success \
+ 'verify' \
+ 'test_cmp expected check'
+
+test_done
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
new file mode 100755
index 0000000..11502b7
--- /dev/null
+++ b/t/t4007-rename-3.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Rename interaction with pathspec.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success 'prepare reference tree' '
+ mkdir path0 path1 &&
+ cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+ git update-index --add path0/COPYING &&
+ tree=$(git write-tree) &&
+ echo $tree
+'
+
+test_expect_success 'prepare work tree' '
+ cp path0/COPYING path1/COPYING &&
+ git update-index --add --remove path0/COPYING path1/COPYING
+'
+
+# In the tree, there is only path0/COPYING. In the cache, path0 and
+# path1 both have COPYING and the latter is a copy of path0/COPYING.
+# Comparing the full tree with cache should tell us so.
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 C100 path0/COPYING path1/COPYING
+EOF
+
+test_expect_success 'copy detection' '
+ git diff-index -C --find-copies-harder $tree >current &&
+ compare_diff_raw current expected
+'
+
+test_expect_success 'copy detection, cached' '
+ git diff-index -C --find-copies-harder --cached $tree >current &&
+ compare_diff_raw current expected
+'
+
+# In the tree, there is only path0/COPYING. In the cache, path0 and
+# path1 both have COPYING and the latter is a copy of path0/COPYING.
+# However when we say we care only about path1, we should just see
+# path1/COPYING suddenly appearing from nowhere, not detected as
+# a copy from path0/COPYING.
+
+cat >expected <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
+EOF
+
+test_expect_success 'copy, limited to a subtree' '
+ git diff-index -C --find-copies-harder $tree path1 >current &&
+ compare_diff_raw current expected
+'
+
+test_expect_success 'tweak work tree' '
+ rm -f path0/COPYING &&
+ git update-index --remove path0/COPYING
+'
+# In the tree, there is only path0/COPYING. In the cache, path0 does
+# not have COPYING anymore and path1 has COPYING which is a copy of
+# path0/COPYING. Showing the full tree with cache should tell us about
+# the rename.
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 path0/COPYING path1/COPYING
+EOF
+
+test_expect_success 'rename detection' '
+ git diff-index -C --find-copies-harder $tree >current &&
+ compare_diff_raw current expected
+'
+
+# In the tree, there is only path0/COPYING. In the cache, path0 does
+# not have COPYING anymore and path1 has COPYING which is a copy of
+# path0/COPYING. When we say we care only about path1, we should just
+# see path1/COPYING appearing from nowhere.
+
+cat >expected <<\EOF
+:000000 100644 0000000000000000000000000000000000000000 6ff87c4664981e4397625791c8ea3bbb5f2279a3 A path1/COPYING
+EOF
+
+test_expect_success 'rename, limited to a subtree' '
+ git diff-index -C --find-copies-harder $tree path1 >current &&
+ compare_diff_raw current expected
+'
+
+test_done
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
new file mode 100755
index 0000000..e19ca65
--- /dev/null
+++ b/t/t4008-diff-break-rewrite.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Break and then rename
+
+We have two very different files, file0 and file1, registered in a tree.
+
+We update file1 so drastically that it is more similar to file0, and
+then remove file0. With -B, changes to file1 should be broken into
+separate delete and create, resulting in removal of file0, removal of
+original file1 and creation of completely rewritten file1.
+
+Further, with -B and -M together, these three modifications should
+turn into rename-edit of file0 into file1.
+
+Starting from the same two files in the tree, we swap file0 and file1.
+With -B, this should be detected as two complete rewrites, resulting in
+four changes in total.
+
+Further, with -B and -M together, these should turn into two renames.
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ setup \
+ 'cat "$TEST_DIRECTORY"/../README >file0 &&
+ cat "$TEST_DIRECTORY"/../COPYING >file1 &&
+ git update-index --add file0 file1 &&
+ tree=$(git write-tree) &&
+ echo "$tree"'
+
+test_expect_success \
+ 'change file1 with copy-edit of file0 and remove file0' \
+ 'sed -e "s/git/GIT/" file0 >file1 &&
+ rm -f file0 &&
+ git update-index --remove file0 file1'
+
+test_expect_success \
+ 'run diff with -B' \
+ 'git diff-index -B --cached "$tree" >current'
+
+cat >expected <<\EOF
+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 11e331465a89c394dc25c780de230043750c1ec8 M100 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B (#1)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -B and -M' \
+ 'git diff-index -B -M "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c R100 file0 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B -M (#2)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'swap file0 and file1' \
+ 'rm -f file0 file1 &&
+ git read-tree -m $tree &&
+ git checkout-index -f -u -a &&
+ mv file0 tmp &&
+ mv file1 file0 &&
+ mv tmp file1 &&
+ git update-index file0 file1'
+
+test_expect_success \
+ 'run diff with -B' \
+ 'git diff-index -B "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 6ff87c4664981e4397625791c8ea3bbb5f2279a3 M100 file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B (#3)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -B and -M' \
+ 'git diff-index -B -M "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 6ff87c4664981e4397625791c8ea3bbb5f2279a3 R100 file1 file0
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R100 file0 file1
+EOF
+
+test_expect_success \
+ 'validate result of -B -M (#4)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success SYMLINKS \
+ 'make file0 into something completely different' \
+ 'rm -f file0 &&
+ ln -s frotz file0 &&
+ git update-index file0 file1'
+
+test_expect_success \
+ 'run diff with -B' \
+ 'git diff-index -B "$tree" >current'
+
+cat >expected <<\EOF
+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M100 file1
+EOF
+
+test_expect_success SYMLINKS \
+ 'validate result of -B (#5)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -B -M' \
+ 'git diff-index -B -M "$tree" >current'
+
+# file0 changed from regular to symlink. file1 is very close to the preimage of file0.
+# because we break file0, file1 can become a rename of it.
+cat >expected <<\EOF
+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 R file0 file1
+EOF
+
+test_expect_success SYMLINKS \
+ 'validate result of -B -M (#6)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -M' \
+ 'git diff-index -M "$tree" >current'
+
+# This should not mistake file0 as the copy source of new file1
+# due to type differences.
+cat >expected <<\EOF
+:100644 120000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 67be421f88824578857624f7b3dc75e99a8a1481 T file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 M file1
+EOF
+
+test_expect_success SYMLINKS \
+ 'validate result of -M (#7)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'file1 edited to look like file0 and file0 rename-edited to file2' \
+ 'rm -f file0 file1 &&
+ git read-tree -m $tree &&
+ git checkout-index -f -u -a &&
+ sed -e "s/git/GIT/" file0 >file1 &&
+ sed -e "s/git/GET/" file0 >file2 &&
+ rm -f file0
+ git update-index --add --remove file0 file1 file2'
+
+test_expect_success \
+ 'run diff with -B' \
+ 'git diff-index -B "$tree" >current'
+
+cat >expected <<\EOF
+:100644 000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 0000000000000000000000000000000000000000 D file0
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 08bb2fb671deff4c03a4d4a0a1315dff98d5732c M100 file1
+:000000 100644 0000000000000000000000000000000000000000 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 A file2
+EOF
+
+test_expect_success \
+ 'validate result of -B (#8)' \
+ 'compare_diff_raw expected current'
+
+test_expect_success \
+ 'run diff with -B -M' \
+ 'git diff-index -B -M "$tree" >current'
+
+cat >expected <<\EOF
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 08bb2fb671deff4c03a4d4a0a1315dff98d5732c C095 file0 file1
+:100644 100644 f5deac7be59e7eeab8657fd9ae706fd6a57daed2 59f832e5c8b3f7e486be15ad0cd3e95ba9af8998 R095 file0 file2
+EOF
+
+test_expect_success \
+ 'validate result of -B -M (#9)' \
+ 'compare_diff_raw expected current'
+
+test_done
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
new file mode 100755
index 0000000..de3f174
--- /dev/null
+++ b/t/t4009-diff-rename-4.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Same rename detection as t4003 but testing diff-raw -z.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ echo frotz >rezrov &&
+ git update-index --add COPYING rezrov &&
+ tree=$(git write-tree) &&
+ echo $tree'
+
+test_expect_success \
+ 'prepare work tree' \
+ 'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
+ sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
+ rm -f COPYING &&
+ git update-index --add --remove COPYING COPYING.?'
+
+# tree has COPYING and rezrov. work tree has COPYING.1 and COPYING.2,
+# both are slightly edited, and unchanged rezrov. We say COPYING.1
+# and COPYING.2 are based on COPYING, and do not say anything about
+# rezrov.
+
+git diff-index -z -M $tree >current
+
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+COPYING
+COPYING.1
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234
+COPYING
+COPYING.2
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#1)' \
+ 'compare_diff_raw_z current expected'
+
+################################################################
+
+test_expect_success \
+ 'prepare work tree again' \
+ 'mv COPYING.2 COPYING &&
+ git update-index --add --remove COPYING COPYING.1 COPYING.2'
+
+# tree has COPYING and rezrov. work tree has COPYING and COPYING.1,
+# both are slightly edited, and unchanged rezrov. We say COPYING.1
+# is based on COPYING and COPYING is still there, and do not say anything
+# about rezrov.
+
+git diff-index -z -C $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M
+COPYING
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+COPYING
+COPYING.1
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#2)' \
+ 'compare_diff_raw_z current expected'
+
+################################################################
+
+# tree has COPYING and rezrov. work tree has the same COPYING and
+# copy-edited COPYING.1, and unchanged rezrov. We should not say
+# anything about rezrov nor COPYING, since the revised again diff-raw
+# nows how to say Copy.
+
+test_expect_success \
+ 'prepare work tree once again' \
+ 'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
+ git update-index --add --remove COPYING COPYING.1'
+
+git diff-index -z -C --find-copies-harder $tree >current
+cat >expected <<\EOF
+:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+COPYING
+COPYING.1
+EOF
+
+test_expect_success \
+ 'validate output from rename/copy detection (#3)' \
+ 'compare_diff_raw_z current expected'
+
+test_done
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
new file mode 100755
index 0000000..94df7ae
--- /dev/null
+++ b/t/t4010-diff-pathspec.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Pathspec restrictions
+
+Prepare:
+ file0
+ path1/file1
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+
+test_expect_success \
+ setup \
+ 'echo frotz >file0 &&
+ mkdir path1 &&
+ echo rezrov >path1/file1 &&
+ git update-index --add file0 path1/file1 &&
+ tree=`git write-tree` &&
+ echo "$tree" &&
+ echo nitfol >file0 &&
+ echo yomin >path1/file1 &&
+ git update-index file0 path1/file1'
+
+cat >expected <<\EOF
+EOF
+test_expect_success \
+ 'limit to path should show nothing' \
+ 'git diff-index --cached $tree -- path >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1
+EOF
+test_expect_success \
+ 'limit to path1 should show path1/file1' \
+ 'git diff-index --cached $tree -- path1 >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M path1/file1
+EOF
+test_expect_success \
+ 'limit to path1/ should show path1/file1' \
+ 'git diff-index --cached $tree -- path1/ >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+:100644 100644 766498d93a4b06057a8e49d23f4068f1170ff38f 0a41e115ab61be0328a19b29f18cdcb49338d516 M file0
+EOF
+test_expect_success \
+ 'limit to file0 should show file0' \
+ 'git diff-index --cached $tree -- file0 >current &&
+ compare_diff_raw current expected'
+
+cat >expected <<\EOF
+EOF
+test_expect_success \
+ 'limit to file0/ should emit nothing.' \
+ 'git diff-index --cached $tree -- file0/ >current &&
+ compare_diff_raw current expected'
+
+test_expect_success 'diff-tree pathspec' '
+ tree2=$(git write-tree) &&
+ echo "$tree2" &&
+ git diff-tree -r --name-only $tree $tree2 -- pa path1/a >current &&
+ >expected &&
+ test_cmp expected current
+'
+
+test_done
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
new file mode 100755
index 0000000..918a21a
--- /dev/null
+++ b/t/t4011-diff-symlink.sh
@@ -0,0 +1,97 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test diff of symlinks.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='Symbolic links not supported, skipping tests.'
+ test_done
+fi
+
+cat > expected << EOF
+diff --git a/frotz b/frotz
+new file mode 120000
+index 0000000..7c465af
+--- /dev/null
++++ b/frotz
+@@ -0,0 +1 @@
++xyzzy
+\ No newline at end of file
+EOF
+
+test_expect_success \
+ 'diff new symlink' \
+ 'ln -s xyzzy frotz &&
+ git update-index &&
+ tree=$(git write-tree) &&
+ git update-index --add frotz &&
+ GIT_DIFF_OPTS=--unified=0 git diff-index -M -p $tree > current &&
+ compare_diff_patch current expected'
+
+test_expect_success \
+ 'diff unchanged symlink' \
+ 'tree=$(git write-tree) &&
+ git update-index frotz &&
+ test -z "$(git diff-index --name-only $tree)"'
+
+cat > expected << EOF
+diff --git a/frotz b/frotz
+deleted file mode 120000
+index 7c465af..0000000
+--- a/frotz
++++ /dev/null
+@@ -1 +0,0 @@
+-xyzzy
+\ No newline at end of file
+EOF
+
+test_expect_success \
+ 'diff removed symlink' \
+ 'mv frotz frotz2 &&
+ git diff-index -M -p $tree > current &&
+ compare_diff_patch current expected'
+
+cat > expected << EOF
+diff --git a/frotz b/frotz
+EOF
+
+test_expect_success \
+ 'diff identical, but newly created symlink' \
+ 'ln -s xyzzy frotz &&
+ git diff-index -M -p $tree > current &&
+ compare_diff_patch current expected'
+
+cat > expected << EOF
+diff --git a/frotz b/frotz
+index 7c465af..df1db54 120000
+--- a/frotz
++++ b/frotz
+@@ -1 +1 @@
+-xyzzy
+\ No newline at end of file
++yxyyz
+\ No newline at end of file
+EOF
+
+test_expect_success \
+ 'diff different symlink' \
+ 'rm frotz &&
+ ln -s yxyyz frotz &&
+ git diff-index -M -p $tree > current &&
+ compare_diff_patch current expected'
+
+test_expect_success \
+ 'diff symlinks with non-existing targets' \
+ 'ln -s narf pinky &&
+ ln -s take\ over brain &&
+ test_must_fail git diff --no-index pinky brain > output 2> output.err &&
+ grep narf output &&
+ ! grep error output.err'
+test_done
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
new file mode 100755
index 0000000..bc46563
--- /dev/null
+++ b/t/t4012-diff-binary.sh
@@ -0,0 +1,97 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='Binary diff and apply
+'
+
+. ./test-lib.sh
+
+test_expect_success 'prepare repository' \
+ 'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
+ git update-index --add a b c d &&
+ echo git >a &&
+ cat "$TEST_DIRECTORY"/test4012.png >b &&
+ echo git >c &&
+ cat b b >d'
+
+cat > expected <<\EOF
+ a | 2 +-
+ b | Bin
+ c | 2 +-
+ d | Bin
+ 4 files changed, 2 insertions(+), 2 deletions(-)
+EOF
+test_expect_success 'diff without --binary' \
+ 'git diff | git apply --stat --summary >current &&
+ test_cmp expected current'
+
+test_expect_success 'diff with --binary' \
+ 'git diff --binary | git apply --stat --summary >current &&
+ test_cmp expected current'
+
+# apply needs to be able to skip the binary material correctly
+# in order to report the line number of a corrupt patch.
+test_expect_success 'apply detecting corrupt patch correctly' \
+ 'git diff | sed -e 's/-CIT/xCIT/' >broken &&
+ if git apply --stat --summary broken 2>detected
+ then
+ echo unhappy - should have detected an error
+ (exit 1)
+ else
+ echo happy
+ fi &&
+ detected=`cat detected` &&
+ detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
+ detected=`sed -ne "${detected}p" broken` &&
+ test "$detected" = xCIT'
+
+test_expect_success 'apply detecting corrupt patch correctly' \
+ 'git diff --binary | sed -e 's/-CIT/xCIT/' >broken &&
+ if git apply --stat --summary broken 2>detected
+ then
+ echo unhappy - should have detected an error
+ (exit 1)
+ else
+ echo happy
+ fi &&
+ detected=`cat detected` &&
+ detected=`expr "$detected" : "fatal.*at line \\([0-9]*\\)\$"` &&
+ detected=`sed -ne "${detected}p" broken` &&
+ test "$detected" = xCIT'
+
+test_expect_success 'initial commit' 'git commit -a -m initial'
+
+# Try removal (b), modification (d), and creation (e).
+test_expect_success 'diff-index with --binary' \
+ 'echo AIT >a && mv b e && echo CIT >c && cat e >d &&
+ git update-index --add --remove a b c d e &&
+ tree0=`git write-tree` &&
+ git diff --cached --binary >current &&
+ git apply --stat --summary current'
+
+test_expect_success 'apply binary patch' \
+ 'git reset --hard &&
+ git apply --binary --index <current &&
+ tree1=`git write-tree` &&
+ test "$tree1" = "$tree0"'
+
+nul_to_q() {
+ perl -pe 'y/\000/Q/'
+}
+
+test_expect_success 'diff --no-index with binary creation' '
+ echo Q | q_to_nul >binary &&
+ (: hide error code from diff, which just indicates differences
+ git diff --binary --no-index /dev/null binary >current ||
+ true
+ ) &&
+ rm binary &&
+ git apply --binary <current &&
+ echo Q >expected &&
+ nul_to_q <binary >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
new file mode 100755
index 0000000..dae6358
--- /dev/null
+++ b/t/t4013-diff-various.sh
@@ -0,0 +1,285 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='Various diff formatting options'
+
+. ./test-lib.sh
+
+LF='
+'
+
+test_expect_success setup '
+
+ GIT_AUTHOR_DATE="2006-06-26 00:00:00 +0000" &&
+ GIT_COMMITTER_DATE="2006-06-26 00:00:00 +0000" &&
+ export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+ mkdir dir &&
+ mkdir dir2 &&
+ for i in 1 2 3; do echo $i; done >file0 &&
+ for i in A B; do echo $i; done >dir/sub &&
+ cat file0 >file2 &&
+ git add file0 file2 dir/sub &&
+ git commit -m Initial &&
+
+ git branch initial &&
+ git branch side &&
+
+ GIT_AUTHOR_DATE="2006-06-26 00:01:00 +0000" &&
+ GIT_COMMITTER_DATE="2006-06-26 00:01:00 +0000" &&
+ export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+ for i in 4 5 6; do echo $i; done >>file0 &&
+ for i in C D; do echo $i; done >>dir/sub &&
+ rm -f file2 &&
+ git update-index --remove file0 file2 dir/sub &&
+ git commit -m "Second${LF}${LF}This is the second commit." &&
+
+ GIT_AUTHOR_DATE="2006-06-26 00:02:00 +0000" &&
+ GIT_COMMITTER_DATE="2006-06-26 00:02:00 +0000" &&
+ export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+ for i in A B C; do echo $i; done >file1 &&
+ git add file1 &&
+ for i in E F; do echo $i; done >>dir/sub &&
+ git update-index dir/sub &&
+ git commit -m Third &&
+
+ GIT_AUTHOR_DATE="2006-06-26 00:03:00 +0000" &&
+ GIT_COMMITTER_DATE="2006-06-26 00:03:00 +0000" &&
+ export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+ git checkout side &&
+ for i in A B C; do echo $i; done >>file0 &&
+ for i in 1 2; do echo $i; done >>dir/sub &&
+ cat dir/sub >file3 &&
+ git add file3 &&
+ git update-index file0 dir/sub &&
+ git commit -m Side &&
+
+ GIT_AUTHOR_DATE="2006-06-26 00:04:00 +0000" &&
+ GIT_COMMITTER_DATE="2006-06-26 00:04:00 +0000" &&
+ export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+ git checkout master &&
+ git pull -s ours . side &&
+
+ GIT_AUTHOR_DATE="2006-06-26 00:05:00 +0000" &&
+ GIT_COMMITTER_DATE="2006-06-26 00:05:00 +0000" &&
+ export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+ for i in A B C; do echo $i; done >>file0 &&
+ for i in 1 2; do echo $i; done >>dir/sub &&
+ git update-index file0 dir/sub &&
+
+ mkdir dir3 &&
+ cp dir/sub dir3/sub &&
+ test-chmtime +1 dir3/sub &&
+
+ git config log.showroot false &&
+ git commit --amend &&
+ git show-branch
+'
+
+: <<\EOF
+! [initial] Initial
+ * [master] Merge branch 'side'
+ ! [side] Side
+---
+ - [master] Merge branch 'side'
+ *+ [side] Side
+ * [master^] Second
++*+ [initial] Initial
+EOF
+
+V=`git version | sed -e 's/^git version //' -e 's/\./\\./g'`
+while read cmd
+do
+ case "$cmd" in
+ '' | '#'*) continue ;;
+ esac
+ test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
+ pfx=`printf "%04d" $test_count`
+ expect="$TEST_DIRECTORY/t4013/diff.$test"
+ actual="$pfx-diff.$test"
+
+ test_expect_success "git $cmd" '
+ {
+ echo "\$ git $cmd"
+ git $cmd |
+ sed -e "s/^\\(-*\\)$V\\(-*\\)\$/\\1g-i-t--v-e-r-s-i-o-n\2/" \
+ -e "s/^\\(.*mixed; boundary=\"-*\\)$V\\(-*\\)\"\$/\\1g-i-t--v-e-r-s-i-o-n\2\"/"
+ echo "\$"
+ } >"$actual" &&
+ if test -f "$expect"
+ then
+ test_cmp "$expect" "$actual" &&
+ rm -f "$actual"
+ else
+ # this is to help developing new tests.
+ cp "$actual" "$expect"
+ false
+ fi
+ '
+done <<\EOF
+diff-tree initial
+diff-tree -r initial
+diff-tree -r --abbrev initial
+diff-tree -r --abbrev=4 initial
+diff-tree --root initial
+diff-tree --root --abbrev initial
+diff-tree --root -r initial
+diff-tree --root -r --abbrev initial
+diff-tree --root -r --abbrev=4 initial
+diff-tree -p initial
+diff-tree --root -p initial
+diff-tree --patch-with-stat initial
+diff-tree --root --patch-with-stat initial
+diff-tree --patch-with-raw initial
+diff-tree --root --patch-with-raw initial
+
+diff-tree --pretty initial
+diff-tree --pretty --root initial
+diff-tree --pretty -p initial
+diff-tree --pretty --stat initial
+diff-tree --pretty --summary initial
+diff-tree --pretty --stat --summary initial
+diff-tree --pretty --root -p initial
+diff-tree --pretty --root --stat initial
+# improved by Timo's patch
+diff-tree --pretty --root --summary initial
+# improved by Timo's patch
+diff-tree --pretty --root --summary -r initial
+diff-tree --pretty --root --stat --summary initial
+diff-tree --pretty --patch-with-stat initial
+diff-tree --pretty --root --patch-with-stat initial
+diff-tree --pretty --patch-with-raw initial
+diff-tree --pretty --root --patch-with-raw initial
+
+diff-tree --pretty=oneline initial
+diff-tree --pretty=oneline --root initial
+diff-tree --pretty=oneline -p initial
+diff-tree --pretty=oneline --root -p initial
+diff-tree --pretty=oneline --patch-with-stat initial
+# improved by Timo's patch
+diff-tree --pretty=oneline --root --patch-with-stat initial
+diff-tree --pretty=oneline --patch-with-raw initial
+diff-tree --pretty=oneline --root --patch-with-raw initial
+
+diff-tree --pretty side
+diff-tree --pretty -p side
+diff-tree --pretty --patch-with-stat side
+
+diff-tree master
+diff-tree -p master
+diff-tree -p -m master
+diff-tree -c master
+diff-tree -c --abbrev master
+diff-tree --cc master
+# stat only should show the diffstat with the first parent
+diff-tree -c --stat master
+diff-tree --cc --stat master
+diff-tree -c --stat --summary master
+diff-tree --cc --stat --summary master
+# stat summary should show the diffstat and summary with the first parent
+diff-tree -c --stat --summary side
+diff-tree --cc --stat --summary side
+# improved by Timo's patch
+diff-tree --cc --patch-with-stat master
+# improved by Timo's patch
+diff-tree --cc --patch-with-stat --summary master
+# this is correct
+diff-tree --cc --patch-with-stat --summary side
+
+log master
+log -p master
+log --root master
+log --root -p master
+log --patch-with-stat master
+log --root --patch-with-stat master
+log --root --patch-with-stat --summary master
+# improved by Timo's patch
+log --root -c --patch-with-stat --summary master
+# improved by Timo's patch
+log --root --cc --patch-with-stat --summary master
+log -p --first-parent master
+log -m -p --first-parent master
+log -m -p master
+log -SF master
+log -SF -p master
+log --decorate --all
+log --decorate=full --all
+
+rev-list --parents HEAD
+rev-list --children HEAD
+
+whatchanged master
+whatchanged -p master
+whatchanged --root master
+whatchanged --root -p master
+whatchanged --patch-with-stat master
+whatchanged --root --patch-with-stat master
+whatchanged --root --patch-with-stat --summary master
+# improved by Timo's patch
+whatchanged --root -c --patch-with-stat --summary master
+# improved by Timo's patch
+whatchanged --root --cc --patch-with-stat --summary master
+whatchanged -SF master
+whatchanged -SF -p master
+
+log --patch-with-stat master -- dir/
+whatchanged --patch-with-stat master -- dir/
+log --patch-with-stat --summary master -- dir/
+whatchanged --patch-with-stat --summary master -- dir/
+
+show initial
+show --root initial
+show side
+show master
+show -c master
+show -m master
+show --first-parent master
+show --stat side
+show --stat --summary side
+show --patch-with-stat side
+show --patch-with-raw side
+show --patch-with-stat --summary side
+
+format-patch --stdout initial..side
+format-patch --stdout initial..master^
+format-patch --stdout initial..master
+format-patch --stdout --no-numbered initial..master
+format-patch --stdout --numbered initial..master
+format-patch --attach --stdout initial..side
+format-patch --attach --stdout --suffix=.diff initial..side
+format-patch --attach --stdout initial..master^
+format-patch --attach --stdout initial..master
+format-patch --inline --stdout initial..side
+format-patch --inline --stdout initial..master^
+format-patch --inline --stdout --numbered-files initial..master
+format-patch --inline --stdout initial..master
+format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
+config format.subjectprefix DIFFERENT_PREFIX
+format-patch --inline --stdout initial..master^^
+format-patch --stdout --cover-letter -n initial..master^
+
+diff --abbrev initial..side
+diff -r initial..side
+diff --stat initial..side
+diff -r --stat initial..side
+diff initial..side
+diff --patch-with-stat initial..side
+diff --patch-with-raw initial..side
+diff --patch-with-stat -r initial..side
+diff --patch-with-raw -r initial..side
+diff --name-status dir2 dir
+diff --no-index --name-status dir2 dir
+diff --no-index --name-status -- dir2 dir
+diff --no-index dir dir3
+diff master master^ side
+diff --dirstat master~1 master~2
+EOF
+
+test_done
diff --git a/t/t4013/diff.config_format.subjectprefix_DIFFERENT_PREFIX b/t/t4013/diff.config_format.subjectprefix_DIFFERENT_PREFIX
new file mode 100644
index 0000000..78f8970
--- /dev/null
+++ b/t/t4013/diff.config_format.subjectprefix_DIFFERENT_PREFIX
@@ -0,0 +1,2 @@
+$ git config format.subjectprefix DIFFERENT_PREFIX
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..3a9f78a
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
@@ -0,0 +1,34 @@
+$ git diff-tree --cc --patch-with-stat --summary master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
new file mode 100644
index 0000000..a61ad8c
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
@@ -0,0 +1,39 @@
+$ git diff-tree --cc --patch-with-stat --summary side
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
new file mode 100644
index 0000000..49f23b9
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
@@ -0,0 +1,34 @@
+$ git diff-tree --cc --patch-with-stat master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
new file mode 100644
index 0000000..cc6eb3b
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
@@ -0,0 +1,6 @@
+$ git diff-tree --cc --stat --summary master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_side b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
new file mode 100644
index 0000000..50362be
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
@@ -0,0 +1,8 @@
+$ git diff-tree --cc --stat --summary side
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_master b/t/t4013/diff.diff-tree_--cc_--stat_master
new file mode 100644
index 0000000..fae7f33
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--stat_master
@@ -0,0 +1,6 @@
+$ git diff-tree --cc --stat master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_--cc_master b/t/t4013/diff.diff-tree_--cc_master
new file mode 100644
index 0000000..5ecb4e1
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_master
@@ -0,0 +1,30 @@
+$ git diff-tree --cc master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.diff-tree_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--patch-with-raw_initial
new file mode 100644
index 0000000..fc177ab
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--patch-with-raw_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --patch-with-raw initial
+$
diff --git a/t/t4013/diff.diff-tree_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--patch-with-stat_initial
new file mode 100644
index 0000000..bd905b1
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--patch-with-stat_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --patch-with-stat initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-raw_initial
new file mode 100644
index 0000000..7bb8b45
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-raw_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty=oneline --patch-with-raw initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-stat_initial
new file mode 100644
index 0000000..cbdde4f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-stat_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty=oneline --patch-with-stat initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-raw_initial
new file mode 100644
index 0000000..cd79f1a
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-raw_initial
@@ -0,0 +1,33 @@
+$ git diff-tree --pretty=oneline --root --patch-with-raw initial
+444ac553ac7612cc88969031b02b3767fb8a353a Initial
+:000000 100644 0000000000000000000000000000000000000000 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e A dir/sub
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial
new file mode 100644
index 0000000..d5c333a
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial
@@ -0,0 +1,34 @@
+$ git diff-tree --pretty=oneline --root --patch-with-stat initial
+444ac553ac7612cc88969031b02b3767fb8a353a Initial
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_-p_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_-p_initial
new file mode 100644
index 0000000..3c5092c
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_-p_initial
@@ -0,0 +1,29 @@
+$ git diff-tree --pretty=oneline --root -p initial
+444ac553ac7612cc88969031b02b3767fb8a353a Initial
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_initial
new file mode 100644
index 0000000..08920ac
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --pretty=oneline --root initial
+444ac553ac7612cc88969031b02b3767fb8a353a Initial
+:000000 040000 0000000000000000000000000000000000000000 da7a33fa77d8066d6698643940ce5860fe2d7fb3 A dir
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_-p_initial b/t/t4013/diff.diff-tree_--pretty=oneline_-p_initial
new file mode 100644
index 0000000..94b76bf
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_-p_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty=oneline -p initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_initial b/t/t4013/diff.diff-tree_--pretty=oneline_initial
new file mode 100644
index 0000000..d50970d
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty=oneline initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--pretty_--patch-with-raw_initial
new file mode 100644
index 0000000..3a85316
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--patch-with-raw_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --patch-with-raw initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_initial
new file mode 100644
index 0000000..2e08239
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --patch-with-stat initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
new file mode 100644
index 0000000..4d30e7e
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
@@ -0,0 +1,43 @@
+$ git diff-tree --pretty --patch-with-stat side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-raw_initial
new file mode 100644
index 0000000..a3203bd
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-raw_initial
@@ -0,0 +1,38 @@
+$ git diff-tree --pretty --root --patch-with-raw initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+:000000 100644 0000000000000000000000000000000000000000 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e A dir/sub
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
new file mode 100644
index 0000000..7dfa6af
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
@@ -0,0 +1,39 @@
+$ git diff-tree --pretty --root --patch-with-stat initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
new file mode 100644
index 0000000..43bfce2
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
@@ -0,0 +1,15 @@
+$ git diff-tree --pretty --root --stat --summary initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial b/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial
new file mode 100644
index 0000000..9154aa4
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial
@@ -0,0 +1,12 @@
+$ git diff-tree --pretty --root --stat initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--summary_-r_initial b/t/t4013/diff.diff-tree_--pretty_--root_--summary_-r_initial
new file mode 100644
index 0000000..ccdaafb
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--summary_-r_initial
@@ -0,0 +1,11 @@
+$ git diff-tree --pretty --root --summary -r initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
new file mode 100644
index 0000000..58e5f74
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
@@ -0,0 +1,11 @@
+$ git diff-tree --pretty --root --summary initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_-p_initial b/t/t4013/diff.diff-tree_--pretty_--root_-p_initial
new file mode 100644
index 0000000..d0411f6
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_-p_initial
@@ -0,0 +1,34 @@
+$ git diff-tree --pretty --root -p initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_initial b/t/t4013/diff.diff-tree_--pretty_--root_initial
new file mode 100644
index 0000000..94e32ea
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_initial
@@ -0,0 +1,11 @@
+$ git diff-tree --pretty --root initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+:000000 040000 0000000000000000000000000000000000000000 da7a33fa77d8066d6698643940ce5860fe2d7fb3 A dir
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--stat_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--stat_--summary_initial
new file mode 100644
index 0000000..c22983a
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--stat_--summary_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --stat --summary initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--stat_initial b/t/t4013/diff.diff-tree_--pretty_--stat_initial
new file mode 100644
index 0000000..8fdcfb4
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--stat_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --stat initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--summary_initial
new file mode 100644
index 0000000..9bc2c4f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--summary_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --summary initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_-p_initial b/t/t4013/diff.diff-tree_--pretty_-p_initial
new file mode 100644
index 0000000..3c9942f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_-p_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty -p initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_-p_side b/t/t4013/diff.diff-tree_--pretty_-p_side
new file mode 100644
index 0000000..b993aa7
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_-p_side
@@ -0,0 +1,38 @@
+$ git diff-tree --pretty -p side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_initial b/t/t4013/diff.diff-tree_--pretty_initial
new file mode 100644
index 0000000..14715bf
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_side b/t/t4013/diff.diff-tree_--pretty_side
new file mode 100644
index 0000000..e9b6e1c
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_side
@@ -0,0 +1,11 @@
+$ git diff-tree --pretty side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+:040000 040000 da7a33fa77d8066d6698643940ce5860fe2d7fb3 f977ed46ae6873c1c30ab878e15a4accedc3618b M dir
+:100644 100644 01e79c32a8c99c557f0757da7cb6d65b3414466d f4615da674c09df322d6ba8d6b21ecfb1b1ba510 M file0
+:000000 100644 0000000000000000000000000000000000000000 7289e35bff32727c08dda207511bec138fdb9ea5 A file3
+$
diff --git a/t/t4013/diff.diff-tree_--root_--abbrev_initial b/t/t4013/diff.diff-tree_--root_--abbrev_initial
new file mode 100644
index 0000000..5aa84b2
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_--abbrev_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root --abbrev initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 040000 0000000... da7a33f... A dir
+:000000 100644 0000000... 01e79c3... A file0
+:000000 100644 0000000... 01e79c3... A file2
+$
diff --git a/t/t4013/diff.diff-tree_--root_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--root_--patch-with-raw_initial
new file mode 100644
index 0000000..d295e47
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_--patch-with-raw_initial
@@ -0,0 +1,33 @@
+$ git diff-tree --root --patch-with-raw initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000000000000000000000000000000000000000 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e A dir/sub
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
new file mode 100644
index 0000000..1562b62
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
@@ -0,0 +1,34 @@
+$ git diff-tree --root --patch-with-stat initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--root_-p_initial b/t/t4013/diff.diff-tree_--root_-p_initial
new file mode 100644
index 0000000..3219c72
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-p_initial
@@ -0,0 +1,29 @@
+$ git diff-tree --root -p initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--root_-r_--abbrev=4_initial b/t/t4013/diff.diff-tree_--root_-r_--abbrev=4_initial
new file mode 100644
index 0000000..0c53616
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-r_--abbrev=4_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root -r --abbrev=4 initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000... 35d2... A dir/sub
+:000000 100644 0000... 01e7... A file0
+:000000 100644 0000... 01e7... A file2
+$
diff --git a/t/t4013/diff.diff-tree_--root_-r_--abbrev_initial b/t/t4013/diff.diff-tree_--root_-r_--abbrev_initial
new file mode 100644
index 0000000..c7b460f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-r_--abbrev_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root -r --abbrev initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000000... 35d242b... A dir/sub
+:000000 100644 0000000... 01e79c3... A file0
+:000000 100644 0000000... 01e79c3... A file2
+$
diff --git a/t/t4013/diff.diff-tree_--root_-r_initial b/t/t4013/diff.diff-tree_--root_-r_initial
new file mode 100644
index 0000000..eed435e
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-r_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root -r initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000000000000000000000000000000000000000 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e A dir/sub
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file2
+$
diff --git a/t/t4013/diff.diff-tree_--root_initial b/t/t4013/diff.diff-tree_--root_initial
new file mode 100644
index 0000000..ddf6b06
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 040000 0000000000000000000000000000000000000000 da7a33fa77d8066d6698643940ce5860fe2d7fb3 A dir
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A file2
+$
diff --git a/t/t4013/diff.diff-tree_-c_--abbrev_master b/t/t4013/diff.diff-tree_-c_--abbrev_master
new file mode 100644
index 0000000..b8e4aa2
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_--abbrev_master
@@ -0,0 +1,5 @@
+$ git diff-tree -c --abbrev master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+::100644 100644 100644 cead32e... 7289e35... 992913c... MM dir/sub
+::100644 100644 100644 b414108... f4615da... 10a8a9f... MM file0
+$
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_master b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
new file mode 100644
index 0000000..ac9f641
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
@@ -0,0 +1,6 @@
+$ git diff-tree -c --stat --summary master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_side b/t/t4013/diff.diff-tree_-c_--stat_--summary_side
new file mode 100644
index 0000000..2afcca1
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_side
@@ -0,0 +1,8 @@
+$ git diff-tree -c --stat --summary side
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+$
diff --git a/t/t4013/diff.diff-tree_-c_--stat_master b/t/t4013/diff.diff-tree_-c_--stat_master
new file mode 100644
index 0000000..c2fe6a9
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_--stat_master
@@ -0,0 +1,6 @@
+$ git diff-tree -c --stat master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_-c_master b/t/t4013/diff.diff-tree_-c_master
new file mode 100644
index 0000000..e2d2bb2
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_master
@@ -0,0 +1,5 @@
+$ git diff-tree -c master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+::100644 100644 100644 cead32e925b1420c84c14cbf7cf755e7e45af8ad 7289e35bff32727c08dda207511bec138fdb9ea5 992913c5aa0a5476d10c49ed0f21fc0c6d1aedf3 MM dir/sub
+::100644 100644 100644 b414108e81e5091fe0974a1858b4d0d22b107f70 f4615da674c09df322d6ba8d6b21ecfb1b1ba510 10a8a9f3657f91a156b9f0184ed79a20adef9f7f MM file0
+$
diff --git a/t/t4013/diff.diff-tree_-p_-m_master b/t/t4013/diff.diff-tree_-p_-m_master
new file mode 100644
index 0000000..b60bea0
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-p_-m_master
@@ -0,0 +1,80 @@
+$ git diff-tree -p -m master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+$
diff --git a/t/t4013/diff.diff-tree_-p_initial b/t/t4013/diff.diff-tree_-p_initial
new file mode 100644
index 0000000..e20ce88
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-p_initial
@@ -0,0 +1,2 @@
+$ git diff-tree -p initial
+$
diff --git a/t/t4013/diff.diff-tree_-p_master b/t/t4013/diff.diff-tree_-p_master
new file mode 100644
index 0000000..b182875
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-p_master
@@ -0,0 +1,2 @@
+$ git diff-tree -p master
+$
diff --git a/t/t4013/diff.diff-tree_-r_--abbrev=4_initial b/t/t4013/diff.diff-tree_-r_--abbrev=4_initial
new file mode 100644
index 0000000..c5a3aa5
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-r_--abbrev=4_initial
@@ -0,0 +1,2 @@
+$ git diff-tree -r --abbrev=4 initial
+$
diff --git a/t/t4013/diff.diff-tree_-r_--abbrev_initial b/t/t4013/diff.diff-tree_-r_--abbrev_initial
new file mode 100644
index 0000000..0b689b7
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-r_--abbrev_initial
@@ -0,0 +1,2 @@
+$ git diff-tree -r --abbrev initial
+$
diff --git a/t/t4013/diff.diff-tree_-r_initial b/t/t4013/diff.diff-tree_-r_initial
new file mode 100644
index 0000000..1765d83
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-r_initial
@@ -0,0 +1,2 @@
+$ git diff-tree -r initial
+$
diff --git a/t/t4013/diff.diff-tree_initial b/t/t4013/diff.diff-tree_initial
new file mode 100644
index 0000000..b49fc53
--- /dev/null
+++ b/t/t4013/diff.diff-tree_initial
@@ -0,0 +1,2 @@
+$ git diff-tree initial
+$
diff --git a/t/t4013/diff.diff-tree_master b/t/t4013/diff.diff-tree_master
new file mode 100644
index 0000000..fe9226f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_master
@@ -0,0 +1,2 @@
+$ git diff-tree master
+$
diff --git a/t/t4013/diff.diff_--abbrev_initial..side b/t/t4013/diff.diff_--abbrev_initial..side
new file mode 100644
index 0000000..a88e66f
--- /dev/null
+++ b/t/t4013/diff.diff_--abbrev_initial..side
@@ -0,0 +1,32 @@
+$ git diff --abbrev initial..side
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--dirstat_master~1_master~2 b/t/t4013/diff.diff_--dirstat_master~1_master~2
new file mode 100644
index 0000000..b672e1c
--- /dev/null
+++ b/t/t4013/diff.diff_--dirstat_master~1_master~2
@@ -0,0 +1,3 @@
+$ git diff --dirstat master~1 master~2
+ 40.0% dir/
+$
diff --git a/t/t4013/diff.diff_--name-status_dir2_dir b/t/t4013/diff.diff_--name-status_dir2_dir
new file mode 100644
index 0000000..d0d96aa
--- /dev/null
+++ b/t/t4013/diff.diff_--name-status_dir2_dir
@@ -0,0 +1,2 @@
+$ git diff --name-status dir2 dir
+$
diff --git a/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir b/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir
new file mode 100644
index 0000000..6756f8d
--- /dev/null
+++ b/t/t4013/diff.diff_--no-index_--name-status_--_dir2_dir
@@ -0,0 +1,3 @@
+$ git diff --no-index --name-status -- dir2 dir
+A dir/sub
+$
diff --git a/t/t4013/diff.diff_--no-index_--name-status_dir2_dir b/t/t4013/diff.diff_--no-index_--name-status_dir2_dir
new file mode 100644
index 0000000..6a47584
--- /dev/null
+++ b/t/t4013/diff.diff_--no-index_--name-status_dir2_dir
@@ -0,0 +1,3 @@
+$ git diff --no-index --name-status dir2 dir
+A dir/sub
+$
diff --git a/t/t4013/diff.diff_--no-index_dir_dir3 b/t/t4013/diff.diff_--no-index_dir_dir3
new file mode 100644
index 0000000..2142c2b
--- /dev/null
+++ b/t/t4013/diff.diff_--no-index_dir_dir3
@@ -0,0 +1,2 @@
+$ git diff --no-index dir dir3
+$
diff --git a/t/t4013/diff.diff_--patch-with-raw_-r_initial..side b/t/t4013/diff.diff_--patch-with-raw_-r_initial..side
new file mode 100644
index 0000000..3590dc7
--- /dev/null
+++ b/t/t4013/diff.diff_--patch-with-raw_-r_initial..side
@@ -0,0 +1,36 @@
+$ git diff --patch-with-raw -r initial..side
+:100644 100644 35d242b... 7289e35... M dir/sub
+:100644 100644 01e79c3... f4615da... M file0
+:000000 100644 0000000... 7289e35... A file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--patch-with-raw_initial..side b/t/t4013/diff.diff_--patch-with-raw_initial..side
new file mode 100644
index 0000000..b21d5dc
--- /dev/null
+++ b/t/t4013/diff.diff_--patch-with-raw_initial..side
@@ -0,0 +1,36 @@
+$ git diff --patch-with-raw initial..side
+:100644 100644 35d242b... 7289e35... M dir/sub
+:100644 100644 01e79c3... f4615da... M file0
+:000000 100644 0000000... 7289e35... A file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--patch-with-stat_-r_initial..side b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
new file mode 100644
index 0000000..9ed317a
--- /dev/null
+++ b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
@@ -0,0 +1,37 @@
+$ git diff --patch-with-stat -r initial..side
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--patch-with-stat_initial..side b/t/t4013/diff.diff_--patch-with-stat_initial..side
new file mode 100644
index 0000000..8b50629
--- /dev/null
+++ b/t/t4013/diff.diff_--patch-with-stat_initial..side
@@ -0,0 +1,37 @@
+$ git diff --patch-with-stat initial..side
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--stat_initial..side b/t/t4013/diff.diff_--stat_initial..side
new file mode 100644
index 0000000..0517b5d
--- /dev/null
+++ b/t/t4013/diff.diff_--stat_initial..side
@@ -0,0 +1,6 @@
+$ git diff --stat initial..side
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff_-r_--stat_initial..side b/t/t4013/diff.diff_-r_--stat_initial..side
new file mode 100644
index 0000000..245220d
--- /dev/null
+++ b/t/t4013/diff.diff_-r_--stat_initial..side
@@ -0,0 +1,6 @@
+$ git diff -r --stat initial..side
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff_-r_initial..side b/t/t4013/diff.diff_-r_initial..side
new file mode 100644
index 0000000..5bb2fe2
--- /dev/null
+++ b/t/t4013/diff.diff_-r_initial..side
@@ -0,0 +1,32 @@
+$ git diff -r initial..side
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_initial..side b/t/t4013/diff.diff_initial..side
new file mode 100644
index 0000000..c8adaf5
--- /dev/null
+++ b/t/t4013/diff.diff_initial..side
@@ -0,0 +1,32 @@
+$ git diff initial..side
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_master_master^_side b/t/t4013/diff.diff_master_master^_side
new file mode 100644
index 0000000..50ec9ca
--- /dev/null
+++ b/t/t4013/diff.diff_master_master^_side
@@ -0,0 +1,29 @@
+$ git diff master master^ side
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
new file mode 100644
index 0000000..52116d3
--- /dev/null
+++ b/t/t4013/diff.format-patch_--attach_--stdout_--suffix=.diff_initial..side
@@ -0,0 +1,61 @@
+$ git format-patch --attach --stdout --suffix=.diff initial..side
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Side.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename="0001-Side.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
new file mode 100644
index 0000000..ce49bd6
--- /dev/null
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
@@ -0,0 +1,170 @@
+$ git format-patch --attach --stdout initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/3] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Second.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename="0001-Second.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/3] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0002-Third.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename="0002-Third.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH 3/3] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0003-Side.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename="0003-Side.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
new file mode 100644
index 0000000..5f1b238
--- /dev/null
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
@@ -0,0 +1,110 @@
+$ git format-patch --attach --stdout initial..master^
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/2] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Second.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename="0001-Second.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/2] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0002-Third.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename="0002-Third.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
new file mode 100644
index 0000000..4a2364a
--- /dev/null
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
@@ -0,0 +1,61 @@
+$ git format-patch --attach --stdout initial..side
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Side.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: attachment; filename="0001-Side.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
new file mode 100644
index 0000000..43b81eb
--- /dev/null
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--numbered-files_initial..master
@@ -0,0 +1,170 @@
+$ git format-patch --inline --stdout --numbered-files initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/3] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="1"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="1"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/3] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="2"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="2"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH 3/3] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="3"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="3"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
new file mode 100644
index 0000000..ca3f60b
--- /dev/null
+++ b/t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
@@ -0,0 +1,170 @@
+$ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [TESTCASE 1/3] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Second.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0001-Second.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [TESTCASE 2/3] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0002-Third.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0002-Third.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [TESTCASE 3/3] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0003-Side.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0003-Side.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
new file mode 100644
index 0000000..08f2301
--- /dev/null
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master
@@ -0,0 +1,170 @@
+$ git format-patch --inline --stdout initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/3] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Second.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0001-Second.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/3] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0002-Third.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0002-Third.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH 3/3] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0003-Side.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0003-Side.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
new file mode 100644
index 0000000..07f1230
--- /dev/null
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^
@@ -0,0 +1,110 @@
+$ git format-patch --inline --stdout initial..master^
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/2] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Second.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0001-Second.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/2] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0002-Third.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0002-Third.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
new file mode 100644
index 0000000..29e00ab
--- /dev/null
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..master^^
@@ -0,0 +1,62 @@
+$ git format-patch --inline --stdout initial..master^^
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [DIFFERENT_PREFIX] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Second.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0001-Second.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--inline_--stdout_initial..side b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
new file mode 100644
index 0000000..67633d4
--- /dev/null
+++ b/t/t4013/diff.format-patch_--inline_--stdout_initial..side
@@ -0,0 +1,61 @@
+$ git format-patch --inline --stdout initial..side
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="0001-Side.patch"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="0001-Side.patch"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
new file mode 100644
index 0000000..1f0f9ad
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^
@@ -0,0 +1,103 @@
+$ git format-patch --stdout --cover-letter -n initial..master^
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: C O Mitter <committer@example.com>
+Date: Mon, 26 Jun 2006 00:05:00 +0000
+Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***
+
+*** BLURB HERE ***
+
+A U Thor (2):
+ Second
+ Third
+
+ dir/sub | 4 ++++
+ file0 | 3 +++
+ file1 | 3 +++
+ file2 | 3 ---
+ 4 files changed, 10 insertions(+), 3 deletions(-)
+ create mode 100644 file1
+ delete mode 100644 file2
+
+--
+g-i-t--v-e-r-s-i-o-n
+
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [DIFFERENT_PREFIX 1/2] Second
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+--
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [DIFFERENT_PREFIX 2/2] Third
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+--
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
new file mode 100644
index 0000000..f7752eb
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
@@ -0,0 +1,127 @@
+$ git format-patch --stdout --no-numbered initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH] Second
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+--
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH] Third
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+--
+g-i-t--v-e-r-s-i-o-n
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+--
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
new file mode 100644
index 0000000..8e67dbf
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
@@ -0,0 +1,127 @@
+$ git format-patch --stdout --numbered initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/3] Second
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+--
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/3] Third
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+--
+g-i-t--v-e-r-s-i-o-n
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH 3/3] Side
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+--
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master b/t/t4013/diff.format-patch_--stdout_initial..master
new file mode 100644
index 0000000..7b89978
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_initial..master
@@ -0,0 +1,127 @@
+$ git format-patch --stdout initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/3] Second
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+--
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/3] Third
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+--
+g-i-t--v-e-r-s-i-o-n
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH 3/3] Side
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+--
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master^ b/t/t4013/diff.format-patch_--stdout_initial..master^
new file mode 100644
index 0000000..b7f9725
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_initial..master^
@@ -0,0 +1,81 @@
+$ git format-patch --stdout initial..master^
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/2] Second
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+--
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/2] Third
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+--
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_initial..side b/t/t4013/diff.format-patch_--stdout_initial..side
new file mode 100644
index 0000000..e765088
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_initial..side
@@ -0,0 +1,47 @@
+$ git format-patch --stdout initial..side
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+--
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
new file mode 100644
index 0000000..d155e0b
--- /dev/null
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -0,0 +1,34 @@
+$ git log --decorate=full --all
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
new file mode 100644
index 0000000..fd7c3e6
--- /dev/null
+++ b/t/t4013/diff.log_--decorate_--all
@@ -0,0 +1,34 @@
+$ git log --decorate --all
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
new file mode 100644
index 0000000..bd7f5c0
--- /dev/null
+++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
@@ -0,0 +1,74 @@
+$ git log --patch-with-stat --summary master -- dir/
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master
new file mode 100644
index 0000000..14595a6
--- /dev/null
+++ b/t/t4013/diff.log_--patch-with-stat_master
@@ -0,0 +1,129 @@
+$ git log --patch-with-stat master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
new file mode 100644
index 0000000..5a4e727
--- /dev/null
+++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
@@ -0,0 +1,74 @@
+$ git log --patch-with-stat master -- dir/
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..df0aaa9
--- /dev/null
+++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
@@ -0,0 +1,199 @@
+$ git log --root --cc --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..c11b5f2c
--- /dev/null
+++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
@@ -0,0 +1,167 @@
+$ git log --root --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master
new file mode 100644
index 0000000..5f0c98f
--- /dev/null
+++ b/t/t4013/diff.log_--root_--patch-with-stat_master
@@ -0,0 +1,161 @@
+$ git log --root --patch-with-stat master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..e62c368
--- /dev/null
+++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
@@ -0,0 +1,199 @@
+$ git log --root -c --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --combined dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --combined file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_-p_master b/t/t4013/diff.log_--root_-p_master
new file mode 100644
index 0000000..b42c334
--- /dev/null
+++ b/t/t4013/diff.log_--root_-p_master
@@ -0,0 +1,142 @@
+$ git log --root -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_master b/t/t4013/diff.log_--root_master
new file mode 100644
index 0000000..e8f4615
--- /dev/null
+++ b/t/t4013/diff.log_--root_master
@@ -0,0 +1,34 @@
+$ git log --root master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_-SF_-p_master b/t/t4013/diff.log_-SF_-p_master
new file mode 100644
index 0000000..5e32438
--- /dev/null
+++ b/t/t4013/diff.log_-SF_-p_master
@@ -0,0 +1,18 @@
+$ git log -SF -p master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+$
diff --git a/t/t4013/diff.log_-SF_master b/t/t4013/diff.log_-SF_master
new file mode 100644
index 0000000..c1599f2
--- /dev/null
+++ b/t/t4013/diff.log_-SF_master
@@ -0,0 +1,7 @@
+$ git log -SF master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+$
diff --git a/t/t4013/diff.log_-m_-p_--first-parent_master b/t/t4013/diff.log_-m_-p_--first-parent_master
new file mode 100644
index 0000000..7a0073f
--- /dev/null
+++ b/t/t4013/diff.log_-m_-p_--first-parent_master
@@ -0,0 +1,100 @@
+$ git log -m -p --first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_-m_-p_master b/t/t4013/diff.log_-m_-p_master
new file mode 100644
index 0000000..9ca62a0
--- /dev/null
+++ b/t/t4013/diff.log_-m_-p_master
@@ -0,0 +1,200 @@
+$ git log -m -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_-p_--first-parent_master b/t/t4013/diff.log_-p_--first-parent_master
new file mode 100644
index 0000000..3fc896d
--- /dev/null
+++ b/t/t4013/diff.log_-p_--first-parent_master
@@ -0,0 +1,78 @@
+$ git log -p --first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_-p_master b/t/t4013/diff.log_-p_master
new file mode 100644
index 0000000..bf1326d
--- /dev/null
+++ b/t/t4013/diff.log_-p_master
@@ -0,0 +1,115 @@
+$ git log -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.log_master b/t/t4013/diff.log_master
new file mode 100644
index 0000000..a8f6ce5
--- /dev/null
+++ b/t/t4013/diff.log_master
@@ -0,0 +1,34 @@
+$ git log master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.rev-list_--children_HEAD b/t/t4013/diff.rev-list_--children_HEAD
new file mode 100644
index 0000000..e7f17d5
--- /dev/null
+++ b/t/t4013/diff.rev-list_--children_HEAD
@@ -0,0 +1,7 @@
+$ git rev-list --children HEAD
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 59d314ad6f356dd08601a4cd5e530381da3e3c64
+9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 59d314ad6f356dd08601a4cd5e530381da3e3c64
+1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+444ac553ac7612cc88969031b02b3767fb8a353a 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+$
diff --git a/t/t4013/diff.rev-list_--parents_HEAD b/t/t4013/diff.rev-list_--parents_HEAD
new file mode 100644
index 0000000..65d2a80
--- /dev/null
+++ b/t/t4013/diff.rev-list_--parents_HEAD
@@ -0,0 +1,7 @@
+$ git rev-list --parents HEAD
+59d314ad6f356dd08601a4cd5e530381da3e3c64 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a 444ac553ac7612cc88969031b02b3767fb8a353a
+9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 444ac553ac7612cc88969031b02b3767fb8a353a
+444ac553ac7612cc88969031b02b3767fb8a353a
+$
diff --git a/t/t4013/diff.show_--first-parent_master b/t/t4013/diff.show_--first-parent_master
new file mode 100644
index 0000000..3dcbe47
--- /dev/null
+++ b/t/t4013/diff.show_--first-parent_master
@@ -0,0 +1,30 @@
+$ git show --first-parent master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+$
diff --git a/t/t4013/diff.show_--patch-with-raw_side b/t/t4013/diff.show_--patch-with-raw_side
new file mode 100644
index 0000000..221b46a
--- /dev/null
+++ b/t/t4013/diff.show_--patch-with-raw_side
@@ -0,0 +1,42 @@
+$ git show --patch-with-raw side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+:100644 100644 35d242b... 7289e35... M dir/sub
+:100644 100644 01e79c3... f4615da... M file0
+:000000 100644 0000000... 7289e35... A file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.show_--patch-with-stat_--summary_side b/t/t4013/diff.show_--patch-with-stat_--summary_side
new file mode 100644
index 0000000..377f2b7
--- /dev/null
+++ b/t/t4013/diff.show_--patch-with-stat_--summary_side
@@ -0,0 +1,44 @@
+$ git show --patch-with-stat --summary side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.show_--patch-with-stat_side b/t/t4013/diff.show_--patch-with-stat_side
new file mode 100644
index 0000000..fb14c53
--- /dev/null
+++ b/t/t4013/diff.show_--patch-with-stat_side
@@ -0,0 +1,43 @@
+$ git show --patch-with-stat side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.show_--root_initial b/t/t4013/diff.show_--root_initial
new file mode 100644
index 0000000..8c89136
--- /dev/null
+++ b/t/t4013/diff.show_--root_initial
@@ -0,0 +1,34 @@
+$ git show --root initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.show_--stat_--summary_side b/t/t4013/diff.show_--stat_--summary_side
new file mode 100644
index 0000000..5bd5977
--- /dev/null
+++ b/t/t4013/diff.show_--stat_--summary_side
@@ -0,0 +1,13 @@
+$ git show --stat --summary side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+$
diff --git a/t/t4013/diff.show_--stat_side b/t/t4013/diff.show_--stat_side
new file mode 100644
index 0000000..3b22327
--- /dev/null
+++ b/t/t4013/diff.show_--stat_side
@@ -0,0 +1,12 @@
+$ git show --stat side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.show_-c_master b/t/t4013/diff.show_-c_master
new file mode 100644
index 0000000..81aba8d
--- /dev/null
+++ b/t/t4013/diff.show_-c_master
@@ -0,0 +1,36 @@
+$ git show -c master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+diff --combined dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --combined file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.show_-m_master b/t/t4013/diff.show_-m_master
new file mode 100644
index 0000000..4ea2ee4
--- /dev/null
+++ b/t/t4013/diff.show_-m_master
@@ -0,0 +1,93 @@
+$ git show -m master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (from c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+$
diff --git a/t/t4013/diff.show_initial b/t/t4013/diff.show_initial
new file mode 100644
index 0000000..4c4066a
--- /dev/null
+++ b/t/t4013/diff.show_initial
@@ -0,0 +1,7 @@
+$ git show initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4013/diff.show_master b/t/t4013/diff.show_master
new file mode 100644
index 0000000..fb08ce0
--- /dev/null
+++ b/t/t4013/diff.show_master
@@ -0,0 +1,36 @@
+$ git show master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.show_side b/t/t4013/diff.show_side
new file mode 100644
index 0000000..530a073
--- /dev/null
+++ b/t/t4013/diff.show_side
@@ -0,0 +1,38 @@
+$ git show side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
new file mode 100644
index 0000000..6a467cc
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
@@ -0,0 +1,61 @@
+$ git whatchanged --patch-with-stat --summary master -- dir/
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+$
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master b/t/t4013/diff.whatchanged_--patch-with-stat_master
new file mode 100644
index 0000000..1e1bbe1
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master
@@ -0,0 +1,116 @@
+$ git whatchanged --patch-with-stat master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+$
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
new file mode 100644
index 0000000..13789f1
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
@@ -0,0 +1,61 @@
+$ git whatchanged --patch-with-stat master -- dir/
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+$
diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..e96ff1f
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
@@ -0,0 +1,199 @@
+$ git whatchanged --root --cc --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..0291153
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
@@ -0,0 +1,160 @@
+$ git whatchanged --root --patch-with-stat --summary master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
new file mode 100644
index 0000000..9b0349c
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
@@ -0,0 +1,154 @@
+$ git whatchanged --root --patch-with-stat master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..c0aff68
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
@@ -0,0 +1,199 @@
+$ git whatchanged --root -c --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+ dir/sub | 2 ++
+ file0 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --combined dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+ A
+ B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --combined file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+ 1
+ 2
+ 3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_-p_master b/t/t4013/diff.whatchanged_--root_-p_master
new file mode 100644
index 0000000..ebf1f06
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_-p_master
@@ -0,0 +1,135 @@
+$ git whatchanged --root -p master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_master b/t/t4013/diff.whatchanged_--root_master
new file mode 100644
index 0000000..a405cb6
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_master
@@ -0,0 +1,42 @@
+$ git whatchanged --root master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+:100644 100644 35d242b... 7289e35... M dir/sub
+:100644 100644 01e79c3... f4615da... M file0
+:000000 100644 0000000... 7289e35... A file3
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+:100644 100644 8422d40... cead32e... M dir/sub
+:000000 100644 0000000... b1e6722... A file1
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+:100644 100644 35d242b... 8422d40... M dir/sub
+:100644 100644 01e79c3... b414108... M file0
+:100644 000000 01e79c3... 0000000... D file2
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+
+:000000 100644 0000000... 35d242b... A dir/sub
+:000000 100644 0000000... 01e79c3... A file0
+:000000 100644 0000000... 01e79c3... A file2
+$
diff --git a/t/t4013/diff.whatchanged_-SF_-p_master b/t/t4013/diff.whatchanged_-SF_-p_master
new file mode 100644
index 0000000..f39da84
--- /dev/null
+++ b/t/t4013/diff.whatchanged_-SF_-p_master
@@ -0,0 +1,18 @@
+$ git whatchanged -SF -p master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+$
diff --git a/t/t4013/diff.whatchanged_-SF_master b/t/t4013/diff.whatchanged_-SF_master
new file mode 100644
index 0000000..0499321
--- /dev/null
+++ b/t/t4013/diff.whatchanged_-SF_master
@@ -0,0 +1,9 @@
+$ git whatchanged -SF master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+:100644 100644 8422d40... cead32e... M dir/sub
+$
diff --git a/t/t4013/diff.whatchanged_-p_master b/t/t4013/diff.whatchanged_-p_master
new file mode 100644
index 0000000..f18d432
--- /dev/null
+++ b/t/t4013/diff.whatchanged_-p_master
@@ -0,0 +1,102 @@
+$ git whatchanged -p master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+$
diff --git a/t/t4013/diff.whatchanged_master b/t/t4013/diff.whatchanged_master
new file mode 100644
index 0000000..cd3bcc2
--- /dev/null
+++ b/t/t4013/diff.whatchanged_master
@@ -0,0 +1,32 @@
+$ git whatchanged master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+:100644 100644 35d242b... 7289e35... M dir/sub
+:100644 100644 01e79c3... f4615da... M file0
+:000000 100644 0000000... 7289e35... A file3
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+:100644 100644 8422d40... cead32e... M dir/sub
+:000000 100644 0000000... b1e6722... A file1
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+:100644 100644 35d242b... 8422d40... M dir/sub
+:100644 100644 01e79c3... b414108... M file0
+:100644 000000 01e79c3... 0000000... D file2
+$
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
new file mode 100755
index 0000000..f87434b
--- /dev/null
+++ b/t/t4014-format-patch.sh
@@ -0,0 +1,668 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='various format-patch tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
+ cat file >elif &&
+ git add file elif &&
+ git commit -m Initial &&
+ git checkout -b side &&
+
+ for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
+ test_chmod +x elif &&
+ git commit -m "Side changes #1" &&
+
+ for i in D E F; do echo "$i"; done >>file &&
+ git update-index file &&
+ git commit -m "Side changes #2" &&
+ git tag C2 &&
+
+ for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file &&
+ git update-index file &&
+ git commit -m "Side changes #3 with \\n backslash-n in it." &&
+
+ git checkout master &&
+ git diff-tree -p C2 | git apply --index &&
+ git commit -m "Master accepts moral equivalent of #2"
+
+'
+
+test_expect_success "format-patch --ignore-if-in-upstream" '
+
+ git format-patch --stdout master..side >patch0 &&
+ cnt=`grep "^From " patch0 | wc -l` &&
+ test $cnt = 3
+
+'
+
+test_expect_success "format-patch --ignore-if-in-upstream" '
+
+ git format-patch --stdout \
+ --ignore-if-in-upstream master..side >patch1 &&
+ cnt=`grep "^From " patch1 | wc -l` &&
+ test $cnt = 2
+
+'
+
+test_expect_success "format-patch result applies" '
+
+ git checkout -b rebuild-0 master &&
+ git am -3 patch0 &&
+ cnt=`git rev-list master.. | wc -l` &&
+ test $cnt = 2
+'
+
+test_expect_success "format-patch --ignore-if-in-upstream result applies" '
+
+ git checkout -b rebuild-1 master &&
+ git am -3 patch1 &&
+ cnt=`git rev-list master.. | wc -l` &&
+ test $cnt = 2
+'
+
+test_expect_success 'commit did not screw up the log message' '
+
+ git cat-file commit side | grep "^Side .* with .* backslash-n"
+
+'
+
+test_expect_success 'format-patch did not screw up the log message' '
+
+ grep "^Subject: .*Side changes #3 with .* backslash-n" patch0 &&
+ grep "^Subject: .*Side changes #3 with .* backslash-n" patch1
+
+'
+
+test_expect_success 'replay did not screw up the log message' '
+
+ git cat-file commit rebuild-1 | grep "^Side .* with .* backslash-n"
+
+'
+
+test_expect_success 'extra headers' '
+
+ git config format.headers "To: R. E. Cipient <rcipient@example.com>
+" &&
+ git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
+" &&
+ git format-patch --stdout master..side > patch2 &&
+ sed -e "/^\$/q" patch2 > hdrs2 &&
+ grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs2 &&
+ grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs2
+
+'
+
+test_expect_success 'extra headers without newlines' '
+
+ git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
+ git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
+ git format-patch --stdout master..side >patch3 &&
+ sed -e "/^\$/q" patch3 > hdrs3 &&
+ grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs3 &&
+ grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs3
+
+'
+
+test_expect_success 'extra headers with multiple To:s' '
+
+ git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
+ git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
+ git format-patch --stdout master..side > patch4 &&
+ sed -e "/^\$/q" patch4 > hdrs4 &&
+ grep "^To: R. E. Cipient <rcipient@example.com>,\$" hdrs4 &&
+ grep "^ *S. E. Cipient <scipient@example.com>\$" hdrs4
+'
+
+test_expect_success 'additional command line cc' '
+
+ git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
+ git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
+ grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch5 &&
+ grep "^ *S. E. Cipient <scipient@example.com>\$" patch5
+'
+
+test_expect_success 'command line headers' '
+
+ git config --unset-all format.headers &&
+ git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
+ grep "^Cc: R. E. Cipient <rcipient@example.com>\$" patch6
+'
+
+test_expect_success 'configuration headers and command line headers' '
+
+ git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
+ git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
+ grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch7 &&
+ grep "^ *S. E. Cipient <scipient@example.com>\$" patch7
+'
+
+test_expect_success 'command line To: header' '
+
+ git config --unset-all format.headers &&
+ git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
+ grep "^To: R. E. Cipient <rcipient@example.com>\$" patch8
+'
+
+test_expect_success 'configuration To: header' '
+
+ git config format.to "R. E. Cipient <rcipient@example.com>" &&
+ git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
+ grep "^To: R. E. Cipient <rcipient@example.com>\$" patch9
+'
+
+test_expect_success '--no-to overrides config.to' '
+
+ git config --replace-all format.to \
+ "R. E. Cipient <rcipient@example.com>" &&
+ git format-patch --no-to --stdout master..side |
+ sed -e "/^\$/q" >patch10 &&
+ ! grep "^To: R. E. Cipient <rcipient@example.com>\$" patch10
+'
+
+test_expect_success '--no-to and --to replaces config.to' '
+
+ git config --replace-all format.to \
+ "Someone <someone@out.there>" &&
+ git format-patch --no-to --to="Someone Else <else@out.there>" \
+ --stdout master..side |
+ sed -e "/^\$/q" >patch11 &&
+ ! grep "^To: Someone <someone@out.there>\$" patch11 &&
+ grep "^To: Someone Else <else@out.there>\$" patch11
+'
+
+test_expect_success '--no-cc overrides config.cc' '
+
+ git config --replace-all format.cc \
+ "C. E. Cipient <rcipient@example.com>" &&
+ git format-patch --no-cc --stdout master..side |
+ sed -e "/^\$/q" >patch12 &&
+ ! grep "^Cc: C. E. Cipient <rcipient@example.com>\$" patch12
+'
+
+test_expect_success '--no-add-headers overrides config.headers' '
+
+ git config --replace-all format.headers \
+ "Header1: B. E. Cipient <rcipient@example.com>" &&
+ git format-patch --no-add-headers --stdout master..side |
+ sed -e "/^\$/q" >patch13 &&
+ ! grep "^Header1: B. E. Cipient <rcipient@example.com>\$" patch13
+'
+
+test_expect_success 'multiple files' '
+
+ rm -rf patches/ &&
+ git checkout side &&
+ git format-patch -o patches/ master &&
+ ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
+'
+
+check_threading () {
+ expect="$1" &&
+ shift &&
+ (git format-patch --stdout "$@"; echo $? > status.out) |
+ # Prints everything between the Message-ID and In-Reply-To,
+ # and replaces all Message-ID-lookalikes by a sequence number
+ perl -ne '
+ if (/^(message-id|references|in-reply-to)/i) {
+ $printing = 1;
+ } elsif (/^\S/) {
+ $printing = 0;
+ }
+ if ($printing) {
+ $h{$1}=$i++ if (/<([^>]+)>/ and !exists $h{$1});
+ for $k (keys %h) {s/$k/$h{$k}/};
+ print;
+ }
+ print "---\n" if /^From /i;
+ ' > actual &&
+ test 0 = "$(cat status.out)" &&
+ test_cmp "$expect" actual
+}
+
+cat >> expect.no-threading <<EOF
+---
+---
+---
+EOF
+
+test_expect_success 'no threading' '
+ git checkout side &&
+ check_threading expect.no-threading master
+'
+
+cat > expect.thread <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <0>
+EOF
+
+test_expect_success 'thread' '
+ check_threading expect.thread --thread master
+'
+
+cat > expect.in-reply-to <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <3>
+In-Reply-To: <1>
+References: <1>
+EOF
+
+test_expect_success 'thread in-reply-to' '
+ check_threading expect.in-reply-to --in-reply-to="<test.message>" \
+ --thread master
+'
+
+cat > expect.cover-letter <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <3>
+In-Reply-To: <0>
+References: <0>
+EOF
+
+test_expect_success 'thread cover-letter' '
+ check_threading expect.cover-letter --cover-letter --thread master
+'
+
+cat > expect.cl-irt <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <1>
+ <0>
+---
+Message-Id: <3>
+In-Reply-To: <0>
+References: <1>
+ <0>
+---
+Message-Id: <4>
+In-Reply-To: <0>
+References: <1>
+ <0>
+EOF
+
+test_expect_success 'thread cover-letter in-reply-to' '
+ check_threading expect.cl-irt --cover-letter \
+ --in-reply-to="<test.message>" --thread master
+'
+
+test_expect_success 'thread explicit shallow' '
+ check_threading expect.cl-irt --cover-letter \
+ --in-reply-to="<test.message>" --thread=shallow master
+'
+
+cat > expect.deep <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <1>
+References: <0>
+ <1>
+EOF
+
+test_expect_success 'thread deep' '
+ check_threading expect.deep --thread=deep master
+'
+
+cat > expect.deep-irt <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <1>
+ <0>
+---
+Message-Id: <3>
+In-Reply-To: <2>
+References: <1>
+ <0>
+ <2>
+EOF
+
+test_expect_success 'thread deep in-reply-to' '
+ check_threading expect.deep-irt --thread=deep \
+ --in-reply-to="<test.message>" master
+'
+
+cat > expect.deep-cl <<EOF
+---
+Message-Id: <0>
+---
+Message-Id: <1>
+In-Reply-To: <0>
+References: <0>
+---
+Message-Id: <2>
+In-Reply-To: <1>
+References: <0>
+ <1>
+---
+Message-Id: <3>
+In-Reply-To: <2>
+References: <0>
+ <1>
+ <2>
+EOF
+
+test_expect_success 'thread deep cover-letter' '
+ check_threading expect.deep-cl --cover-letter --thread=deep master
+'
+
+cat > expect.deep-cl-irt <<EOF
+---
+Message-Id: <0>
+In-Reply-To: <1>
+References: <1>
+---
+Message-Id: <2>
+In-Reply-To: <0>
+References: <1>
+ <0>
+---
+Message-Id: <3>
+In-Reply-To: <2>
+References: <1>
+ <0>
+ <2>
+---
+Message-Id: <4>
+In-Reply-To: <3>
+References: <1>
+ <0>
+ <2>
+ <3>
+EOF
+
+test_expect_success 'thread deep cover-letter in-reply-to' '
+ check_threading expect.deep-cl-irt --cover-letter \
+ --in-reply-to="<test.message>" --thread=deep master
+'
+
+test_expect_success 'thread via config' '
+ git config format.thread true &&
+ check_threading expect.thread master
+'
+
+test_expect_success 'thread deep via config' '
+ git config format.thread deep &&
+ check_threading expect.deep master
+'
+
+test_expect_success 'thread config + override' '
+ git config format.thread deep &&
+ check_threading expect.thread --thread master
+'
+
+test_expect_success 'thread config + --no-thread' '
+ git config format.thread deep &&
+ check_threading expect.no-threading --no-thread master
+'
+
+test_expect_success 'excessive subject' '
+
+ rm -rf patches/ &&
+ git checkout side &&
+ for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >>file &&
+ git update-index file &&
+ git commit -m "This is an excessively long subject line for a message due to the habit some projects have of not having a short, one-line subject at the start of the commit message, but rather sticking a whole paragraph right at the start as the only thing in the commit message. It had better not become the filename for the patch." &&
+ git format-patch -o patches/ master..side &&
+ ls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch
+'
+
+test_expect_success 'cover-letter inherits diff options' '
+
+ git mv file foo &&
+ git commit -m foo &&
+ git format-patch --cover-letter -1 &&
+ ! grep "file => foo .* 0 *\$" 0000-cover-letter.patch &&
+ git format-patch --cover-letter -1 -M &&
+ grep "file => foo .* 0 *\$" 0000-cover-letter.patch
+
+'
+
+cat > expect << EOF
+ This is an excessively long subject line for a message due to the
+ habit some projects have of not having a short, one-line subject at
+ the start of the commit message, but rather sticking a whole
+ paragraph right at the start as the only thing in the commit
+ message. It had better not become the filename for the patch.
+ foo
+
+EOF
+
+test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
+
+ git format-patch --cover-letter -2 &&
+ sed -e "1,/A U Thor/d" -e "/^\$/q" < 0000-cover-letter.patch > output &&
+ test_cmp expect output
+
+'
+
+cat > expect << EOF
+---
+ file | 16 ++++++++++++++++
+ 1 files changed, 16 insertions(+), 0 deletions(-)
+
+diff --git a/file b/file
+index 40f36c6..2dc5c23 100644
+--- a/file
++++ b/file
+@@ -13,4 +13,20 @@ C
+ 10
+ D
+ E
+ F
++5
+EOF
+
+test_expect_success 'format-patch respects -U' '
+
+ git format-patch -U4 -2 &&
+ sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+ test_cmp expect output
+
+'
+
+cat > expect << EOF
+
+diff --git a/file b/file
+index 40f36c6..2dc5c23 100644
+--- a/file
++++ b/file
+@@ -14,3 +14,19 @@ C
+ D
+ E
+ F
++5
+EOF
+
+test_expect_success 'format-patch -p suppresses stat' '
+
+ git format-patch -p -2 &&
+ sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'format-patch from a subdirectory (1)' '
+ filename=$(
+ rm -rf sub &&
+ mkdir -p sub/dir &&
+ cd sub/dir &&
+ git format-patch -1
+ ) &&
+ case "$filename" in
+ 0*)
+ ;; # ok
+ *)
+ echo "Oops? $filename"
+ false
+ ;;
+ esac &&
+ test -f "$filename"
+'
+
+test_expect_success 'format-patch from a subdirectory (2)' '
+ filename=$(
+ rm -rf sub &&
+ mkdir -p sub/dir &&
+ cd sub/dir &&
+ git format-patch -1 -o ..
+ ) &&
+ case "$filename" in
+ ../0*)
+ ;; # ok
+ *)
+ echo "Oops? $filename"
+ false
+ ;;
+ esac &&
+ basename=$(expr "$filename" : ".*/\(.*\)") &&
+ test -f "sub/$basename"
+'
+
+test_expect_success 'format-patch from a subdirectory (3)' '
+ rm -f 0* &&
+ filename=$(
+ rm -rf sub &&
+ mkdir -p sub/dir &&
+ cd sub/dir &&
+ git format-patch -1 -o "$TRASH_DIRECTORY"
+ ) &&
+ basename=$(expr "$filename" : ".*/\(.*\)") &&
+ test -f "$basename"
+'
+
+test_expect_success 'format-patch --in-reply-to' '
+ git format-patch -1 --stdout --in-reply-to "baz@foo.bar" > patch8 &&
+ grep "^In-Reply-To: <baz@foo.bar>" patch8 &&
+ grep "^References: <baz@foo.bar>" patch8
+'
+
+test_expect_success 'format-patch --signoff' '
+ git format-patch -1 --signoff --stdout |
+ grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+'
+
+echo "fatal: --name-only does not make sense" > expect.name-only
+echo "fatal: --name-status does not make sense" > expect.name-status
+echo "fatal: --check does not make sense" > expect.check
+
+test_expect_success 'options no longer allowed for format-patch' '
+ test_must_fail git format-patch --name-only 2> output &&
+ test_cmp expect.name-only output &&
+ test_must_fail git format-patch --name-status 2> output &&
+ test_cmp expect.name-status output &&
+ test_must_fail git format-patch --check 2> output &&
+ test_cmp expect.check output'
+
+test_expect_success 'format-patch --numstat should produce a patch' '
+ git format-patch --numstat --stdout master..side > output &&
+ test 6 = $(grep "^diff --git a/" output | wc -l)'
+
+test_expect_success 'format-patch -- <path>' '
+ git format-patch master..side -- file 2>error &&
+ ! grep "Use .--" error
+'
+
+test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '
+ git format-patch --ignore-if-in-upstream HEAD
+'
+
+test_expect_success 'format-patch --signature' '
+ git format-patch --stdout --signature="my sig" -1 >output &&
+ grep "my sig" output
+'
+
+test_expect_success 'format-patch with format.signature config' '
+ git config format.signature "config sig" &&
+ git format-patch --stdout -1 >output &&
+ grep "config sig" output
+'
+
+test_expect_success 'format-patch --signature overrides format.signature' '
+ git config format.signature "config sig" &&
+ git format-patch --stdout --signature="overrides" -1 >output &&
+ ! grep "config sig" output &&
+ grep "overrides" output
+'
+
+test_expect_success 'format-patch --no-signature ignores format.signature' '
+ git config format.signature "config sig" &&
+ git format-patch --stdout --signature="my sig" --no-signature \
+ -1 >output &&
+ ! grep "config sig" output &&
+ ! grep "my sig" output &&
+ ! grep "^-- \$" output
+'
+
+test_expect_success 'format-patch --signature --cover-letter' '
+ git config --unset-all format.signature &&
+ git format-patch --stdout --signature="my sig" --cover-letter \
+ -1 >output &&
+ grep "my sig" output &&
+ test 2 = $(grep "my sig" output | wc -l)
+'
+
+test_expect_success 'format.signature="" supresses signatures' '
+ git config format.signature "" &&
+ git format-patch --stdout -1 >output &&
+ ! grep "^-- \$" output
+'
+
+test_expect_success 'format-patch --no-signature supresses signatures' '
+ git config --unset-all format.signature &&
+ git format-patch --stdout --no-signature -1 >output &&
+ ! grep "^-- \$" output
+'
+
+test_expect_success 'format-patch --signature="" supresses signatures' '
+ git format-patch --signature="" -1 >output &&
+ ! grep "^-- \$" output
+'
+
+test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
new file mode 100755
index 0000000..935d101
--- /dev/null
+++ b/t/t4015-diff-whitespace.sh
@@ -0,0 +1,494 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='Test special whitespace in diff engine.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+# Ray Lehtiniemi's example
+
+cat << EOF > x
+do {
+ nothing;
+} while (0);
+EOF
+
+git update-index --add x
+
+cat << EOF > x
+do
+{
+ nothing;
+}
+while (0);
+EOF
+
+cat << EOF > expect
+diff --git a/x b/x
+index adf3937..6edc172 100644
+--- a/x
++++ b/x
+@@ -1,3 +1,5 @@
+-do {
++do
++{
+ nothing;
+-} while (0);
++}
++while (0);
+EOF
+
+git diff > out
+test_expect_success "Ray's example without options" 'test_cmp expect out'
+
+git diff -w > out
+test_expect_success "Ray's example with -w" 'test_cmp expect out'
+
+git diff -b > out
+test_expect_success "Ray's example with -b" 'test_cmp expect out'
+
+tr 'Q' '\015' << EOF > x
+whitespace at beginning
+whitespace change
+whitespace in the middle
+whitespace at end
+unchanged line
+CR at endQ
+EOF
+
+git update-index x
+
+tr '_' ' ' << EOF > x
+ whitespace at beginning
+whitespace change
+white space in the middle
+whitespace at end__
+unchanged line
+CR at end
+EOF
+
+tr 'Q_' '\015 ' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
+-whitespace change
+-whitespace in the middle
+-whitespace at end
++ whitespace at beginning
++whitespace change
++white space in the middle
++whitespace at end__
+ unchanged line
+-CR at endQ
++CR at end
+EOF
+git diff > out
+test_expect_success 'another test, without options' 'test_cmp expect out'
+
+cat << EOF > expect
+EOF
+git diff -w > out
+test_expect_success 'another test, with -w' 'test_cmp expect out'
+git diff -w -b > out
+test_expect_success 'another test, with -w -b' 'test_cmp expect out'
+git diff -w --ignore-space-at-eol > out
+test_expect_success 'another test, with -w --ignore-space-at-eol' 'test_cmp expect out'
+git diff -w -b --ignore-space-at-eol > out
+test_expect_success 'another test, with -w -b --ignore-space-at-eol' 'test_cmp expect out'
+
+tr 'Q' '\015' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
++ whitespace at beginning
+ whitespace change
+-whitespace in the middle
++white space in the middle
+ whitespace at end
+ unchanged line
+ CR at endQ
+EOF
+git diff -b > out
+test_expect_success 'another test, with -b' 'test_cmp expect out'
+git diff -b --ignore-space-at-eol > out
+test_expect_success 'another test, with -b --ignore-space-at-eol' 'test_cmp expect out'
+
+tr 'Q' '\015' << EOF > expect
+diff --git a/x b/x
+index d99af23..8b32fb5 100644
+--- a/x
++++ b/x
+@@ -1,6 +1,6 @@
+-whitespace at beginning
+-whitespace change
+-whitespace in the middle
++ whitespace at beginning
++whitespace change
++white space in the middle
+ whitespace at end
+ unchanged line
+ CR at endQ
+EOF
+git diff --ignore-space-at-eol > out
+test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'
+
+test_expect_success 'check mixed spaces and tabs in indent' '
+
+ # This is indented with SP HT SP.
+ echo " foo();" > x &&
+ git diff --check | grep "space before tab in indent"
+
+'
+
+test_expect_success 'check mixed tabs and spaces in indent' '
+
+ # This is indented with HT SP HT.
+ echo " foo();" > x &&
+ git diff --check | grep "space before tab in indent"
+
+'
+
+test_expect_success 'check with no whitespace errors' '
+
+ git commit -m "snapshot" &&
+ echo "foo();" > x &&
+ git diff --check
+
+'
+
+test_expect_success 'check with trailing whitespace' '
+
+ echo "foo(); " > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check with space before tab in indent' '
+
+ # indent has space followed by hard tab
+ echo " foo();" > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success '--check and --exit-code are not exclusive' '
+
+ git checkout x &&
+ git diff --check --exit-code
+
+'
+
+test_expect_success '--check and --quiet are not exclusive' '
+
+ git diff --check --quiet
+
+'
+
+test_expect_success 'check staged with no whitespace errors' '
+
+ echo "foo();" > x &&
+ git add x &&
+ git diff --cached --check
+
+'
+
+test_expect_success 'check staged with trailing whitespace' '
+
+ echo "foo(); " > x &&
+ git add x &&
+ test_must_fail git diff --cached --check
+
+'
+
+test_expect_success 'check staged with space before tab in indent' '
+
+ # indent has space followed by hard tab
+ echo " foo();" > x &&
+ git add x &&
+ test_must_fail git diff --cached --check
+
+'
+
+test_expect_success 'check with no whitespace errors (diff-index)' '
+
+ echo "foo();" > x &&
+ git add x &&
+ git diff-index --check HEAD
+
+'
+
+test_expect_success 'check with trailing whitespace (diff-index)' '
+
+ echo "foo(); " > x &&
+ git add x &&
+ test_must_fail git diff-index --check HEAD
+
+'
+
+test_expect_success 'check with space before tab in indent (diff-index)' '
+
+ # indent has space followed by hard tab
+ echo " foo();" > x &&
+ git add x &&
+ test_must_fail git diff-index --check HEAD
+
+'
+
+test_expect_success 'check staged with no whitespace errors (diff-index)' '
+
+ echo "foo();" > x &&
+ git add x &&
+ git diff-index --cached --check HEAD
+
+'
+
+test_expect_success 'check staged with trailing whitespace (diff-index)' '
+
+ echo "foo(); " > x &&
+ git add x &&
+ test_must_fail git diff-index --cached --check HEAD
+
+'
+
+test_expect_success 'check staged with space before tab in indent (diff-index)' '
+
+ # indent has space followed by hard tab
+ echo " foo();" > x &&
+ git add x &&
+ test_must_fail git diff-index --cached --check HEAD
+
+'
+
+test_expect_success 'check with no whitespace errors (diff-tree)' '
+
+ echo "foo();" > x &&
+ git commit -m "new commit" x &&
+ git diff-tree --check HEAD^ HEAD
+
+'
+
+test_expect_success 'check with trailing whitespace (diff-tree)' '
+
+ echo "foo(); " > x &&
+ git commit -m "another commit" x &&
+ test_must_fail git diff-tree --check HEAD^ HEAD
+
+'
+
+test_expect_success 'check with space before tab in indent (diff-tree)' '
+
+ # indent has space followed by hard tab
+ echo " foo();" > x &&
+ git commit -m "yet another" x &&
+ test_must_fail git diff-tree --check HEAD^ HEAD
+
+'
+
+test_expect_success 'check trailing whitespace (trailing-space: off)' '
+
+ git config core.whitespace "-trailing-space" &&
+ echo "foo (); " > x &&
+ git diff --check
+
+'
+
+test_expect_success 'check trailing whitespace (trailing-space: on)' '
+
+ git config core.whitespace "trailing-space" &&
+ echo "foo (); " > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check space before tab in indent (space-before-tab: off)' '
+
+ # indent contains space followed by HT
+ git config core.whitespace "-space-before-tab" &&
+ echo " foo ();" > x &&
+ git diff --check
+
+'
+
+test_expect_success 'check space before tab in indent (space-before-tab: on)' '
+
+ # indent contains space followed by HT
+ git config core.whitespace "space-before-tab" &&
+ echo " foo (); " > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check spaces as indentation (indent-with-non-tab: off)' '
+
+ git config core.whitespace "-indent-with-non-tab"
+ echo " foo ();" > x &&
+ git diff --check
+
+'
+
+test_expect_success 'check spaces as indentation (indent-with-non-tab: on)' '
+
+ git config core.whitespace "indent-with-non-tab" &&
+ echo " foo ();" > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tabs and spaces as indentation (indent-with-non-tab: on)' '
+
+ git config core.whitespace "indent-with-non-tab" &&
+ echo " foo ();" > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
+
+ git config core.whitespace "-tab-in-indent" &&
+ echo " foo ();" > x &&
+ git diff --check
+
+'
+
+test_expect_success 'check tabs as indentation (tab-in-indent: on)' '
+
+ git config core.whitespace "tab-in-indent" &&
+ echo " foo ();" > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '
+
+ git config core.whitespace "tab-in-indent" &&
+ echo " foo ();" > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '
+
+ git config core.whitespace "tab-in-indent,indent-with-non-tab" &&
+ echo "foo ();" > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tab-in-indent excluded from wildcard whitespace attribute' '
+
+ git config --unset core.whitespace &&
+ echo "x whitespace" > .gitattributes &&
+ echo " foo ();" > x &&
+ git diff --check &&
+ rm -f .gitattributes
+
+'
+
+test_expect_success 'line numbers in --check output are correct' '
+
+ echo "" > x &&
+ echo "foo(); " >> x &&
+ git diff --check | grep "x:2:"
+
+'
+
+test_expect_success 'checkdiff detects new trailing blank lines (1)' '
+ echo "foo();" >x &&
+ echo "" >>x &&
+ git diff --check | grep "new blank line"
+'
+
+test_expect_success 'checkdiff detects new trailing blank lines (2)' '
+ { echo a; echo b; echo; echo; } >x &&
+ git add x &&
+ { echo a; echo; echo; echo; echo; } >x &&
+ git diff --check | grep "new blank line"
+'
+
+test_expect_success 'checkdiff allows new blank lines' '
+ git checkout x &&
+ mv x y &&
+ (
+ echo "/* This is new */" &&
+ echo "" &&
+ cat y
+ ) >x &&
+ git diff --check
+'
+
+cat <<EOF >expect
+EOF
+test_expect_success 'whitespace-only changes not reported' '
+ git reset --hard &&
+ echo >x "hello world" &&
+ git add x &&
+ git commit -m "hello 1" &&
+ echo >x "hello world" &&
+ git diff -b >actual &&
+ test_cmp expect actual
+'
+
+cat <<EOF >expect
+diff --git a/x b/z
+similarity index NUM%
+rename from x
+rename to z
+index 380c32a..a97b785 100644
+EOF
+test_expect_success 'whitespace-only changes reported across renames' '
+ git reset --hard &&
+ for i in 1 2 3 4 5 6 7 8 9; do echo "$i$i$i$i$i$i"; done >x &&
+ git add x &&
+ git commit -m "base" &&
+ sed -e "5s/^/ /" x >z &&
+ git rm x &&
+ git add z &&
+ git diff -w -M --cached |
+ sed -e "/^similarity index /s/[0-9][0-9]*/NUM/" >actual &&
+ test_cmp expect actual
+'
+
+cat >expected <<\EOF
+diff --git a/empty b/void
+similarity index 100%
+rename from empty
+rename to void
+EOF
+
+test_expect_success 'rename empty' '
+ git reset --hard &&
+ >empty &&
+ git add empty &&
+ git commit -m empty &&
+ git mv empty void &&
+ git diff -w --cached -M >current &&
+ test_cmp expected current
+'
+
+test_expect_success 'combined diff with autocrlf conversion' '
+
+ git reset --hard &&
+ echo >x hello &&
+ git commit -m "one side" x &&
+ git checkout HEAD^ &&
+ echo >x goodbye &&
+ git commit -m "the other side" x &&
+ git config core.autocrlf true &&
+ test_must_fail git merge master &&
+
+ git diff | sed -e "1,/^@@@/d" >actual &&
+ ! grep "^-" actual
+
+'
+
+test_done
diff --git a/t/t4016-diff-quote.sh b/t/t4016-diff-quote.sh
new file mode 100755
index 0000000..34e5144
--- /dev/null
+++ b/t/t4016-diff-quote.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Junio C Hamano
+#
+
+test_description='Quoting paths in diff output.
+'
+
+. ./test-lib.sh
+
+P0='pathname'
+P1='pathname with HT'
+P2='pathname with SP'
+P3='pathname
+with LF'
+: 2>/dev/null >"$P1" && test -f "$P1" && rm -f "$P1" || {
+ skip_all='Your filesystem does not allow tabs in filenames, test skipped.'
+ test_done
+}
+
+test_expect_success setup '
+ echo P0.0 >"$P0.0" &&
+ echo P0.1 >"$P0.1" &&
+ echo P0.2 >"$P0.2" &&
+ echo P0.3 >"$P0.3" &&
+ echo P1.0 >"$P1.0" &&
+ echo P1.2 >"$P1.2" &&
+ echo P1.3 >"$P1.3" &&
+ git add . &&
+ git commit -m initial &&
+ git mv "$P0.0" "R$P0.0" &&
+ git mv "$P0.1" "R$P1.0" &&
+ git mv "$P0.2" "R$P2.0" &&
+ git mv "$P0.3" "R$P3.0" &&
+ git mv "$P1.0" "R$P0.1" &&
+ git mv "$P1.2" "R$P2.1" &&
+ git mv "$P1.3" "R$P3.1" &&
+ :
+'
+
+cat >expect <<\EOF
+ rename pathname.1 => "Rpathname\twith HT.0" (100%)
+ rename pathname.3 => "Rpathname\nwith LF.0" (100%)
+ rename "pathname\twith HT.3" => "Rpathname\nwith LF.1" (100%)
+ rename pathname.2 => Rpathname with SP.0 (100%)
+ rename "pathname\twith HT.2" => Rpathname with SP.1 (100%)
+ rename pathname.0 => Rpathname.0 (100%)
+ rename "pathname\twith HT.0" => Rpathname.1 (100%)
+EOF
+test_expect_success 'git diff --summary -M HEAD' '
+ git diff --summary -M HEAD >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
+ pathname.1 => "Rpathname\twith HT.0" | 0
+ pathname.3 => "Rpathname\nwith LF.0" | 0
+ "pathname\twith HT.3" => "Rpathname\nwith LF.1" | 0
+ pathname.2 => Rpathname with SP.0 | 0
+ "pathname\twith HT.2" => Rpathname with SP.1 | 0
+ pathname.0 => Rpathname.0 | 0
+ "pathname\twith HT.0" => Rpathname.1 | 0
+ 7 files changed, 0 insertions(+), 0 deletions(-)
+EOF
+test_expect_success 'git diff --stat -M HEAD' '
+ git diff --stat -M HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4017-diff-retval.sh b/t/t4017-diff-retval.sh
new file mode 100755
index 0000000..6158985
--- /dev/null
+++ b/t/t4017-diff-retval.sh
@@ -0,0 +1,166 @@
+#!/bin/sh
+
+test_description='Return value of diffs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo "1 " >a &&
+ git add . &&
+ git commit -m zeroth &&
+ echo 1 >a &&
+ git add . &&
+ git commit -m first &&
+ echo 2 >b &&
+ git add . &&
+ git commit -a -m second
+'
+
+test_expect_success 'git diff --quiet -w HEAD^^ HEAD^' '
+ git diff --quiet -w HEAD^^ HEAD^
+'
+
+test_expect_success 'git diff --quiet HEAD^^ HEAD^' '
+ test_must_fail git diff --quiet HEAD^^ HEAD^
+'
+
+test_expect_success 'git diff --quiet -w HEAD^ HEAD' '
+ test_must_fail git diff --quiet -w HEAD^ HEAD
+'
+
+test_expect_success 'git diff-tree HEAD^ HEAD' '
+ git diff-tree --exit-code HEAD^ HEAD
+ test $? = 1
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+ git diff-tree --exit-code HEAD^ HEAD -- a
+ test $? = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+ git diff-tree --exit-code HEAD^ HEAD -- b
+ test $? = 1
+'
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+ echo $(git rev-parse HEAD) | git diff-tree --exit-code --stdin
+ test $? = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+ git diff-tree --exit-code HEAD HEAD
+ test $? = 0
+'
+test_expect_success 'git diff-files' '
+ git diff-files --exit-code
+ test $? = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+ git diff-index --exit-code --cached HEAD
+ test $? = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+ git diff-index --exit-code --cached HEAD^
+ test $? = 1
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+ echo text >>b &&
+ echo 3 >c &&
+ git add . && {
+ git diff-index --exit-code --cached HEAD^
+ test $? = 1
+ }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+ git commit -m "text in b" && {
+ git diff-tree -p --exit-code -Stext HEAD^ HEAD -- b
+ test $? = 1
+ }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+ git diff-tree -p --exit-code -Snot-found HEAD^ HEAD -- b
+ test $? = 0
+'
+test_expect_success 'git diff-files' '
+ echo 3 >>c && {
+ git diff-files --exit-code
+ test $? = 1
+ }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+ git update-index c && {
+ git diff-index --exit-code --cached HEAD
+ test $? = 1
+ }
+'
+
+test_expect_success '--check --exit-code returns 0 for no difference' '
+
+ git diff --check --exit-code
+
+'
+
+test_expect_success '--check --exit-code returns 1 for a clean difference' '
+
+ echo "good" > a &&
+ git diff --check --exit-code
+ test $? = 1
+
+'
+
+test_expect_success '--check --exit-code returns 3 for a dirty difference' '
+
+ echo "bad " >> a &&
+ git diff --check --exit-code
+ test $? = 3
+
+'
+
+test_expect_success '--check with --no-pager returns 2 for dirty difference' '
+
+ git --no-pager diff --check
+ test $? = 2
+
+'
+
+test_expect_success 'check should test not just the last line' '
+ echo "" >>a &&
+ git --no-pager diff --check
+ test $? = 2
+
+'
+
+test_expect_success 'check detects leftover conflict markers' '
+ git reset --hard &&
+ git checkout HEAD^ &&
+ echo binary >>b &&
+ git commit -m "side" b &&
+ test_must_fail git merge master &&
+ git add b && (
+ git --no-pager diff --cached --check >test.out
+ test $? = 2
+ ) &&
+ test 3 = $(grep "conflict marker" test.out | wc -l) &&
+ git reset --hard
+'
+
+test_expect_success 'check honors conflict marker length' '
+ git reset --hard &&
+ echo ">>>>>>> boo" >>b &&
+ echo "======" >>a &&
+ git diff --check a &&
+ (
+ git diff --check b
+ test $? = 2
+ ) &&
+ git reset --hard &&
+ echo ">>>>>>>> boo" >>b &&
+ echo "========" >>a &&
+ git diff --check &&
+ echo "b conflict-marker-size=8" >.gitattributes &&
+ (
+ git diff --check b
+ test $? = 2
+ ) &&
+ git diff --check a &&
+ git reset --hard
+'
+
+test_done
diff --git a/t/t4018-diff-funcname.sh b/t/t4018-diff-funcname.sh
new file mode 100755
index 0000000..5b10e97
--- /dev/null
+++ b/t/t4018-diff-funcname.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='Test custom diff function name patterns'
+
+. ./test-lib.sh
+
+LF='
+'
+
+cat > Beer.java << EOF
+public class Beer
+{
+ int special;
+ public static void main(String args[])
+ {
+ String s=" ";
+ for(int x = 99; x > 0; x--)
+ {
+ System.out.print(x + " bottles of beer on the wall "
+ + x + " bottles of beer\n"
+ + "Take one down, pass it around, " + (x - 1)
+ + " bottles of beer on the wall.\n");
+ }
+ System.out.print("Go to the store, buy some more,\n"
+ + "99 bottles of beer on the wall.\n");
+ }
+}
+EOF
+
+sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
+
+builtin_patterns="bibtex cpp html java objc pascal php python ruby tex"
+for p in $builtin_patterns
+do
+ test_expect_success "builtin $p pattern compiles" '
+ echo "*.java diff=$p" > .gitattributes &&
+ ! ( git diff --no-index Beer.java Beer-correct.java 2>&1 |
+ grep "fatal" > /dev/null )
+ '
+done
+
+test_expect_success 'default behaviour' '
+ rm -f .gitattributes &&
+ git diff --no-index Beer.java Beer-correct.java |
+ grep "^@@.*@@ public class Beer"
+'
+
+test_expect_success 'preset java pattern' '
+ echo "*.java diff=java" >.gitattributes &&
+ git diff --no-index Beer.java Beer-correct.java |
+ grep "^@@.*@@ public static void main("
+'
+
+git config diff.java.funcname '!static
+!String
+[^ ].*s.*'
+
+test_expect_success 'custom pattern' '
+ git diff --no-index Beer.java Beer-correct.java |
+ grep "^@@.*@@ int special;$"
+'
+
+test_expect_success 'last regexp must not be negated' '
+ git config diff.java.funcname "!static" &&
+ git diff --no-index Beer.java Beer-correct.java 2>&1 |
+ grep "fatal: Last expression must not be negated:"
+'
+
+test_expect_success 'pattern which matches to end of line' '
+ git config diff.java.funcname "Beer$" &&
+ git diff --no-index Beer.java Beer-correct.java |
+ grep "^@@.*@@ Beer"
+'
+
+test_expect_success 'alternation in pattern' '
+ git config diff.java.xfuncname "^[ ]*((public|static).*)$" &&
+ git diff --no-index Beer.java Beer-correct.java |
+ grep "^@@.*@@ public static void main("
+'
+
+test_done
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh
new file mode 100755
index 0000000..f6d1f1e
--- /dev/null
+++ b/t/t4019-diff-wserror.sh
@@ -0,0 +1,202 @@
+#!/bin/sh
+
+test_description='diff whitespace error detection'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ git config diff.color.whitespace "blue reverse" &&
+ >F &&
+ git add F &&
+ echo " Eight SP indent" >>F &&
+ echo " HT and SP indent" >>F &&
+ echo "With trailing SP " >>F &&
+ echo "Carriage ReturnQ" | tr Q "\015" >>F &&
+ echo "No problem" >>F &&
+ echo >>F
+
+'
+
+blue_grep='7;34m' ;# ESC [ 7 ; 3 4 m
+
+printf "\033[%s" "$blue_grep" >check-grep
+if (grep "$blue_grep" <check-grep | grep "$blue_grep") >/dev/null 2>&1
+then
+ grep_a=grep
+elif (grep -a "$blue_grep" <check-grep | grep -a "$blue_grep") >/dev/null 2>&1
+then
+ grep_a='grep -a'
+else
+ grep_a=grep ;# expected to fail...
+fi
+rm -f check-grep
+
+prepare_output () {
+ git diff --color >output
+ $grep_a "$blue_grep" output >error
+ $grep_a -v "$blue_grep" output >normal
+}
+
+test_expect_success default '
+
+ prepare_output
+
+ grep Eight normal >/dev/null &&
+ grep HT error >/dev/null &&
+ grep With error >/dev/null &&
+ grep Return error >/dev/null &&
+ grep No normal >/dev/null
+
+'
+
+test_expect_success 'without -trail' '
+
+ git config core.whitespace -trail
+ prepare_output
+
+ grep Eight normal >/dev/null &&
+ grep HT error >/dev/null &&
+ grep With normal >/dev/null &&
+ grep Return normal >/dev/null &&
+ grep No normal >/dev/null
+
+'
+
+test_expect_success 'without -trail (attribute)' '
+
+ git config --unset core.whitespace
+ echo "F whitespace=-trail" >.gitattributes
+ prepare_output
+
+ grep Eight normal >/dev/null &&
+ grep HT error >/dev/null &&
+ grep With normal >/dev/null &&
+ grep Return normal >/dev/null &&
+ grep No normal >/dev/null
+
+'
+
+test_expect_success 'without -space' '
+
+ rm -f .gitattributes
+ git config core.whitespace -space
+ prepare_output
+
+ grep Eight normal >/dev/null &&
+ grep HT normal >/dev/null &&
+ grep With error >/dev/null &&
+ grep Return error >/dev/null &&
+ grep No normal >/dev/null
+
+'
+
+test_expect_success 'without -space (attribute)' '
+
+ git config --unset core.whitespace
+ echo "F whitespace=-space" >.gitattributes
+ prepare_output
+
+ grep Eight normal >/dev/null &&
+ grep HT normal >/dev/null &&
+ grep With error >/dev/null &&
+ grep Return error >/dev/null &&
+ grep No normal >/dev/null
+
+'
+
+test_expect_success 'with indent-non-tab only' '
+
+ rm -f .gitattributes
+ git config core.whitespace indent,-trailing,-space
+ prepare_output
+
+ grep Eight error >/dev/null &&
+ grep HT normal >/dev/null &&
+ grep With normal >/dev/null &&
+ grep Return normal >/dev/null &&
+ grep No normal >/dev/null
+
+'
+
+test_expect_success 'with indent-non-tab only (attribute)' '
+
+ git config --unset core.whitespace
+ echo "F whitespace=indent,-trailing,-space" >.gitattributes
+ prepare_output
+
+ grep Eight error >/dev/null &&
+ grep HT normal >/dev/null &&
+ grep With normal >/dev/null &&
+ grep Return normal >/dev/null &&
+ grep No normal >/dev/null
+
+'
+
+test_expect_success 'with cr-at-eol' '
+
+ rm -f .gitattributes
+ git config core.whitespace cr-at-eol
+ prepare_output
+
+ grep Eight normal >/dev/null &&
+ grep HT error >/dev/null &&
+ grep With error >/dev/null &&
+ grep Return normal >/dev/null &&
+ grep No normal >/dev/null
+
+'
+
+test_expect_success 'with cr-at-eol (attribute)' '
+
+ git config --unset core.whitespace
+ echo "F whitespace=trailing,cr-at-eol" >.gitattributes
+ prepare_output
+
+ grep Eight normal >/dev/null &&
+ grep HT error >/dev/null &&
+ grep With error >/dev/null &&
+ grep Return normal >/dev/null &&
+ grep No normal >/dev/null
+
+'
+
+test_expect_success 'trailing empty lines (1)' '
+
+ rm -f .gitattributes &&
+ test_must_fail git diff --check >output &&
+ grep "new blank line at" output &&
+ grep "trailing whitespace" output
+
+'
+
+test_expect_success 'trailing empty lines (2)' '
+
+ echo "F -whitespace" >.gitattributes &&
+ git diff --check >output &&
+ ! test -s output
+
+'
+
+test_expect_success 'do not color trailing cr in context' '
+ git config --unset core.whitespace
+ rm -f .gitattributes &&
+ echo AAAQ | tr Q "\015" >G &&
+ git add G &&
+ echo BBBQ | tr Q "\015" >>G
+ git diff --color G | tr "\015" Q >output &&
+ grep "BBB.*${blue_grep}Q" output &&
+ grep "AAA.*\[mQ" output
+
+'
+
+test_expect_success 'color new trailing blank lines' '
+ { echo a; echo b; echo; echo; } >x &&
+ git add x &&
+ { echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&
+ git diff --color x >output &&
+ cnt=$($grep_a "${blue_grep}" output | wc -l) &&
+ test $cnt = 2
+'
+
+test_done
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
new file mode 100755
index 0000000..a7602cf
--- /dev/null
+++ b/t/t4020-diff-external.sh
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+test_description='external diff interface test'
+
+. ./test-lib.sh
+
+_z40=0000000000000000000000000000000000000000
+
+test_expect_success setup '
+
+ test_tick &&
+ echo initial >file &&
+ git add file &&
+ git commit -m initial &&
+
+ test_tick &&
+ echo second >file &&
+ git add file &&
+ git commit -m second &&
+
+ test_tick &&
+ echo third >file
+'
+
+test_expect_success 'GIT_EXTERNAL_DIFF environment' '
+
+ GIT_EXTERNAL_DIFF=echo git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$path" = zfile &&
+ test "z$oldmode" = z100644 &&
+ test "z$newhex" = "z$_z40" &&
+ test "z$newmode" = z100644 &&
+ oh=$(git rev-parse --verify HEAD:file) &&
+ test "z$oh" = "z$oldhex"
+ }
+
+'
+
+test_expect_success 'GIT_EXTERNAL_DIFF environment should apply only to diff' '
+
+ GIT_EXTERNAL_DIFF=echo git log -p -1 HEAD |
+ grep "^diff --git a/file b/file"
+
+'
+
+test_expect_success 'GIT_EXTERNAL_DIFF environment and --no-ext-diff' '
+
+ GIT_EXTERNAL_DIFF=echo git diff --no-ext-diff |
+ grep "^diff --git a/file b/file"
+
+'
+
+test_expect_success 'diff attribute' '
+
+ git config diff.parrot.command echo &&
+
+ echo >.gitattributes "file diff=parrot" &&
+
+ git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$path" = zfile &&
+ test "z$oldmode" = z100644 &&
+ test "z$newhex" = "z$_z40" &&
+ test "z$newmode" = z100644 &&
+ oh=$(git rev-parse --verify HEAD:file) &&
+ test "z$oh" = "z$oldhex"
+ }
+
+'
+
+test_expect_success 'diff attribute should apply only to diff' '
+
+ git log -p -1 HEAD |
+ grep "^diff --git a/file b/file"
+
+'
+
+test_expect_success 'diff attribute and --no-ext-diff' '
+
+ git diff --no-ext-diff |
+ grep "^diff --git a/file b/file"
+
+'
+
+test_expect_success 'diff attribute' '
+
+ git config --unset diff.parrot.command &&
+ git config diff.color.command echo &&
+
+ echo >.gitattributes "file diff=color" &&
+
+ git diff | {
+ read path oldfile oldhex oldmode newfile newhex newmode &&
+ test "z$path" = zfile &&
+ test "z$oldmode" = z100644 &&
+ test "z$newhex" = "z$_z40" &&
+ test "z$newmode" = z100644 &&
+ oh=$(git rev-parse --verify HEAD:file) &&
+ test "z$oh" = "z$oldhex"
+ }
+
+'
+
+test_expect_success 'diff attribute should apply only to diff' '
+
+ git log -p -1 HEAD |
+ grep "^diff --git a/file b/file"
+
+'
+
+test_expect_success 'diff attribute and --no-ext-diff' '
+
+ git diff --no-ext-diff |
+ grep "^diff --git a/file b/file"
+
+'
+
+test_expect_success 'no diff with -diff' '
+ echo >.gitattributes "file -diff" &&
+ git diff | grep Binary
+'
+
+echo NULZbetweenZwords | perl -pe 'y/Z/\000/' > file
+
+test_expect_success 'force diff with "diff"' '
+ echo >.gitattributes "file diff" &&
+ git diff >actual &&
+ test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
+'
+
+test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' '
+ echo anotherfile > file2 &&
+ git add file2 &&
+ git commit -m "added 2nd file" &&
+ echo modified >file2 &&
+ GIT_EXTERNAL_DIFF=echo git diff
+'
+
+test_expect_success 'GIT_EXTERNAL_DIFF generates pretty paths' '
+ touch file.ext &&
+ git add file.ext &&
+ echo with extension > file.ext &&
+ GIT_EXTERNAL_DIFF=echo git diff file.ext | grep ......_file\.ext &&
+ git update-index --force-remove file.ext &&
+ rm file.ext
+'
+
+echo "#!$SHELL_PATH" >fake-diff.sh
+cat >> fake-diff.sh <<\EOF
+cat $2 >> crlfed.txt
+EOF
+chmod a+x fake-diff.sh
+
+keep_only_cr () {
+ tr -dc '\015'
+}
+
+test_expect_success 'external diff with autocrlf = true' '
+ git config core.autocrlf true &&
+ GIT_EXTERNAL_DIFF=./fake-diff.sh git diff &&
+ test $(wc -l < crlfed.txt) = $(cat crlfed.txt | keep_only_cr | wc -c)
+'
+
+test_expect_success 'diff --cached' '
+ git add file &&
+ git update-index --assume-unchanged file &&
+ echo second >file &&
+ git diff --cached >actual &&
+ test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
+'
+
+test_done
diff --git a/t/t4020/diff.NUL b/t/t4020/diff.NUL
new file mode 100644
index 0000000..db2f890
--- /dev/null
+++ b/t/t4020/diff.NUL
Binary files differ
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
new file mode 100755
index 0000000..709b323
--- /dev/null
+++ b/t/t4021-format-patch-numbered.sh
@@ -0,0 +1,124 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Brian C Gernhardt
+#
+
+test_description='Format-patch numbering options'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ echo A > file &&
+ git add file &&
+ git commit -m First &&
+
+ echo B >> file &&
+ git commit -a -m Second &&
+
+ echo C >> file &&
+ git commit -a -m Third
+
+'
+
+# Each of these gets used multiple times.
+
+test_num_no_numbered() {
+ cnt=$(grep "^Subject: \[PATCH\]" $1 | wc -l) &&
+ test $cnt = $2
+}
+
+test_single_no_numbered() {
+ test_num_no_numbered $1 1
+}
+
+test_no_numbered() {
+ test_num_no_numbered $1 2
+}
+
+test_single_numbered() {
+ grep "^Subject: \[PATCH 1/1\]" $1
+}
+
+test_numbered() {
+ grep "^Subject: \[PATCH 1/2\]" $1 &&
+ grep "^Subject: \[PATCH 2/2\]" $1
+}
+
+test_expect_success 'single patch defaults to no numbers' '
+ git format-patch --stdout HEAD~1 >patch0.single &&
+ test_single_no_numbered patch0.single
+'
+
+test_expect_success 'multiple patch defaults to numbered' '
+
+ git format-patch --stdout HEAD~2 >patch0.multiple &&
+ test_numbered patch0.multiple
+
+'
+
+test_expect_success 'Use --numbered' '
+
+ git format-patch --numbered --stdout HEAD~1 >patch1 &&
+ test_single_numbered patch1
+
+'
+
+test_expect_success 'format.numbered = true' '
+
+ git config format.numbered true &&
+ git format-patch --stdout HEAD~2 >patch2 &&
+ test_numbered patch2
+
+'
+
+test_expect_success 'format.numbered && single patch' '
+
+ git format-patch --stdout HEAD^ > patch3 &&
+ test_single_numbered patch3
+
+'
+
+test_expect_success 'format.numbered && --no-numbered' '
+
+ git format-patch --no-numbered --stdout HEAD~2 >patch4 &&
+ test_no_numbered patch4
+
+'
+
+test_expect_success 'format.numbered && --keep-subject' '
+
+ git format-patch --keep-subject --stdout HEAD^ >patch4a &&
+ grep "^Subject: Third" patch4a
+
+'
+
+test_expect_success 'format.numbered = auto' '
+
+ git config format.numbered auto
+ git format-patch --stdout HEAD~2 > patch5 &&
+ test_numbered patch5
+
+'
+
+test_expect_success 'format.numbered = auto && single patch' '
+
+ git format-patch --stdout HEAD^ > patch6 &&
+ test_single_no_numbered patch6
+
+'
+
+test_expect_success 'format.numbered = auto && --no-numbered' '
+
+ git format-patch --no-numbered --stdout HEAD~2 > patch7 &&
+ test_no_numbered patch7
+
+'
+
+test_expect_success '--start-number && --numbered' '
+
+ git format-patch --start-number 3 --numbered --stdout HEAD~1 > patch8 &&
+ grep "^Subject: \[PATCH 3/3\]" patch8
+'
+
+test_done
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
new file mode 100755
index 0000000..2a537a2
--- /dev/null
+++ b/t/t4022-diff-rewrite.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+test_description='rewrite diff'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ cat "$TEST_DIRECTORY"/../COPYING >test &&
+ git add test &&
+ tr \
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
+ <"$TEST_DIRECTORY"/../COPYING >test
+
+'
+
+test_expect_success 'detect rewrite' '
+
+ actual=$(git diff-files -B --summary test) &&
+ expr "$actual" : " rewrite test ([0-9]*%)$" || {
+ echo "Eh? <<$actual>>"
+ false
+ }
+
+'
+
+test_done
+
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
new file mode 100755
index 0000000..40a95a1
--- /dev/null
+++ b/t/t4023-diff-rename-typechange.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='typechange rename detection'
+
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='Symbolic links not supported, skipping tests.'
+ test_done
+fi
+
+test_expect_success setup '
+
+ rm -f foo bar &&
+ cat "$TEST_DIRECTORY"/../COPYING >foo &&
+ ln -s linklink bar &&
+ git add foo bar &&
+ git commit -a -m Initial &&
+ git tag one &&
+
+ rm -f foo bar &&
+ cat "$TEST_DIRECTORY"/../COPYING >bar &&
+ ln -s linklink foo &&
+ git add foo bar &&
+ git commit -a -m Second &&
+ git tag two &&
+
+ rm -f foo bar &&
+ cat "$TEST_DIRECTORY"/../COPYING >foo &&
+ git add foo &&
+ git commit -a -m Third &&
+ git tag three &&
+
+ mv foo bar &&
+ ln -s linklink foo &&
+ git add foo bar &&
+ git commit -a -m Fourth &&
+ git tag four &&
+
+ # This is purely for sanity check
+
+ rm -f foo bar &&
+ cat "$TEST_DIRECTORY"/../COPYING >foo &&
+ cat "$TEST_DIRECTORY"/../Makefile >bar &&
+ git add foo bar &&
+ git commit -a -m Fifth &&
+ git tag five &&
+
+ rm -f foo bar &&
+ cat "$TEST_DIRECTORY"/../Makefile >foo &&
+ cat "$TEST_DIRECTORY"/../COPYING >bar &&
+ git add foo bar &&
+ git commit -a -m Sixth &&
+ git tag six
+
+'
+
+test_expect_success 'cross renames to be detected for regular files' '
+
+ git diff-tree five six -r --name-status -B -M | sort >actual &&
+ {
+ echo "R100 foo bar"
+ echo "R100 bar foo"
+ } | sort >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'cross renames to be detected for typechange' '
+
+ git diff-tree one two -r --name-status -B -M | sort >actual &&
+ {
+ echo "R100 foo bar"
+ echo "R100 bar foo"
+ } | sort >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'moves and renames' '
+
+ git diff-tree three four -r --name-status -B -M | sort >actual &&
+ {
+ echo "R100 foo bar"
+ echo "T100 foo"
+ } | sort >expect &&
+ test_cmp expect actual
+
+'
+
+test_done
diff --git a/t/t4024-diff-optimize-common.sh b/t/t4024-diff-optimize-common.sh
new file mode 100755
index 0000000..c4d733f
--- /dev/null
+++ b/t/t4024-diff-optimize-common.sh
@@ -0,0 +1,157 @@
+#!/bin/sh
+
+test_description='common tail optimization'
+
+. ./test-lib.sh
+
+z=zzzzzzzz ;# 8
+z="$z$z$z$z$z$z$z$z" ;# 64
+z="$z$z$z$z$z$z$z$z" ;# 512
+z="$z$z$z$z" ;# 2048
+z2047=$(expr "$z" : '.\(.*\)') ; #2047
+
+x=zzzzzzzzzz ;# 10
+y="$x$x$x$x$x$x$x$x$x$x" ;# 100
+z="$y$y$y$y$y$y$y$y$y$y" ;# 1000
+z1000=$z
+z100=$y
+z10=$x
+
+zs() {
+ count="$1"
+ while test "$count" -ge 1000
+ do
+ count=$(($count - 1000))
+ printf "%s" $z1000
+ done
+ while test "$count" -ge 100
+ do
+ count=$(($count - 100))
+ printf "%s" $z100
+ done
+ while test "$count" -ge 10
+ do
+ count=$(($count - 10))
+ printf "%s" $z10
+ done
+ while test "$count" -ge 1
+ do
+ count=$(($count - 1))
+ printf "z"
+ done
+}
+
+zc () {
+ sed -e "/^index/d" \
+ -e "s/$z1000/Q/g" \
+ -e "s/QQQQQQQQQ/Z9000/g" \
+ -e "s/QQQQQQQQ/Z8000/g" \
+ -e "s/QQQQQQQ/Z7000/g" \
+ -e "s/QQQQQQ/Z6000/g" \
+ -e "s/QQQQQ/Z5000/g" \
+ -e "s/QQQQ/Z4000/g" \
+ -e "s/QQQ/Z3000/g" \
+ -e "s/QQ/Z2000/g" \
+ -e "s/Q/Z1000/g" \
+ -e "s/$z100/Q/g" \
+ -e "s/QQQQQQQQQ/Z900/g" \
+ -e "s/QQQQQQQQ/Z800/g" \
+ -e "s/QQQQQQQ/Z700/g" \
+ -e "s/QQQQQQ/Z600/g" \
+ -e "s/QQQQQ/Z500/g" \
+ -e "s/QQQQ/Z400/g" \
+ -e "s/QQQ/Z300/g" \
+ -e "s/QQ/Z200/g" \
+ -e "s/Q/Z100/g" \
+ -e "s/000Z//g" \
+ -e "s/$z10/Q/g" \
+ -e "s/QQQQQQQQQ/Z90/g" \
+ -e "s/QQQQQQQQ/Z80/g" \
+ -e "s/QQQQQQQ/Z70/g" \
+ -e "s/QQQQQQ/Z60/g" \
+ -e "s/QQQQQ/Z50/g" \
+ -e "s/QQQQ/Z40/g" \
+ -e "s/QQQ/Z30/g" \
+ -e "s/QQ/Z20/g" \
+ -e "s/Q/Z10/g" \
+ -e "s/00Z//g" \
+ -e "s/z/Q/g" \
+ -e "s/QQQQQQQQQ/Z9/g" \
+ -e "s/QQQQQQQQ/Z8/g" \
+ -e "s/QQQQQQQ/Z7/g" \
+ -e "s/QQQQQQ/Z6/g" \
+ -e "s/QQQQQ/Z5/g" \
+ -e "s/QQQQ/Z4/g" \
+ -e "s/QQQ/Z3/g" \
+ -e "s/QQ/Z2/g" \
+ -e "s/Q/Z1/g" \
+ -e "s/0Z//g" \
+ ;
+}
+
+expect_pattern () {
+ cnt="$1"
+ cat <<EOF
+diff --git a/file-a$cnt b/file-a$cnt
+--- a/file-a$cnt
++++ b/file-a$cnt
+@@ -1 +1 @@
+-Z${cnt}a
++Z${cnt}A
+diff --git a/file-b$cnt b/file-b$cnt
+--- a/file-b$cnt
++++ b/file-b$cnt
+@@ -1 +1 @@
+-b
++B
+diff --git a/file-c$cnt b/file-c$cnt
+--- a/file-c$cnt
++++ b/file-c$cnt
+@@ -1 +1 @@
+-cZ$cnt
+\ No newline at end of file
++CZ$cnt
+\ No newline at end of file
+diff --git a/file-d$cnt b/file-d$cnt
+--- a/file-d$cnt
++++ b/file-d$cnt
+@@ -1 +1 @@
+-d
++D
+EOF
+}
+
+sample='1023 1024 1025 2047 4095'
+
+test_expect_success setup '
+
+ for n in $sample
+ do
+ ( zs $n ; echo a ) >file-a$n &&
+ ( echo b; zs $n; echo ) >file-b$n &&
+ ( printf c; zs $n ) >file-c$n &&
+ ( echo d; zs $n ) >file-d$n &&
+
+ git add file-a$n file-b$n file-c$n file-d$n &&
+
+ ( zs $n ; echo A ) >file-a$n &&
+ ( echo B; zs $n; echo ) >file-b$n &&
+ ( printf C; zs $n ) >file-c$n &&
+ ( echo D; zs $n ) >file-d$n &&
+
+ expect_pattern $n || break
+
+ done >expect
+'
+
+test_expect_success 'diff -U0' '
+
+ for n in $sample
+ do
+ git diff -U0 file-?$n
+ done | zc >actual &&
+ test_cmp expect actual
+
+'
+
+test_done
diff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh
new file mode 100755
index 0000000..7a3dbc1
--- /dev/null
+++ b/t/t4025-hunk-header.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description='diff hunk header truncation'
+
+. ./test-lib.sh
+
+N='日本語'
+N1='æ—¥'
+N2='日本'
+NS="$N$N$N$N$N$N$N$N$N$N$N$N$N"
+
+test_expect_success setup '
+
+ (
+ echo "A $NS"
+ for c in B C D E F G H I J K
+ do
+ echo " $c"
+ done
+ echo "L $NS"
+ for c in M N O P Q R S T U V
+ do
+ echo " $c"
+ done
+ ) >file &&
+ git add file &&
+
+ sed -e "/^ [EP]/s/$/ modified/" <file >file+ &&
+ mv file+ file
+
+'
+
+test_expect_success 'hunk header truncation with an overly long line' '
+
+ git diff | sed -n -e "s/^.*@@//p" >actual &&
+ (
+ echo " A $N$N$N$N$N$N$N$N$N2"
+ echo " L $N$N$N$N$N$N$N$N$N1"
+ ) >expected &&
+ test_cmp actual expected
+
+'
+
+test_done
diff --git a/t/t4026-color.sh b/t/t4026-color.sh
new file mode 100755
index 0000000..d5ccdd0
--- /dev/null
+++ b/t/t4026-color.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Timo Hirvonen
+#
+
+test_description='Test diff/status color escape codes'
+. ./test-lib.sh
+
+color()
+{
+ actual=$(git config --get-color no.such.slot "$1") &&
+ test "$actual" = "$2"
+}
+
+invalid_color()
+{
+ test_must_fail git config --get-color no.such.slot "$1"
+}
+
+test_expect_success 'reset' '
+ color "reset" "[m"
+'
+
+test_expect_success 'attribute before color name' '
+ color "bold red" "[1;31m"
+'
+
+test_expect_success 'color name before attribute' '
+ color "red bold" "[1;31m"
+'
+
+test_expect_success 'attr fg bg' '
+ color "ul blue red" "[4;34;41m"
+'
+
+test_expect_success 'fg attr bg' '
+ color "blue ul red" "[4;34;41m"
+'
+
+test_expect_success 'fg bg attr' '
+ color "blue red ul" "[4;34;41m"
+'
+
+test_expect_success 'fg bg attr...' '
+ color "blue bold dim ul blink reverse" "[1;2;4;5;7;34m"
+'
+
+test_expect_success 'long color specification' '
+ color "254 255 bold dim ul blink reverse" "[1;2;4;5;7;38;5;254;48;5;255m"
+'
+
+test_expect_success '256 colors' '
+ color "254 bold 255" "[1;38;5;254;48;5;255m"
+'
+
+test_expect_success 'color too small' '
+ invalid_color "-2"
+'
+
+test_expect_success 'color too big' '
+ invalid_color "256"
+'
+
+test_expect_success 'extra character after color number' '
+ invalid_color "3X"
+'
+
+test_expect_success 'extra character after color name' '
+ invalid_color "redX"
+'
+
+test_expect_success 'extra character after attribute' '
+ invalid_color "dimX"
+'
+
+test_expect_success 'unknown color slots are ignored (diff)' '
+ git config --unset diff.color.new
+ git config color.diff.nosuchslotwilleverbedefined white &&
+ git diff --color
+'
+
+test_expect_success 'unknown color slots are ignored (branch)' '
+ git config color.branch.nosuchslotwilleverbedefined white &&
+ git branch -a
+'
+
+test_expect_success 'unknown color slots are ignored (status)' '
+ git config color.status.nosuchslotwilleverbedefined white || exit
+ git status
+ case $? in 0|1) : ok ;; *) false ;; esac
+'
+
+test_done
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
new file mode 100755
index 0000000..1bd8e5e
--- /dev/null
+++ b/t/t4027-diff-submodule.sh
@@ -0,0 +1,195 @@
+#!/bin/sh
+
+test_description='difference in submodules'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+_z40=0000000000000000000000000000000000000000
+test_expect_success setup '
+ test_tick &&
+ test_create_repo sub &&
+ (
+ cd sub &&
+ echo hello >world &&
+ git add world &&
+ git commit -m submodule
+ ) &&
+
+ test_tick &&
+ echo frotz >nitfol &&
+ git add nitfol sub &&
+ git commit -m superproject &&
+
+ (
+ cd sub &&
+ echo goodbye >world &&
+ git add world &&
+ git commit -m "submodule #2"
+ ) &&
+
+ set x $(
+ cd sub &&
+ git rev-list HEAD
+ ) &&
+ echo ":160000 160000 $3 $_z40 M sub" >expect &&
+ subtip=$3 subprev=$2
+'
+
+test_expect_success 'git diff --raw HEAD' '
+ git diff --raw --abbrev=40 HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git diff-index --raw HEAD' '
+ git diff-index --raw HEAD >actual.index &&
+ test_cmp expect actual.index
+'
+
+test_expect_success 'git diff-files --raw' '
+ git diff-files --raw >actual.files &&
+ test_cmp expect actual.files
+'
+
+expect_from_to () {
+ printf "%sSubproject commit %s\n+Subproject commit %s\n" \
+ "-" "$1" "$2"
+}
+
+test_expect_success 'git diff HEAD' '
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree)' '
+ echo >>sub/world &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev-dirty &&
+ test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (index)' '
+ (
+ cd sub &&
+ git reset --hard &&
+ echo >>world &&
+ git add world
+ ) &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev-dirty &&
+ test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked)' '
+ (
+ cd sub &&
+ git reset --hard &&
+ git clean -qfdx &&
+ >cruft
+ ) &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev-dirty &&
+ test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)' '
+ git commit -m "x" sub &&
+ echo >>sub/world &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git diff --ignore-submodules HEAD >actual2 &&
+ ! test -s actual2 &&
+ git diff --ignore-submodules=untracked HEAD >actual3 &&
+ sed -e "1,/^@@/d" actual3 >actual3.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual3.body &&
+ git diff --ignore-submodules=dirty HEAD >actual4 &&
+ ! test -s actual4
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (index, refs match)' '
+ (
+ cd sub &&
+ git reset --hard &&
+ echo >>world &&
+ git add world
+ ) &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match)' '
+ (
+ cd sub &&
+ git reset --hard &&
+ git clean -qfdx &&
+ >cruft
+ ) &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git diff --ignore-submodules=all HEAD >actual2 &&
+ ! test -s actual2 &&
+ git diff --ignore-submodules=untracked HEAD >actual3 &&
+ ! test -s actual3 &&
+ git diff --ignore-submodules=dirty HEAD >actual4 &&
+ ! test -s actual4
+'
+
+test_expect_success 'git diff (empty submodule dir)' '
+ : >empty &&
+ rm -rf sub/* sub/.git &&
+ git diff > actual.empty &&
+ test_cmp empty actual.empty
+'
+
+test_expect_success 'conflicted submodule setup' '
+
+ # 39 efs
+ c=fffffffffffffffffffffffffffffffffffffff
+ (
+ echo "000000 $_z40 0 sub"
+ echo "160000 1$c 1 sub"
+ echo "160000 2$c 2 sub"
+ echo "160000 3$c 3 sub"
+ ) | git update-index --index-info &&
+ echo >expect.nosub '\''diff --cc sub
+index 2ffffff,3ffffff..0000000
+--- a/sub
++++ b/sub
+@@@ -1,1 -1,1 +1,1 @@@
+- Subproject commit 2fffffffffffffffffffffffffffffffffffffff
+ -Subproject commit 3fffffffffffffffffffffffffffffffffffffff
+++Subproject commit 0000000000000000000000000000000000000000'\'' &&
+
+ hh=$(git rev-parse HEAD) &&
+ sed -e "s/$_z40/$hh/" expect.nosub >expect.withsub
+
+'
+
+test_expect_success 'combined (empty submodule)' '
+ rm -fr sub && mkdir sub &&
+ git diff >actual &&
+ test_cmp expect.nosub actual
+'
+
+test_expect_success 'combined (with submodule)' '
+ rm -fr sub &&
+ git clone --no-checkout . sub &&
+ git diff >actual &&
+ test_cmp expect.withsub actual
+'
+
+
+
+test_done
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
new file mode 100755
index 0000000..204ba67
--- /dev/null
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='format-patch mime headers and extra headers do not conflict'
+. ./test-lib.sh
+
+test_expect_success 'create commit with utf-8 body' '
+ echo content >file &&
+ git add file &&
+ git commit -m one &&
+ echo more >>file &&
+ git commit -a -m "two
+
+ utf-8 body: ñ"
+'
+
+test_expect_success 'patch has mime headers' '
+ rm -f 0001-two.patch &&
+ git format-patch HEAD^ &&
+ grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch
+'
+
+test_expect_success 'patch has mime and extra headers' '
+ rm -f 0001-two.patch &&
+ git config format.headers "x-foo: bar" &&
+ git format-patch HEAD^ &&
+ grep -i "x-foo: bar" 0001-two.patch &&
+ grep -i "content-type: text/plain; charset=utf-8" 0001-two.patch
+'
+
+test_done
diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh
new file mode 100755
index 0000000..3ccc237
--- /dev/null
+++ b/t/t4029-diff-trailing-space.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# Copyright (c) Jim Meyering
+#
+test_description='diff honors config option, diff.suppressBlankEmpty'
+
+. ./test-lib.sh
+
+cat <<\EOF > exp ||
+diff --git a/f b/f
+index 5f6a263..8cb8bae 100644
+--- a/f
++++ b/f
+@@ -1,2 +1,2 @@
+
+-x
++y
+EOF
+exit 1
+
+test_expect_success \
+ "$test_description" \
+ 'printf "\nx\n" > f &&
+ git add f &&
+ git commit -q -m. f &&
+ printf "\ny\n" > f &&
+ git config --bool diff.suppressBlankEmpty true &&
+ git diff f > actual &&
+ test_cmp exp actual &&
+ perl -i.bak -p -e "s/^\$/ /" exp &&
+ git config --bool diff.suppressBlankEmpty false &&
+ git diff f > actual &&
+ test_cmp exp actual &&
+ git config --bool --unset diff.suppressBlankEmpty &&
+ git diff f > actual &&
+ test_cmp exp actual
+ '
+
+test_done
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
new file mode 100755
index 0000000..88c5619
--- /dev/null
+++ b/t/t4030-diff-textconv.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='diff.*.textconv tests'
+. ./test-lib.sh
+
+find_diff() {
+ sed '1,/^index /d' | sed '/^-- $/,$d'
+}
+
+cat >expect.binary <<'EOF'
+Binary files a/file and b/file differ
+EOF
+
+cat >expect.text <<'EOF'
+--- a/file
++++ b/file
+@@ -1 +1,2 @@
+ 0
++1
+EOF
+
+cat >hexdump <<'EOF'
+#!/bin/sh
+perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
+EOF
+chmod +x hexdump
+
+test_expect_success 'setup binary file with history' '
+ printf "\\0\\n" >file &&
+ git add file &&
+ git commit -m one &&
+ printf "\\01\\n" >>file &&
+ git add file &&
+ git commit -m two
+'
+
+test_expect_success 'file is considered binary by porcelain' '
+ git diff HEAD^ HEAD >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.binary actual
+'
+
+test_expect_success 'file is considered binary by plumbing' '
+ git diff-tree -p HEAD^ HEAD >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.binary actual
+'
+
+test_expect_success 'setup textconv filters' '
+ echo file diff=foo >.gitattributes &&
+ git config diff.foo.textconv "\"$(pwd)\""/hexdump &&
+ git config diff.fail.textconv false
+'
+
+test_expect_success 'diff produces text' '
+ git diff HEAD^ HEAD >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.text actual
+'
+
+test_expect_success 'diff-tree produces binary' '
+ git diff-tree -p HEAD^ HEAD >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.binary actual
+'
+
+test_expect_success 'log produces text' '
+ git log -1 -p >log &&
+ find_diff <log >actual &&
+ test_cmp expect.text actual
+'
+
+test_expect_success 'format-patch produces binary' '
+ git format-patch --no-binary --stdout HEAD^ >patch &&
+ find_diff <patch >actual &&
+ test_cmp expect.binary actual
+'
+
+test_expect_success 'status -v produces text' '
+ git reset --soft HEAD^ &&
+ git status -v >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.text actual &&
+ git reset --soft HEAD@{1}
+'
+
+cat >expect.stat <<'EOF'
+ file | Bin 2 -> 4 bytes
+ 1 files changed, 0 insertions(+), 0 deletions(-)
+EOF
+test_expect_success 'diffstat does not run textconv' '
+ echo file diff=fail >.gitattributes &&
+ git diff --stat HEAD^ HEAD >actual &&
+ test_cmp expect.stat actual
+'
+# restore working setup
+echo file diff=foo >.gitattributes
+
+cat >expect.typechange <<'EOF'
+--- a/file
++++ /dev/null
+@@ -1,2 +0,0 @@
+-0
+-1
+diff --git a/file b/file
+new file mode 120000
+index 0000000..67be421
+--- /dev/null
++++ b/file
+@@ -0,0 +1 @@
++frotz
+\ No newline at end of file
+EOF
+# make a symlink the hard way that works on symlink-challenged file systems
+test_expect_success 'textconv does not act on symlinks' '
+ printf frotz > file &&
+ git add file &&
+ git ls-files -s | sed -e s/100644/120000/ |
+ git update-index --index-info &&
+ git commit -m typechange &&
+ git show >diff &&
+ find_diff <diff >actual &&
+ test_cmp expect.typechange actual
+'
+
+test_done
diff --git a/t/t4031-diff-rewrite-binary.sh b/t/t4031-diff-rewrite-binary.sh
new file mode 100755
index 0000000..7e7b307
--- /dev/null
+++ b/t/t4031-diff-rewrite-binary.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='rewrite diff on binary file'
+
+. ./test-lib.sh
+
+# We must be large enough to meet the MINIMUM_BREAK_SIZE
+# requirement.
+make_file() {
+ # common first line to help identify rewrite versus regular diff
+ printf "=\n" >file
+ for i in 1 2 3 4 5 6 7 8 9 10
+ do
+ for j in 1 2 3 4 5 6 7 8 9
+ do
+ for k in 1 2 3 4 5
+ do
+ printf "$1\n"
+ done
+ done
+ done >>file
+}
+
+test_expect_success 'create binary file with changes' '
+ make_file "\\0" &&
+ git add file &&
+ make_file "\\01"
+'
+
+test_expect_success 'vanilla diff is binary' '
+ git diff >diff &&
+ grep "Binary files a/file and b/file differ" diff
+'
+
+test_expect_success 'rewrite diff is binary' '
+ git diff -B >diff &&
+ grep "dissimilarity index" diff &&
+ grep "Binary files a/file and b/file differ" diff
+'
+
+test_expect_success 'rewrite diff can show binary patch' '
+ git diff -B --binary >diff &&
+ grep "dissimilarity index" diff &&
+ grep "GIT binary patch" diff
+'
+
+{
+ echo "#!$SHELL_PATH"
+ cat <<'EOF'
+perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' < "$1"
+EOF
+} >dump
+chmod +x dump
+
+test_expect_success 'setup textconv' '
+ echo file diff=foo >.gitattributes &&
+ git config diff.foo.textconv "\"$(pwd)\""/dump
+'
+
+test_expect_success 'rewrite diff respects textconv' '
+ git diff -B >diff &&
+ grep "dissimilarity index" diff &&
+ grep "^-61" diff &&
+ grep "^-0" diff
+'
+
+test_done
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
new file mode 100755
index 0000000..e4e3e28
--- /dev/null
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='diff hunk fusing'
+
+. ./test-lib.sh
+
+f() {
+ echo $1
+ i=1
+ while test $i -le $2
+ do
+ echo $i
+ i=$(expr $i + 1)
+ done
+ echo $3
+}
+
+t() {
+ case $# in
+ 4) hunks=$4; cmd="diff -U$3";;
+ 5) hunks=$5; cmd="diff -U$3 --inter-hunk-context=$4";;
+ esac
+ label="$cmd, $1 common $2"
+ file=f$1
+ expected=expected.$file.$3.$hunks
+
+ if ! test -f $file
+ then
+ f A $1 B >$file
+ git add $file
+ git commit -q -m. $file
+ f X $1 Y >$file
+ fi
+
+ test_expect_success "$label: count hunks ($hunks)" "
+ test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
+ "
+
+ test -f $expected &&
+ test_expect_success "$label: check output" "
+ git $cmd $file | grep -v '^index ' >actual &&
+ test_cmp $expected actual
+ "
+}
+
+cat <<EOF >expected.f1.0.1 || exit 1
+diff --git a/f1 b/f1
+--- a/f1
++++ b/f1
+@@ -1,3 +1,3 @@
+-A
++X
+ 1
+-B
++Y
+EOF
+
+cat <<EOF >expected.f1.0.2 || exit 1
+diff --git a/f1 b/f1
+--- a/f1
++++ b/f1
+@@ -1 +1 @@
+-A
++X
+@@ -3 +3 @@ A
+-B
++Y
+EOF
+
+# common lines ctx intrctx hunks
+t 1 line 0 2
+t 1 line 0 0 2
+t 1 line 0 1 1
+t 1 line 0 2 1
+t 1 line 1 1
+
+t 2 lines 0 2
+t 2 lines 0 0 2
+t 2 lines 0 1 2
+t 2 lines 0 2 1
+t 2 lines 1 1
+
+t 3 lines 1 2
+t 3 lines 1 0 2
+t 3 lines 1 1 1
+t 3 lines 1 2 1
+
+t 9 lines 3 2
+t 9 lines 3 2 2
+t 9 lines 3 3 1
+
+test_done
diff --git a/t/t4033-diff-patience.sh b/t/t4033-diff-patience.sh
new file mode 100755
index 0000000..1eb1498
--- /dev/null
+++ b/t/t4033-diff-patience.sh
@@ -0,0 +1,168 @@
+#!/bin/sh
+
+test_description='patience diff algorithm'
+
+. ./test-lib.sh
+
+cat >file1 <<\EOF
+#include <stdio.h>
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+ int i;
+ for(i = 0; i < 10; i++)
+ {
+ printf("Your answer is: ");
+ printf("%d\n", foo);
+ }
+}
+
+int fact(int n)
+{
+ if(n > 1)
+ {
+ return fact(n-1) * n;
+ }
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ frobnitz(fact(10));
+}
+EOF
+
+cat >file2 <<\EOF
+#include <stdio.h>
+
+int fib(int n)
+{
+ if(n > 2)
+ {
+ return fib(n-1) + fib(n-2);
+ }
+ return 1;
+}
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+ int i;
+ for(i = 0; i < 10; i++)
+ {
+ printf("%d\n", foo);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ frobnitz(fib(10));
+}
+EOF
+
+cat >expect <<\EOF
+diff --git a/file1 b/file2
+index 6faa5a3..e3af329 100644
+--- a/file1
++++ b/file2
+@@ -1,26 +1,25 @@
+ #include <stdio.h>
+
++int fib(int n)
++{
++ if(n > 2)
++ {
++ return fib(n-1) + fib(n-2);
++ }
++ return 1;
++}
++
+ // Frobs foo heartily
+ int frobnitz(int foo)
+ {
+ int i;
+ for(i = 0; i < 10; i++)
+ {
+- printf("Your answer is: ");
+ printf("%d\n", foo);
+ }
+ }
+
+-int fact(int n)
+-{
+- if(n > 1)
+- {
+- return fact(n-1) * n;
+- }
+- return 1;
+-}
+-
+ int main(int argc, char **argv)
+ {
+- frobnitz(fact(10));
++ frobnitz(fib(10));
+ }
+EOF
+
+test_expect_success 'patience diff' '
+
+ test_must_fail git diff --no-index --patience file1 file2 > output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'patience diff output is valid' '
+
+ mv file2 expect &&
+ git apply < output &&
+ test_cmp expect file2
+
+'
+
+cat >uniq1 <<\EOF
+1
+2
+3
+4
+5
+6
+EOF
+
+cat >uniq2 <<\EOF
+a
+b
+c
+d
+e
+f
+EOF
+
+cat >expect <<\EOF
+diff --git a/uniq1 b/uniq2
+index b414108..0fdf397 100644
+--- a/uniq1
++++ b/uniq2
+@@ -1,6 +1,6 @@
+-1
+-2
+-3
+-4
+-5
+-6
++a
++b
++c
++d
++e
++f
+EOF
+
+test_expect_success 'completely different files' '
+
+ test_must_fail git diff --no-index --patience uniq1 uniq2 > output &&
+ test_cmp expect output
+
+'
+
+test_done
diff --git a/t/t4034-diff-words.sh b/t/t4034-diff-words.sh
new file mode 100755
index 0000000..6f7548c
--- /dev/null
+++ b/t/t4034-diff-words.sh
@@ -0,0 +1,334 @@
+#!/bin/sh
+
+test_description='word diff colors'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ git config diff.color.old red
+ git config diff.color.new green
+ git config diff.color.func magenta
+
+'
+
+word_diff () {
+ test_must_fail git diff --no-index "$@" pre post > output &&
+ test_decode_color <output >output.decrypted &&
+ test_cmp expect output.decrypted
+}
+
+cat > pre <<\EOF
+h(4)
+
+a = b + c
+EOF
+
+cat > post <<\EOF
+h(4),hh[44]
+
+a = b + c
+
+aa = a
+
+aeff = aeff * ( aaa )
+EOF
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
+<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+
+test_expect_success 'word diff with runs of whitespace' '
+
+ word_diff --color-words
+
+'
+
+test_expect_success '--word-diff=color' '
+
+ word_diff --word-diff=color
+
+'
+
+test_expect_success '--color --word-diff=color' '
+
+ word_diff --color --word-diff=color
+
+'
+
+sed 's/#.*$//' > expect <<EOF
+diff --git a/pre b/post
+index 330b04f..5ed8eff 100644
+--- a/pre
++++ b/post
+@@ -1,3 +1,7 @@
+-h(4)
++h(4),hh[44]
+~
+ # significant space
+~
+ a = b + c
+~
+~
++aa = a
+~
+~
++aeff = aeff * ( aaa )
+~
+EOF
+
+test_expect_success '--word-diff=porcelain' '
+
+ word_diff --word-diff=porcelain
+
+'
+
+cat > expect <<EOF
+diff --git a/pre b/post
+index 330b04f..5ed8eff 100644
+--- a/pre
++++ b/post
+@@ -1,3 +1,7 @@
+[-h(4)-]{+h(4),hh[44]+}
+
+a = b + c
+
+{+aa = a+}
+
+{+aeff = aeff * ( aaa )+}
+EOF
+
+test_expect_success '--word-diff=plain' '
+
+ word_diff --word-diff=plain
+
+'
+
+test_expect_success '--word-diff=plain --no-color' '
+
+ word_diff --word-diff=plain --no-color
+
+'
+
+cat > expect <<EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
+<RED>[-h(4)-]<RESET><GREEN>{+h(4),hh[44]+}<RESET>
+
+a = b + c<RESET>
+
+<GREEN>{+aa = a+}<RESET>
+
+<GREEN>{+aeff = aeff * ( aaa )+}<RESET>
+EOF
+
+test_expect_success '--word-diff=plain --color' '
+
+ word_diff --word-diff=plain --color
+
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<CYAN>@@ -1 +1 @@<RESET>
+<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+<CYAN>@@ -3,0 +4,4 @@<RESET> <RESET><MAGENTA>a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+
+test_expect_success 'word diff without context' '
+
+ word_diff --color-words --unified=0
+
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
+h(4),<GREEN>hh<RESET>[44]
+
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa<RESET> )
+EOF
+cp expect expect.letter-runs-are-words
+
+test_expect_success 'word diff with a regular expression' '
+
+ word_diff --color-words="[a-z]+"
+
+'
+
+test_expect_success 'set a diff driver' '
+ git config diff.testdriver.wordRegex "[^[:space:]]" &&
+ cat <<EOF > .gitattributes
+pre diff=testdriver
+post diff=testdriver
+EOF
+'
+
+test_expect_success 'option overrides .gitattributes' '
+
+ word_diff --color-words="[a-z]+"
+
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
+h(4)<GREEN>,hh[44]<RESET>
+
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+cp expect expect.non-whitespace-is-word
+
+test_expect_success 'use regex supplied by driver' '
+
+ word_diff --color-words
+
+'
+
+test_expect_success 'set diff.wordRegex option' '
+ git config diff.wordRegex "[[:alnum:]]+"
+'
+
+cp expect.letter-runs-are-words expect
+
+test_expect_success 'command-line overrides config' '
+ word_diff --color-words="[a-z]+"
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
+h(4),<GREEN>{+hh+}<RESET>[44]
+
+a = b + c<RESET>
+
+<GREEN>{+aa = a+}<RESET>
+
+<GREEN>{+aeff = aeff * ( aaa+}<RESET> )
+EOF
+
+test_expect_success 'command-line overrides config: --word-diff-regex' '
+ word_diff --color --word-diff-regex="[a-z]+"
+'
+
+cp expect.non-whitespace-is-word expect
+
+test_expect_success '.gitattributes override config' '
+ word_diff --color-words
+'
+
+test_expect_success 'remove diff driver regex' '
+ git config --unset diff.testdriver.wordRegex
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<CYAN>@@ -1,3 +1,7 @@<RESET>
+h(4),<GREEN>hh[44<RESET>]
+
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa<RESET> )
+EOF
+
+test_expect_success 'use configured regex' '
+ word_diff --color-words
+'
+
+echo 'aaa (aaa)' > pre
+echo 'aaa (aaa) aaa' > post
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index c29453b..be22f37 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<CYAN>@@ -1 +1 @@<RESET>
+aaa (aaa) <GREEN>aaa<RESET>
+EOF
+
+test_expect_success 'test parsing words for newline' '
+
+ word_diff --color-words="a+"
+
+
+'
+
+echo '(:' > pre
+echo '(' > post
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 289cb9d..2d06f37 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<CYAN>@@ -1 +1 @@<RESET>
+(<RED>:<RESET>
+EOF
+
+test_expect_success 'test when words are only removed at the end' '
+
+ word_diff --color-words=.
+
+'
+
+cat > expect <<\EOF
+diff --git a/pre b/post
+index 289cb9d..2d06f37 100644
+--- a/pre
++++ b/post
+@@ -1 +1 @@
+-(:
++(
+EOF
+
+test_expect_success '--word-diff=none' '
+
+ word_diff --word-diff=plain --word-diff=none
+
+'
+
+test_done
diff --git a/t/t4035-diff-quiet.sh b/t/t4035-diff-quiet.sh
new file mode 100755
index 0000000..e747e84
--- /dev/null
+++ b/t/t4035-diff-quiet.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='Return value of diffs'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo 1 >a &&
+ git add . &&
+ git commit -m first &&
+ echo 2 >b &&
+ git add . &&
+ git commit -a -m second
+'
+
+test_expect_success 'git diff-tree HEAD^ HEAD' '
+ git diff-tree --quiet HEAD^ HEAD >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- a' '
+ git diff-tree --quiet HEAD^ HEAD -- a >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-tree HEAD^ HEAD -- b' '
+ git diff-tree --quiet HEAD^ HEAD -- b >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+'
+# this diff outputs one line: sha1 of the given head
+test_expect_success 'echo HEAD | git diff-tree --stdin' '
+ echo $(git rev-parse HEAD) | git diff-tree --quiet --stdin >cnt
+ test $? = 1 && test $(wc -l <cnt) = 1
+'
+test_expect_success 'git diff-tree HEAD HEAD' '
+ git diff-tree --quiet HEAD HEAD >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+ git diff-files --quiet >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD' '
+ git diff-index --quiet --cached HEAD >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+ git diff-index --quiet --cached HEAD^ >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-index --cached HEAD^' '
+ echo text >>b &&
+ echo 3 >c &&
+ git add . && {
+ git diff-index --quiet --cached HEAD^ >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+ }
+'
+test_expect_success 'git diff-tree -Stext HEAD^ HEAD -- b' '
+ git commit -m "text in b" && {
+ git diff-tree --quiet -Stext HEAD^ HEAD -- b >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+ }
+'
+test_expect_success 'git diff-tree -Snot-found HEAD^ HEAD -- b' '
+ git diff-tree --quiet -Snot-found HEAD^ HEAD -- b >cnt
+ test $? = 0 && test $(wc -l <cnt) = 0
+'
+test_expect_success 'git diff-files' '
+ echo 3 >>c && {
+ git diff-files --quiet >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+ }
+'
+test_expect_success 'git diff-index --cached HEAD' '
+ git update-index c && {
+ git diff-index --quiet --cached HEAD >cnt
+ test $? = 1 && test $(wc -l <cnt) = 0
+ }
+'
+
+test_done
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
new file mode 100755
index 0000000..ba43f18
--- /dev/null
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='format-patch -s should force MIME encoding as needed'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >F &&
+ git add F &&
+ git commit -m initial &&
+ echo new line >F &&
+
+ test_tick &&
+ git commit -m "This adds some lines to F" F
+
+'
+
+test_expect_success 'format normally' '
+
+ git format-patch --stdout -1 >output &&
+ ! grep Content-Type output
+
+'
+
+test_expect_success 'format with signoff without funny signer name' '
+
+ git format-patch -s --stdout -1 >output &&
+ ! grep Content-Type output
+
+'
+
+test_expect_success 'format with non ASCII signer name' '
+
+ GIT_COMMITTER_NAME="ã¯ã¾ã® ãµã«ãŠã†" \
+ git format-patch -s --stdout -1 >output &&
+ grep Content-Type output
+
+'
+
+test_expect_success 'attach and signoff do not duplicate mime headers' '
+
+ GIT_COMMITTER_NAME="ã¯ã¾ã® ãµã«ãŠã†" \
+ git format-patch -s --stdout -1 --attach >output &&
+ test `grep -ci ^MIME-Version: output` = 1
+
+'
+
+test_done
+
diff --git a/t/t4037-diff-r-t-dirs.sh b/t/t4037-diff-r-t-dirs.sh
new file mode 100755
index 0000000..f5ce3b2
--- /dev/null
+++ b/t/t4037-diff-r-t-dirs.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description='diff -r -t shows directory additions and deletions'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir dc dr dt &&
+ >dc/1 &&
+ >dr/2 &&
+ >dt/3 &&
+ >fc &&
+ >fr &&
+ >ft &&
+ git add . &&
+ test_tick &&
+ git commit -m initial &&
+
+ rm -fr dt dr ft fr &&
+ mkdir da ft &&
+ for p in dc/1 da/4 dt ft/5 fc
+ do
+ echo hello >$p || exit
+ done &&
+ git add -u &&
+ git add . &&
+ test_tick &&
+ git commit -m second
+'
+
+cat >expect <<\EOF
+A da
+A da/4
+M dc
+M dc/1
+D dr
+D dr/2
+A dt
+D dt
+D dt/3
+M fc
+D fr
+D ft
+A ft
+A ft/5
+EOF
+
+test_expect_success verify '
+ git diff-tree -r -t --name-status HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
new file mode 100755
index 0000000..40277c7
--- /dev/null
+++ b/t/t4038-diff-combined.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='combined diff'
+
+. ./test-lib.sh
+
+setup_helper () {
+ one=$1 branch=$2 side=$3 &&
+
+ git branch $side $branch &&
+ for l in $one two three fyra
+ do
+ echo $l
+ done >file &&
+ git add file &&
+ test_tick &&
+ git commit -m $branch &&
+ git checkout $side &&
+ for l in $one two three quatro
+ do
+ echo $l
+ done >file &&
+ git add file &&
+ test_tick &&
+ git commit -m $side &&
+ test_must_fail git merge $branch &&
+ for l in $one three four
+ do
+ echo $l
+ done >file &&
+ git add file &&
+ test_tick &&
+ git commit -m "merge $branch into $side"
+}
+
+verify_helper () {
+ it=$1 &&
+
+ # Ignore lines that were removed only from the other parent
+ sed -e '
+ 1,/^@@@/d
+ /^ -/d
+ s/^\(.\)./\1/
+ ' "$it" >"$it.actual.1" &&
+ sed -e '
+ 1,/^@@@/d
+ /^- /d
+ s/^.\(.\)/\1/
+ ' "$it" >"$it.actual.2" &&
+
+ git diff "$it^" "$it" -- | sed -e '1,/^@@/d' >"$it.expect.1" &&
+ test_cmp "$it.expect.1" "$it.actual.1" &&
+
+ git diff "$it^2" "$it" -- | sed -e '1,/^@@/d' >"$it.expect.2" &&
+ test_cmp "$it.expect.2" "$it.actual.2"
+}
+
+test_expect_success setup '
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+
+ git branch withone &&
+ git branch sansone &&
+
+ git checkout withone &&
+ setup_helper one withone sidewithone &&
+
+ git checkout sansone &&
+ setup_helper "" sansone sidesansone
+'
+
+test_expect_success 'check combined output (1)' '
+ git show sidewithone -- >sidewithone &&
+ verify_helper sidewithone
+'
+
+test_expect_success 'check combined output (2)' '
+ git show sidesansone -- >sidesansone &&
+ verify_helper sidesansone
+'
+
+test_expect_success 'diagnose truncated file' '
+ >file &&
+ git add file &&
+ git commit --amend -C HEAD &&
+ git show >out &&
+ grep "diff --cc file" out
+'
+
+test_done
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
new file mode 100755
index 0000000..9d9498b
--- /dev/null
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='diff with assume-unchanged entries'
+
+. ./test-lib.sh
+
+# external diff has been tested in t4020-diff-external.sh
+
+test_expect_success 'setup' '
+ echo zero > zero &&
+ git add zero &&
+ git commit -m zero &&
+ echo one > one &&
+ echo two > two &&
+ git add one two &&
+ git commit -m onetwo &&
+ git update-index --assume-unchanged one &&
+ echo borked >> one &&
+ test "$(git ls-files -v one)" = "h one"
+'
+
+test_expect_success 'diff-index does not examine assume-unchanged entries' '
+ git diff-index HEAD^ -- one | grep -q 5626abf0f72e58d7a153368ba57db4c673c0e171
+'
+
+test_expect_success 'diff-files does not examine assume-unchanged entries' '
+ rm one &&
+ test -z "$(git diff-files -- one)"
+'
+
+test_done
diff --git a/t/t4040-whitespace-status.sh b/t/t4040-whitespace-status.sh
new file mode 100755
index 0000000..a30b03b
--- /dev/null
+++ b/t/t4040-whitespace-status.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+test_description='diff --exit-code with whitespace'
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir a b &&
+ echo >c &&
+ echo >a/d &&
+ echo >b/e &&
+ git add . &&
+ test_tick &&
+ git commit -m initial &&
+ echo " " >a/d &&
+ test_tick &&
+ git commit -a -m second &&
+ echo " " >a/d &&
+ echo " " >b/e &&
+ git add a/d
+'
+
+test_expect_success 'diff-tree --exit-code' '
+ test_must_fail git diff --exit-code HEAD^ HEAD &&
+ test_must_fail git diff-tree --exit-code HEAD^ HEAD
+'
+
+test_expect_success 'diff-tree -b --exit-code' '
+ git diff -b --exit-code HEAD^ HEAD &&
+ git diff-tree -b -p --exit-code HEAD^ HEAD &&
+ git diff-tree -b --exit-code HEAD^ HEAD
+'
+
+test_expect_success 'diff-index --cached --exit-code' '
+ test_must_fail git diff --cached --exit-code HEAD &&
+ test_must_fail git diff-index --cached --exit-code HEAD
+'
+
+test_expect_success 'diff-index -b -p --cached --exit-code' '
+ git diff -b --cached --exit-code HEAD &&
+ git diff-index -b -p --cached --exit-code HEAD
+'
+
+test_expect_success 'diff-index --exit-code' '
+ test_must_fail git diff --exit-code HEAD &&
+ test_must_fail git diff-index --exit-code HEAD
+'
+
+test_expect_success 'diff-index -b -p --exit-code' '
+ git diff -b --exit-code HEAD &&
+ git diff-index -b -p --exit-code HEAD
+'
+
+test_expect_success 'diff-files --exit-code' '
+ test_must_fail git diff --exit-code &&
+ test_must_fail git diff-files --exit-code
+'
+
+test_expect_success 'diff-files -b -p --exit-code' '
+ git diff -b --exit-code &&
+ git diff-files -b -p --exit-code
+'
+
+test_done
diff --git a/t/t4041-diff-submodule-option.sh b/t/t4041-diff-submodule-option.sh
new file mode 100755
index 0000000..8e391cf
--- /dev/null
+++ b/t/t4041-diff-submodule-option.sh
@@ -0,0 +1,428 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin
+#
+
+test_description='Support for verbose submodule differences in git diff
+
+This test tries to verify the sanity of the --submodule option of git diff.
+'
+
+. ./test-lib.sh
+
+add_file () {
+ sm=$1
+ shift
+ owd=$(pwd)
+ cd "$sm"
+ for name; do
+ echo "$name" > "$name" &&
+ git add "$name" &&
+ test_tick &&
+ git commit -m "Add $name"
+ done >/dev/null
+ git rev-parse --verify HEAD | cut -c1-7
+ cd "$owd"
+}
+commit_file () {
+ test_tick &&
+ git commit "$@" -m "Commit $*" >/dev/null
+}
+
+test_create_repo sm1 &&
+add_file . foo >/dev/null
+
+head1=$(add_file sm1 foo1 foo2)
+
+test_expect_success 'added submodule' "
+ git add sm1 &&
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 0000000...$head1 (new submodule)
+EOF
+"
+
+commit_file sm1 &&
+head2=$(add_file sm1 foo3)
+
+test_expect_success 'modified submodule(forward)' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head1..$head2:
+ > Add foo3
+EOF
+"
+
+test_expect_success 'modified submodule(forward)' "
+ git diff --submodule=log >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head1..$head2:
+ > Add foo3
+EOF
+"
+
+test_expect_success 'modified submodule(forward) --submodule' "
+ git diff --submodule >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head1..$head2:
+ > Add foo3
+EOF
+"
+
+fullhead1=$(cd sm1; git rev-list --max-count=1 $head1)
+fullhead2=$(cd sm1; git rev-list --max-count=1 $head2)
+test_expect_success 'modified submodule(forward) --submodule=short' "
+ git diff --submodule=short >actual &&
+ diff actual - <<-EOF
+diff --git a/sm1 b/sm1
+index $head1..$head2 160000
+--- a/sm1
++++ b/sm1
+@@ -1 +1 @@
+-Subproject commit $fullhead1
++Subproject commit $fullhead2
+EOF
+"
+
+commit_file sm1 &&
+cd sm1 &&
+git reset --hard HEAD~2 >/dev/null &&
+head3=$(git rev-parse --verify HEAD | cut -c1-7) &&
+cd ..
+
+test_expect_success 'modified submodule(backward)' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head2..$head3 (rewind):
+ < Add foo3
+ < Add foo2
+EOF
+"
+
+head4=$(add_file sm1 foo4 foo5) &&
+head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
+test_expect_success 'modified submodule(backward and forward)' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head2...$head4:
+ > Add foo5
+ > Add foo4
+ < Add foo3
+ < Add foo2
+EOF
+"
+
+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=log --cached >actual &&
+ diff actual - <<-EOF
+Submodule sm1 41fbea9...0000000 (submodule deleted)
+diff --git a/sm1 b/sm1
+new file mode 100644
+index 0000000..9da5fb8
+--- /dev/null
++++ b/sm1
+@@ -0,0 +1 @@
++sm1
+EOF
+"
+
+test_expect_success 'typechanged submodule(submodule->blob)' "
+ git diff --submodule=log >actual &&
+ diff actual - <<-EOF
+diff --git a/sm1 b/sm1
+deleted file mode 100644
+index 9da5fb8..0000000
+--- a/sm1
++++ /dev/null
+@@ -1 +0,0 @@
+-sm1
+Submodule sm1 0000000...$head4 (new submodule)
+EOF
+"
+
+rm -rf sm1 &&
+git checkout-index sm1
+test_expect_success 'typechanged submodule(submodule->blob)' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head4...0000000 (submodule deleted)
+diff --git a/sm1 b/sm1
+new file mode 100644
+index 0000000..$head5
+--- /dev/null
++++ b/sm1
+@@ -0,0 +1 @@
++sm1
+EOF
+"
+
+rm -f sm1 &&
+test_create_repo sm1 &&
+head6=$(add_file sm1 foo6 foo7)
+fullhead6=$(cd sm1; git rev-list --max-count=1 $head6)
+test_expect_success 'nonexistent commit' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head4...$head6 (commits not present)
+EOF
+"
+
+commit_file
+test_expect_success 'typechanged submodule(blob->submodule)' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+diff --git a/sm1 b/sm1
+deleted file mode 100644
+index $head5..0000000
+--- a/sm1
++++ /dev/null
+@@ -1 +0,0 @@
+-sm1
+Submodule sm1 0000000...$head6 (new submodule)
+EOF
+"
+
+commit_file sm1 &&
+test_expect_success 'submodule is up to date' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+EOF
+"
+
+test_expect_success 'submodule contains untracked content' "
+ echo new > sm1/new-file &&
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 contains untracked content
+EOF
+"
+
+test_expect_success 'submodule contains untracked content (untracked ignored)' "
+ git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+ ! test -s actual
+"
+
+test_expect_success 'submodule contains untracked content (dirty ignored)' "
+ git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
+ ! test -s actual
+"
+
+test_expect_success 'submodule contains untracked content (all ignored)' "
+ git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual &&
+ ! test -s actual
+"
+
+test_expect_success 'submodule contains untracked and modifed content' "
+ echo new > sm1/foo6 &&
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 contains untracked content
+Submodule sm1 contains modified content
+EOF
+"
+
+test_expect_success 'submodule contains untracked and modifed content (untracked ignored)' "
+ echo new > sm1/foo6 &&
+ git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 contains modified content
+EOF
+"
+
+test_expect_success 'submodule contains untracked and modifed content (dirty ignored)' "
+ echo new > sm1/foo6 &&
+ git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
+ ! test -s actual
+"
+
+test_expect_success 'submodule contains untracked and modifed content (all ignored)' "
+ echo new > sm1/foo6 &&
+ git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
+ ! test -s actual
+"
+
+test_expect_success 'submodule contains modifed content' "
+ rm -f sm1/new-file &&
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 contains modified content
+EOF
+"
+
+(cd sm1; git commit -mchange foo6 >/dev/null) &&
+head8=$(cd sm1; git rev-parse --verify HEAD | cut -c1-7) &&
+test_expect_success 'submodule is modified' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6..$head8:
+ > change
+EOF
+"
+
+test_expect_success 'modified submodule contains untracked content' "
+ echo new > sm1/new-file &&
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 contains untracked content
+Submodule sm1 $head6..$head8:
+ > change
+EOF
+"
+
+test_expect_success 'modified submodule contains untracked content (untracked ignored)' "
+ git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6..$head8:
+ > change
+EOF
+"
+
+test_expect_success 'modified submodule contains untracked content (dirty ignored)' "
+ git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6..$head8:
+ > change
+EOF
+"
+
+test_expect_success 'modified submodule contains untracked content (all ignored)' "
+ git diff-index -p --ignore-submodules=all --submodule=log HEAD >actual &&
+ ! test -s actual
+"
+
+test_expect_success 'modified submodule contains untracked and modifed content' "
+ echo modification >> sm1/foo6 &&
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 contains untracked content
+Submodule sm1 contains modified content
+Submodule sm1 $head6..$head8:
+ > change
+EOF
+"
+
+test_expect_success 'modified submodule contains untracked and modifed content (untracked ignored)' "
+ echo modification >> sm1/foo6 &&
+ git diff-index -p --ignore-submodules=untracked --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 contains modified content
+Submodule sm1 $head6..$head8:
+ > change
+EOF
+"
+
+test_expect_success 'modified submodule contains untracked and modifed content (dirty ignored)' "
+ echo modification >> sm1/foo6 &&
+ git diff-index -p --ignore-submodules=dirty --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6..$head8:
+ > change
+EOF
+"
+
+test_expect_success 'modified submodule contains untracked and modifed content (all ignored)' "
+ echo modification >> sm1/foo6 &&
+ git diff-index -p --ignore-submodules --submodule=log HEAD >actual &&
+ ! test -s actual
+"
+
+test_expect_success 'modified submodule contains modifed content' "
+ rm -f sm1/new-file &&
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 contains modified content
+Submodule sm1 $head6..$head8:
+ > change
+EOF
+"
+
+rm -rf sm1
+test_expect_success 'deleted submodule' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6...0000000 (submodule deleted)
+EOF
+"
+
+test_create_repo sm2 &&
+head7=$(add_file sm2 foo8 foo9) &&
+git add sm2
+
+test_expect_success 'multiple submodules' "
+ git diff-index -p --submodule=log HEAD >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6...0000000 (submodule deleted)
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+"
+
+test_expect_success 'path filter' "
+ git diff-index -p --submodule=log HEAD sm2 >actual &&
+ diff actual - <<-EOF
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+"
+
+commit_file sm2
+test_expect_success 'given commit' "
+ git diff-index -p --submodule=log HEAD^ >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6...0000000 (submodule deleted)
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+"
+
+test_expect_success 'given commit --submodule' "
+ git diff-index -p --submodule HEAD^ >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6...0000000 (submodule deleted)
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+"
+
+fullhead7=$(cd sm2; git rev-list --max-count=1 $head7)
+
+test_expect_success 'given commit --submodule=short' "
+ git diff-index -p --submodule=short HEAD^ >actual &&
+ diff actual - <<-EOF
+diff --git a/sm1 b/sm1
+deleted file mode 160000
+index $head6..0000000
+--- a/sm1
++++ /dev/null
+@@ -1 +0,0 @@
+-Subproject commit $fullhead6
+diff --git a/sm2 b/sm2
+new file mode 160000
+index 0000000..$head7
+--- /dev/null
++++ b/sm2
+@@ -0,0 +1 @@
++Subproject commit $fullhead7
+EOF
+"
+
+test_expect_success 'setup .git file for sm2' '
+ (cd sm2 &&
+ REAL="$(pwd)/../.real" &&
+ mv .git "$REAL"
+ echo "gitdir: $REAL" >.git)
+'
+
+test_expect_success 'diff --submodule with .git file' '
+ git diff --submodule HEAD^ >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6...0000000 (submodule deleted)
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+'
+
+test_done
diff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh
new file mode 100755
index 0000000..91f8198
--- /dev/null
+++ b/t/t4042-diff-textconv-caching.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+test_description='test textconv caching'
+. ./test-lib.sh
+
+cat >helper <<'EOF'
+#!/bin/sh
+sed 's/^/converted: /' "$@" >helper.out
+cat helper.out
+EOF
+chmod +x helper
+
+test_expect_success 'setup' '
+ echo foo content 1 >foo.bin &&
+ echo bar content 1 >bar.bin &&
+ git add . &&
+ git commit -m one &&
+ echo foo content 2 >foo.bin &&
+ echo bar content 2 >bar.bin &&
+ git commit -a -m two &&
+ echo "*.bin diff=magic" >.gitattributes &&
+ git config diff.magic.textconv ./helper &&
+ git config diff.magic.cachetextconv true
+'
+
+cat >expect <<EOF
+diff --git a/bar.bin b/bar.bin
+index fcf9166..28283d5 100644
+--- a/bar.bin
++++ b/bar.bin
+@@ -1 +1 @@
+-converted: bar content 1
++converted: bar content 2
+diff --git a/foo.bin b/foo.bin
+index d5b9fe3..1345db2 100644
+--- a/foo.bin
++++ b/foo.bin
+@@ -1 +1 @@
+-converted: foo content 1
++converted: foo content 2
+EOF
+
+test_expect_success 'first textconv works' '
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cached textconv produces same output' '
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cached textconv does not run helper' '
+ rm -f helper.out &&
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual &&
+ ! test -r helper.out
+'
+
+cat >expect <<EOF
+diff --git a/bar.bin b/bar.bin
+index fcf9166..28283d5 100644
+--- a/bar.bin
++++ b/bar.bin
+@@ -1,2 +1,2 @@
+ converted: other
+-converted: bar content 1
++converted: bar content 2
+diff --git a/foo.bin b/foo.bin
+index d5b9fe3..1345db2 100644
+--- a/foo.bin
++++ b/foo.bin
+@@ -1,2 +1,2 @@
+ converted: other
+-converted: foo content 1
++converted: foo content 2
+EOF
+test_expect_success 'changing textconv invalidates cache' '
+ echo other >other &&
+ git config diff.magic.textconv "./helper other" &&
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+diff --git a/bar.bin b/bar.bin
+index fcf9166..28283d5 100644
+--- a/bar.bin
++++ b/bar.bin
+@@ -1,2 +1,2 @@
+ converted: other
+-converted: bar content 1
++converted: bar content 2
+diff --git a/foo.bin b/foo.bin
+index d5b9fe3..1345db2 100644
+--- a/foo.bin
++++ b/foo.bin
+@@ -1 +1 @@
+-converted: foo content 1
++converted: foo content 2
+EOF
+test_expect_success 'switching diff driver produces correct results' '
+ git config diff.moremagic.textconv ./helper &&
+ echo foo.bin diff=moremagic >>.gitattributes &&
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4043-diff-rename-binary.sh b/t/t4043-diff-rename-binary.sh
new file mode 100755
index 0000000..0601281
--- /dev/null
+++ b/t/t4043-diff-rename-binary.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Jakub Narebski, Christian Couder
+#
+
+test_description='Move a binary file'
+
+. ./test-lib.sh
+
+
+test_expect_success 'prepare repository' '
+ git init &&
+ echo foo > foo &&
+ echo "barQ" | q_to_nul > bar &&
+ git add . &&
+ git commit -m "Initial commit"
+'
+
+test_expect_success 'move the files into a "sub" directory' '
+ mkdir sub &&
+ git mv bar foo sub/ &&
+ git commit -m "Moved to sub/"
+'
+
+cat > expected <<\EOF
+ bar => sub/bar | Bin 5 -> 5 bytes
+ foo => sub/foo | 0
+ 2 files changed, 0 insertions(+), 0 deletions(-)
+
+diff --git a/bar b/sub/bar
+similarity index 100%
+rename from bar
+rename to sub/bar
+diff --git a/foo b/sub/foo
+similarity index 100%
+rename from foo
+rename to sub/foo
+EOF
+
+test_expect_success 'git show -C -C report renames' '
+ git show -C -C --raw --binary --stat | tail -n 12 > current &&
+ test_cmp expected current
+'
+
+test_done
diff --git a/t/t4044-diff-index-unique-abbrev.sh b/t/t4044-diff-index-unique-abbrev.sh
new file mode 100755
index 0000000..d5ce72b
--- /dev/null
+++ b/t/t4044-diff-index-unique-abbrev.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='test unique sha1 abbreviation on "index from..to" line'
+. ./test-lib.sh
+
+cat >expect_initial <<EOF
+100644 blob 51d2738463ea4ca66f8691c91e33ce64b7d41bb1 foo
+EOF
+
+cat >expect_update <<EOF
+100644 blob 51d2738efb4ad8a1e40bed839ab8e116f0a15e47 foo
+EOF
+
+test_expect_success 'setup' '
+ echo 4827 > foo &&
+ git add foo &&
+ git commit -m "initial" &&
+ git cat-file -p HEAD: > actual &&
+ test_cmp expect_initial actual &&
+ echo 11742 > foo &&
+ git commit -a -m "update" &&
+ git cat-file -p HEAD: > actual &&
+ test_cmp expect_update actual
+'
+
+cat >expect <<EOF
+index 51d27384..51d2738e 100644
+EOF
+
+test_expect_success 'diff does not produce ambiguous index line' '
+ git diff HEAD^..HEAD | grep index > actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
new file mode 100755
index 0000000..9b433de
--- /dev/null
+++ b/t/t4100-apply-stat.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git apply --stat --summary test, with --recount
+
+'
+. ./test-lib.sh
+
+UNC='s/^\(@@ -[1-9][0-9]*\),[0-9]* \(+[1-9][0-9]*\),[0-9]* @@/\1,999 \2,999 @@/'
+
+num=0
+while read title
+do
+ num=$(( $num + 1 ))
+ test_expect_success "$title" '
+ git apply --stat --summary \
+ <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
+ test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+ '
+
+ test_expect_success "$title with recount" '
+ sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
+ git apply --recount --stat --summary >current &&
+ test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+ '
+done <<\EOF
+rename
+copy
+rewrite
+mode
+non git (1)
+non git (2)
+non git (3)
+incomplete (1)
+incomplete (2)
+EOF
+
+test_done
diff --git a/t/t4100/t-apply-1.expect b/t/t4100/t-apply-1.expect
new file mode 100644
index 0000000..540e64d
--- /dev/null
+++ b/t/t4100/t-apply-1.expect
@@ -0,0 +1,11 @@
+ Documentation/git-ssh-pull.txt | 12 ++++++------
+ Documentation/git-ssh-push.txt | 10 +++++-----
+ Documentation/git.txt | 6 +++---
+ Makefile | 6 +++---
+ ssh-pull.c | 4 ++--
+ ssh-push.c | 14 +++++++-------
+ 6 files changed, 26 insertions(+), 26 deletions(-)
+ rename Documentation/{git-rpull.txt => git-ssh-pull.txt} (90%)
+ rename Documentation/{git-rpush.txt => git-ssh-push.txt} (71%)
+ rename rpull.c => ssh-pull.c (97%)
+ rename rpush.c => ssh-push.c (93%)
diff --git a/t/t4100/t-apply-1.patch b/t/t4100/t-apply-1.patch
new file mode 100644
index 0000000..90ab54f
--- /dev/null
+++ b/t/t4100/t-apply-1.patch
@@ -0,0 +1,194 @@
+418aaf847a8b3ffffb4f777a2dd5262ca5ce0ef7 (from dc93841715dfa9a9cdda6f2c4a25eec831ea7aa0)
+diff --git a/Documentation/git-rpull.txt b/Documentation/git-ssh-pull.txt
+similarity index 90%
+rename from Documentation/git-rpull.txt
+rename to Documentation/git-ssh-pull.txt
+--- a/Documentation/git-rpull.txt
++++ b/Documentation/git-ssh-pull.txt
+@@ -1,21 +1,21 @@
+-git-rpull(1)
+-============
++git-ssh-pull(1)
++===============
+ v0.1, May 2005
+
+ NAME
+ ----
+-git-rpull - Pulls from a remote repository over ssh connection
++git-ssh-pull - Pulls from a remote repository over ssh connection
+
+
+
+ SYNOPSIS
+ --------
+-'git-rpull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url
++'git-ssh-pull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url
+
+ DESCRIPTION
+ -----------
+-Pulls from a remote repository over ssh connection, invoking git-rpush on
+-the other end.
++Pulls from a remote repository over ssh connection, invoking git-ssh-push
++on the other end.
+
+ OPTIONS
+ -------
+diff --git a/Documentation/git-rpush.txt b/Documentation/git-ssh-push.txt
+similarity index 71%
+rename from Documentation/git-rpush.txt
+rename to Documentation/git-ssh-push.txt
+--- a/Documentation/git-rpush.txt
++++ b/Documentation/git-ssh-push.txt
+@@ -1,19 +1,19 @@
+-git-rpush(1)
+-============
++git-ssh-push(1)
++===============
+ v0.1, May 2005
+
+ NAME
+ ----
+-git-rpush - Helper "server-side" program used by git-rpull
++git-ssh-push - Helper "server-side" program used by git-ssh-pull
+
+
+ SYNOPSIS
+ --------
+-'git-rpush'
++'git-ssh-push'
+
+ DESCRIPTION
+ -----------
+-Helper "server-side" program used by git-rpull.
++Helper "server-side" program used by git-ssh-pull.
+
+
+ Author
+diff --git a/Documentation/git.txt b/Documentation/git.txt
+--- a/Documentation/git.txt
++++ b/Documentation/git.txt
+@@ -148,7 +148,7 @@ link:git-resolve-script.html[git-resolve
+ link:git-tag-script.html[git-tag-script]::
+ An example script to create a tag object signed with GPG
+
+-link:git-rpull.html[git-rpull]::
++link:git-ssh-pull.html[git-ssh-pull]::
+ Pulls from a remote repository over ssh connection
+
+ Interogators:
+@@ -156,8 +156,8 @@ Interogators:
+ link:git-diff-helper.html[git-diff-helper]::
+ Generates patch format output for git-diff-*
+
+-link:git-rpush.html[git-rpush]::
+- Helper "server-side" program used by git-rpull
++link:git-ssh-push.html[git-ssh-push]::
++ Helper "server-side" program used by git-ssh-pull
+
+
+
+diff --git a/Makefile b/Makefile
+--- a/Makefile
++++ b/Makefile
+@@ -30,7 +30,7 @@ PROG= git-update-index git-diff-files
+ git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
+ git-check-files git-ls-tree git-merge-base git-merge-cache \
+ git-unpack-file git-export git-diff-cache git-convert-cache \
+- git-http-pull git-rpush git-rpull git-rev-list git-mktag \
++ git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \
+ git-diff-helper git-tar-tree git-local-pull git-write-blob \
+ git-get-tar-commit-id git-mkdelta git-apply git-stripspace
+
+@@ -105,8 +105,8 @@ git-diff-cache: diff-cache.c
+ git-convert-cache: convert-cache.c
+ git-http-pull: http-pull.c pull.c
+ git-local-pull: local-pull.c pull.c
+-git-rpush: rsh.c
+-git-rpull: rsh.c pull.c
++git-ssh-push: rsh.c
++git-ssh-pull: rsh.c pull.c
+ git-rev-list: rev-list.c
+ git-mktag: mktag.c
+ git-diff-helper: diff-helper.c
+diff --git a/rpull.c b/ssh-pull.c
+similarity index 97%
+rename from rpull.c
+rename to ssh-pull.c
+--- a/rpull.c
++++ b/ssh-pull.c
+@@ -64,13 +64,13 @@ int main(int argc, char **argv)
+ arg++;
+ }
+ if (argc < arg + 2) {
+- usage("git-rpull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url");
++ usage("git-ssh-pull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url");
+ return 1;
+ }
+ commit_id = argv[arg];
+ url = argv[arg + 1];
+
+- if (setup_connection(&fd_in, &fd_out, "git-rpush", url, arg, argv + 1))
++ if (setup_connection(&fd_in, &fd_out, "git-ssh-push", url, arg, argv + 1))
+ return 1;
+
+ if (get_version())
+diff --git a/rpush.c b/ssh-push.c
+similarity index 93%
+rename from rpush.c
+rename to ssh-push.c
+--- a/rpush.c
++++ b/ssh-push.c
+@@ -16,7 +16,7 @@ int serve_object(int fd_in, int fd_out)
+ do {
+ size = read(fd_in, sha1 + posn, 20 - posn);
+ if (size < 0) {
+- perror("git-rpush: read ");
++ perror("git-ssh-push: read ");
+ return -1;
+ }
+ if (!size)
+@@ -30,7 +30,7 @@ int serve_object(int fd_in, int fd_out)
+ buf = map_sha1_file(sha1, &objsize);
+
+ if (!buf) {
+- fprintf(stderr, "git-rpush: could not find %s\n",
++ fprintf(stderr, "git-ssh-push: could not find %s\n",
+ sha1_to_hex(sha1));
+ remote = -1;
+ }
+@@ -45,9 +45,9 @@ int serve_object(int fd_in, int fd_out)
+ size = write(fd_out, buf + posn, objsize - posn);
+ if (size <= 0) {
+ if (!size) {
+- fprintf(stderr, "git-rpush: write closed");
++ fprintf(stderr, "git-ssh-push: write closed");
+ } else {
+- perror("git-rpush: write ");
++ perror("git-ssh-push: write ");
+ }
+ return -1;
+ }
+@@ -71,7 +71,7 @@ void service(int fd_in, int fd_out) {
+ retval = read(fd_in, &type, 1);
+ if (retval < 1) {
+ if (retval < 0)
+- perror("rpush: read ");
++ perror("git-ssh-push: read ");
+ return;
+ }
+ if (type == 'v' && serve_version(fd_in, fd_out))
+@@ -91,12 +91,12 @@ int main(int argc, char **argv)
+ arg++;
+ }
+ if (argc < arg + 2) {
+- usage("git-rpush [-c] [-t] [-a] commit-id url");
++ usage("git-ssh-push [-c] [-t] [-a] commit-id url");
+ return 1;
+ }
+ commit_id = argv[arg];
+ url = argv[arg + 1];
+- if (setup_connection(&fd_in, &fd_out, "git-rpull", url, arg, argv + 1))
++ if (setup_connection(&fd_in, &fd_out, "git-ssh-pull", url, arg, argv + 1))
+ return 1;
+
+ service(fd_in, fd_out);
diff --git a/t/t4100/t-apply-2.expect b/t/t4100/t-apply-2.expect
new file mode 100644
index 0000000..d1e6459
--- /dev/null
+++ b/t/t4100/t-apply-2.expect
@@ -0,0 +1,5 @@
+ Makefile | 2 +-
+ git-fetch-script | 5 -----
+ git-pull-script | 34 +---------------------------------
+ 3 files changed, 2 insertions(+), 39 deletions(-)
+ copy git-pull-script => git-fetch-script (87%)
diff --git a/t/t4100/t-apply-2.patch b/t/t4100/t-apply-2.patch
new file mode 100644
index 0000000..f5c7d60
--- /dev/null
+++ b/t/t4100/t-apply-2.patch
@@ -0,0 +1,72 @@
+7ef76925d9c19ef74874e1735e2436e56d0c4897 (from 6b14d7faf0bad026a81a27bac07b47691f621b8f)
+diff --git a/Makefile b/Makefile
+--- a/Makefile
++++ b/Makefile
+@@ -20,7 +20,7 @@ INSTALL=install
+
+ SCRIPTS=git-apply-patch-script git-merge-one-file-script git-prune-script \
+ git-pull-script git-tag-script git-resolve-script git-whatchanged \
+- git-deltafy-script
++ git-deltafy-script git-fetch-script
+
+ PROG= git-update-index git-diff-files git-init-db git-write-tree \
+ git-read-tree git-commit-tree git-cat-file git-fsck-cache \
+diff --git a/git-pull-script b/git-fetch-script
+similarity index 87%
+copy from git-pull-script
+copy to git-fetch-script
+--- a/git-pull-script
++++ b/git-fetch-script
+@@ -39,8 +39,3 @@ download_one "$merge_repo/$merge_name" "
+
+ echo "Getting object database"
+ download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)"
+-
+-git-resolve-script \
+- "$(cat "$GIT_DIR"/HEAD)" \
+- "$(cat "$GIT_DIR"/MERGE_HEAD)" \
+- "$merge_repo"
+diff --git a/git-pull-script b/git-pull-script
+--- a/git-pull-script
++++ b/git-pull-script
+@@ -6,39 +6,7 @@ merge_name=${2:-HEAD}
+ : ${GIT_DIR=.git}
+ : ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"}
+
+-download_one () {
+- # remote_path="$1" local_file="$2"
+- case "$1" in
+- http://*)
+- wget -q -O "$2" "$1" ;;
+- /*)
+- test -f "$1" && cat >"$2" "$1" ;;
+- *)
+- rsync -L "$1" "$2" ;;
+- esac
+-}
+-
+-download_objects () {
+- # remote_repo="$1" head_sha1="$2"
+- case "$1" in
+- http://*)
+- git-http-pull -a "$2" "$1/"
+- ;;
+- /*)
+- git-local-pull -l -a "$2" "$1/"
+- ;;
+- *)
+- rsync -avz --ignore-existing \
+- "$1/objects/." "$GIT_OBJECT_DIRECTORY"/.
+- ;;
+- esac
+-}
+-
+-echo "Getting remote $merge_name"
+-download_one "$merge_repo/$merge_name" "$GIT_DIR"/MERGE_HEAD
+-
+-echo "Getting object database"
+-download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)"
++git-fetch-script "$merge_repo" "$merge_name"
+
+ git-resolve-script \
+ "$(cat "$GIT_DIR"/HEAD)" \
diff --git a/t/t4100/t-apply-3.expect b/t/t4100/t-apply-3.expect
new file mode 100644
index 0000000..912a552
--- /dev/null
+++ b/t/t4100/t-apply-3.expect
@@ -0,0 +1,7 @@
+ Documentation/git-ls-tree.txt | 20 +-
+ ls-tree.c | 459 ++++++++++++++++++++++-------------------
+ t/t3100-ls-tree-restrict.sh | 3
+ tree.c | 2
+ tree.h | 1
+ 5 files changed, 262 insertions(+), 223 deletions(-)
+ rewrite ls-tree.c (82%)
diff --git a/t/t4100/t-apply-3.patch b/t/t4100/t-apply-3.patch
new file mode 100644
index 0000000..90cdbaa
--- /dev/null
+++ b/t/t4100/t-apply-3.patch
@@ -0,0 +1,567 @@
+6af1f0192ff8740fe77db7cf02c739ccfbdf119c (from 2bc2564145835996734d6ed5d1880f85b17233d6)
+diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
+--- a/Documentation/git-ls-tree.txt
++++ b/Documentation/git-ls-tree.txt
+@@ -4,23 +4,26 @@ v0.1, May 2005
+
+ NAME
+ ----
+-git-ls-tree - Displays a tree object in human readable form
++git-ls-tree - Lists the contents of a tree object.
+
+
+ SYNOPSIS
+ --------
+-'git-ls-tree' [-r] [-z] <tree-ish> [paths...]
++'git-ls-tree' [-d] [-r] [-z] <tree-ish> [paths...]
+
+ DESCRIPTION
+ -----------
+-Converts the tree object to a human readable (and script processable)
+-form.
++Lists the contents of a tree object, like what "/bin/ls -a" does
++in the current working directory.
+
+ OPTIONS
+ -------
+ <tree-ish>::
+ Id of a tree.
+
++-d::
++ show only the named tree entry itself, not its children
++
+ -r::
+ recurse into sub-trees
+
+@@ -28,18 +31,19 @@ OPTIONS
+ \0 line termination on output
+
+ paths::
+- Optionally, restrict the output of git-ls-tree to specific
+- paths. Directories will only list their tree blob ids.
+- Implies -r.
++ When paths are given, shows them. Otherwise implicitly
++ uses the root level of the tree as the sole path argument.
++
+
+ Output Format
+ -------------
+- <mode>\t <type>\t <object>\t <file>
++ <mode> SP <type> SP <object> TAB <file>
+
+
+ Author
+ ------
+ Written by Linus Torvalds <torvalds@osdl.org>
++Completely rewritten from scratch by Junio C Hamano <junkio@cox.net>
+
+ Documentation
+ --------------
+diff --git a/ls-tree.c b/ls-tree.c
+dissimilarity index 82%
+--- ls-tree.c
++++ ls-tree.c
+@@ -1,212 +1,247 @@
+-/*
+- * GIT - The information manager from hell
+- *
+- * Copyright (C) Linus Torvalds, 2005
+- */
+-#include "cache.h"
+-
+-static int line_termination = '\n';
+-static int recursive = 0;
+-
+-struct path_prefix {
+- struct path_prefix *prev;
+- const char *name;
+-};
+-
+-#define DEBUG(fmt, ...)
+-
+-static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix)
+-{
+- int len = 0;
+- if (prefix) {
+- if (prefix->prev) {
+- len = string_path_prefix(buff,blen,prefix->prev);
+- buff += len;
+- blen -= len;
+- if (blen > 0) {
+- *buff = '/';
+- len++;
+- buff++;
+- blen--;
+- }
+- }
+- strncpy(buff,prefix->name,blen);
+- return len + strlen(prefix->name);
+- }
+-
+- return 0;
+-}
+-
+-static void print_path_prefix(struct path_prefix *prefix)
+-{
+- if (prefix) {
+- if (prefix->prev) {
+- print_path_prefix(prefix->prev);
+- putchar('/');
+- }
+- fputs(prefix->name, stdout);
+- }
+-}
+-
+-/*
+- * return:
+- * -1 if prefix is *not* a subset of path
+- * 0 if prefix == path
+- * 1 if prefix is a subset of path
+- */
+-static int pathcmp(const char *path, struct path_prefix *prefix)
+-{
+- char buff[PATH_MAX];
+- int len,slen;
+-
+- if (prefix == NULL)
+- return 1;
+-
+- len = string_path_prefix(buff, sizeof buff, prefix);
+- slen = strlen(path);
+-
+- if (slen < len)
+- return -1;
+-
+- if (strncmp(path,buff,len) == 0) {
+- if (slen == len)
+- return 0;
+- else
+- return 1;
+- }
+-
+- return -1;
+-}
+-
+-/*
+- * match may be NULL, or a *sorted* list of paths
+- */
+-static void list_recursive(void *buffer,
+- const char *type,
+- unsigned long size,
+- struct path_prefix *prefix,
+- char **match, int matches)
+-{
+- struct path_prefix this_prefix;
+- this_prefix.prev = prefix;
+-
+- if (strcmp(type, "tree"))
+- die("expected a 'tree' node");
+-
+- if (matches)
+- recursive = 1;
+-
+- while (size) {
+- int namelen = strlen(buffer)+1;
+- void *eltbuf = NULL;
+- char elttype[20];
+- unsigned long eltsize;
+- unsigned char *sha1 = buffer + namelen;
+- char *path = strchr(buffer, ' ') + 1;
+- unsigned int mode;
+- const char *matched = NULL;
+- int mtype = -1;
+- int mindex;
+-
+- if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
+- die("corrupt 'tree' file");
+- buffer = sha1 + 20;
+- size -= namelen + 20;
+-
+- this_prefix.name = path;
+- for ( mindex = 0; mindex < matches; mindex++) {
+- mtype = pathcmp(match[mindex],&this_prefix);
+- if (mtype >= 0) {
+- matched = match[mindex];
+- break;
+- }
+- }
+-
+- /*
+- * If we're not matching, or if this is an exact match,
+- * print out the info
+- */
+- if (!matches || (matched != NULL && mtype == 0)) {
+- printf("%06o %s %s\t", mode,
+- S_ISDIR(mode) ? "tree" : "blob",
+- sha1_to_hex(sha1));
+- print_path_prefix(&this_prefix);
+- putchar(line_termination);
+- }
+-
+- if (! recursive || ! S_ISDIR(mode))
+- continue;
+-
+- if (matches && ! matched)
+- continue;
+-
+- if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) {
+- error("cannot read %s", sha1_to_hex(sha1));
+- continue;
+- }
+-
+- /* If this is an exact directory match, we may have
+- * directory files following this path. Match on them.
+- * Otherwise, we're at a pach subcomponent, and we need
+- * to try to match again.
+- */
+- if (mtype == 0)
+- mindex++;
+-
+- list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex);
+- free(eltbuf);
+- }
+-}
+-
+-static int qcmp(const void *a, const void *b)
+-{
+- return strcmp(*(char **)a, *(char **)b);
+-}
+-
+-static int list(unsigned char *sha1,char **path)
+-{
+- void *buffer;
+- unsigned long size;
+- int npaths;
+-
+- for (npaths = 0; path[npaths] != NULL; npaths++)
+- ;
+-
+- qsort(path,npaths,sizeof(char *),qcmp);
+-
+- buffer = read_object_with_reference(sha1, "tree", &size, NULL);
+- if (!buffer)
+- die("unable to read sha1 file");
+- list_recursive(buffer, "tree", size, NULL, path, npaths);
+- free(buffer);
+- return 0;
+-}
+-
+-static const char *ls_tree_usage = "git-ls-tree [-r] [-z] <key> [paths...]";
+-
+-int main(int argc, char **argv)
+-{
+- unsigned char sha1[20];
+-
+- while (1 < argc && argv[1][0] == '-') {
+- switch (argv[1][1]) {
+- case 'z':
+- line_termination = 0;
+- break;
+- case 'r':
+- recursive = 1;
+- break;
+- default:
+- usage(ls_tree_usage);
+- }
+- argc--; argv++;
+- }
+-
+- if (argc < 2)
+- usage(ls_tree_usage);
+- if (get_sha1(argv[1], sha1) < 0)
+- usage(ls_tree_usage);
+- if (list(sha1, &argv[2]) < 0)
+- die("list failed");
+- return 0;
+-}
++/*
++ * GIT - The information manager from hell
++ *
++ * Copyright (C) Linus Torvalds, 2005
++ */
++#include "cache.h"
++#include "blob.h"
++#include "tree.h"
++
++static int line_termination = '\n';
++#define LS_RECURSIVE 1
++#define LS_TREE_ONLY 2
++static int ls_options = 0;
++
++static struct tree_entry_list root_entry;
++
++static void prepare_root(unsigned char *sha1)
++{
++ unsigned char rsha[20];
++ unsigned long size;
++ void *buf;
++ struct tree *root_tree;
++
++ buf = read_object_with_reference(sha1, "tree", &size, rsha);
++ free(buf);
++ if (!buf)
++ die("Could not read %s", sha1_to_hex(sha1));
++
++ root_tree = lookup_tree(rsha);
++ if (!root_tree)
++ die("Could not read %s", sha1_to_hex(sha1));
++
++ /* Prepare a fake entry */
++ root_entry.directory = 1;
++ root_entry.executable = root_entry.symlink = 0;
++ root_entry.mode = S_IFDIR;
++ root_entry.name = "";
++ root_entry.item.tree = root_tree;
++ root_entry.parent = NULL;
++}
++
++static int prepare_children(struct tree_entry_list *elem)
++{
++ if (!elem->directory)
++ return -1;
++ if (!elem->item.tree->object.parsed) {
++ struct tree_entry_list *e;
++ if (parse_tree(elem->item.tree))
++ return -1;
++ /* Set up the parent link */
++ for (e = elem->item.tree->entries; e; e = e->next)
++ e->parent = elem;
++ }
++ return 0;
++}
++
++static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem,
++ const char *path,
++ const char *path_end)
++{
++ const char *ep;
++ int len;
++
++ while (path < path_end) {
++ if (prepare_children(elem))
++ return NULL;
++
++ /* In elem->tree->entries, find the one that has name
++ * that matches what is between path and ep.
++ */
++ elem = elem->item.tree->entries;
++
++ ep = strchr(path, '/');
++ if (!ep || path_end <= ep)
++ ep = path_end;
++ len = ep - path;
++
++ while (elem) {
++ if ((strlen(elem->name) == len) &&
++ !strncmp(elem->name, path, len))
++ break;
++ elem = elem->next;
++ }
++ if (path_end <= ep || !elem)
++ return elem;
++ while (*ep == '/' && ep < path_end)
++ ep++;
++ path = ep;
++ }
++ return NULL;
++}
++
++static struct tree_entry_list *find_entry(const char *path,
++ const char *path_end)
++{
++ /* Find tree element, descending from root, that
++ * corresponds to the named path, lazily expanding
++ * the tree if possible.
++ */
++ if (path == path_end) {
++ /* Special. This is the root level */
++ return &root_entry;
++ }
++ return find_entry_0(&root_entry, path, path_end);
++}
++
++static void show_entry_name(struct tree_entry_list *e)
++{
++ /* This is yucky. The root level is there for
++ * our convenience but we really want to do a
++ * forest.
++ */
++ if (e->parent && e->parent != &root_entry) {
++ show_entry_name(e->parent);
++ putchar('/');
++ }
++ printf("%s", e->name);
++}
++
++static const char *entry_type(struct tree_entry_list *e)
++{
++ return (e->directory ? "tree" : "blob");
++}
++
++static const char *entry_hex(struct tree_entry_list *e)
++{
++ return sha1_to_hex(e->directory
++ ? e->item.tree->object.sha1
++ : e->item.blob->object.sha1);
++}
++
++/* forward declaration for mutually recursive routines */
++static int show_entry(struct tree_entry_list *, int);
++
++static int show_children(struct tree_entry_list *e, int level)
++{
++ if (prepare_children(e))
++ die("internal error: ls-tree show_children called with non tree");
++ e = e->item.tree->entries;
++ while (e) {
++ show_entry(e, level);
++ e = e->next;
++ }
++ return 0;
++}
++
++static int show_entry(struct tree_entry_list *e, int level)
++{
++ int err = 0;
++
++ if (e != &root_entry) {
++ printf("%06o %s %s ", e->mode, entry_type(e),
++ entry_hex(e));
++ show_entry_name(e);
++ putchar(line_termination);
++ }
++
++ if (e->directory) {
++ /* If this is a directory, we have the following cases:
++ * (1) This is the top-level request (explicit path from the
++ * command line, or "root" if there is no command line).
++ * a. Without any flag. We show direct children. We do not
++ * recurse into them.
++ * b. With -r. We do recurse into children.
++ * c. With -d. We do not recurse into children.
++ * (2) We came here because our caller is either (1-a) or
++ * (1-b).
++ * a. Without any flag. We do not show our children (which
++ * are grandchildren for the original request).
++ * b. With -r. We continue to recurse into our children.
++ * c. With -d. We should not have come here to begin with.
++ */
++ if (level == 0 && !(ls_options & LS_TREE_ONLY))
++ /* case (1)-a and (1)-b */
++ err = err | show_children(e, level+1);
++ else if (level && ls_options & LS_RECURSIVE)
++ /* case (2)-b */
++ err = err | show_children(e, level+1);
++ }
++ return err;
++}
++
++static int list_one(const char *path, const char *path_end)
++{
++ int err = 0;
++ struct tree_entry_list *e = find_entry(path, path_end);
++ if (!e) {
++ /* traditionally ls-tree does not complain about
++ * missing path. We may change this later to match
++ * what "/bin/ls -a" does, which is to complain.
++ */
++ return err;
++ }
++ err = err | show_entry(e, 0);
++ return err;
++}
++
++static int list(char **path)
++{
++ int i;
++ int err = 0;
++ for (i = 0; path[i]; i++) {
++ int len = strlen(path[i]);
++ while (0 <= len && path[i][len] == '/')
++ len--;
++ err = err | list_one(path[i], path[i] + len);
++ }
++ return err;
++}
++
++static const char *ls_tree_usage =
++ "git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
++
++int main(int argc, char **argv)
++{
++ static char *path0[] = { "", NULL };
++ char **path;
++ unsigned char sha1[20];
++
++ while (1 < argc && argv[1][0] == '-') {
++ switch (argv[1][1]) {
++ case 'z':
++ line_termination = 0;
++ break;
++ case 'r':
++ ls_options |= LS_RECURSIVE;
++ break;
++ case 'd':
++ ls_options |= LS_TREE_ONLY;
++ break;
++ default:
++ usage(ls_tree_usage);
++ }
++ argc--; argv++;
++ }
++
++ if (argc < 2)
++ usage(ls_tree_usage);
++ if (get_sha1(argv[1], sha1) < 0)
++ usage(ls_tree_usage);
++
++ path = (argc == 2) ? path0 : (argv + 2);
++ prepare_root(sha1);
++ if (list(path) < 0)
++ die("list failed");
++ return 0;
++}
+diff --git a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
+--- a/t/t3100-ls-tree-restrict.sh
++++ b/t/t3100-ls-tree-restrict.sh
+@@ -74,8 +74,8 @@ test_expect_success \
+ 'ls-tree filtered' \
+ 'git-ls-tree $tree path1 path0 >current &&
+ cat >expected <<\EOF &&
+-100644 blob X path0
+ 120000 blob X path1
++100644 blob X path0
+ EOF
+ test_output'
+
+@@ -85,7 +85,6 @@ test_expect_success \
+ cat >expected <<\EOF &&
+ 040000 tree X path2
+ 040000 tree X path2/baz
+-100644 blob X path2/baz/b
+ 120000 blob X path2/bazbo
+ 100644 blob X path2/foo
+ EOF
+diff --git a/tree.c b/tree.c
+--- a/tree.c
++++ b/tree.c
+@@ -133,7 +133,7 @@ int parse_tree_buffer(struct tree *item,
+ }
+ if (obj)
+ add_ref(&item->object, obj);
+-
++ entry->parent = NULL; /* needs to be filled by the user */
+ *list_p = entry;
+ list_p = &entry->next;
+ }
+diff --git a/tree.h b/tree.h
+--- a/tree.h
++++ b/tree.h
+@@ -16,6 +16,7 @@ struct tree_entry_list {
+ struct tree *tree;
+ struct blob *blob;
+ } item;
++ struct tree_entry_list *parent;
+ };
+
+ struct tree {
diff --git a/t/t4100/t-apply-4.expect b/t/t4100/t-apply-4.expect
new file mode 100644
index 0000000..1ec028b
--- /dev/null
+++ b/t/t4100/t-apply-4.expect
@@ -0,0 +1,5 @@
+ t/t0000-basic.sh | 0
+ t/test-lib.sh | 0
+ 2 files changed, 0 insertions(+), 0 deletions(-)
+ mode change 100644 => 100755 t/t0000-basic.sh
+ mode change 100644 => 100755 t/test-lib.sh
diff --git a/t/t4100/t-apply-4.patch b/t/t4100/t-apply-4.patch
new file mode 100644
index 0000000..4a56ab5
--- /dev/null
+++ b/t/t4100/t-apply-4.patch
@@ -0,0 +1,7 @@
+ceede59ea90cebad52ba9c8263fef3fb6ef17593 (from 368f99d57e8ed17243f2e164431449d48bfca2fb)
+diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
+old mode 100644
+new mode 100755
+diff --git a/t/test-lib.sh b/t/test-lib.sh
+old mode 100644
+new mode 100755
diff --git a/t/t4100/t-apply-5.expect b/t/t4100/t-apply-5.expect
new file mode 100644
index 0000000..b387df1
--- /dev/null
+++ b/t/t4100/t-apply-5.expect
@@ -0,0 +1,19 @@
+ Documentation/git-rpull.txt | 50 -------------------
+ Documentation/git-rpush.txt | 30 ------------
+ Documentation/git-ssh-pull.txt | 50 +++++++++++++++++++
+ Documentation/git-ssh-push.txt | 30 ++++++++++++
+ Documentation/git.txt | 6 +-
+ Makefile | 6 +-
+ rpull.c | 83 --------------------------------
+ rpush.c | 104 ----------------------------------------
+ ssh-pull.c | 83 ++++++++++++++++++++++++++++++++
+ ssh-push.c | 104 ++++++++++++++++++++++++++++++++++++++++
+ 10 files changed, 273 insertions(+), 273 deletions(-)
+ delete Documentation/git-rpull.txt
+ delete Documentation/git-rpush.txt
+ create Documentation/git-ssh-pull.txt
+ create Documentation/git-ssh-push.txt
+ delete rpull.c
+ delete rpush.c
+ create ssh-pull.c
+ create ssh-push.c
diff --git a/t/t4100/t-apply-5.patch b/t/t4100/t-apply-5.patch
new file mode 100644
index 0000000..5f6ddc1
--- /dev/null
+++ b/t/t4100/t-apply-5.patch
@@ -0,0 +1,612 @@
+diff a/Documentation/git-rpull.txt b/Documentation/git-rpull.txt
+--- a/Documentation/git-rpull.txt
++++ /dev/null
+@@ -1,50 +0,0 @@
+-git-rpull(1)
+-============
+-v0.1, May 2005
+-
+-NAME
+-----
+-git-rpull - Pulls from a remote repository over ssh connection
+-
+-
+-
+-SYNOPSIS
+---------
+-'git-rpull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url
+-
+-DESCRIPTION
+------------
+-Pulls from a remote repository over ssh connection, invoking git-rpush on
+-the other end.
+-
+-OPTIONS
+--------
+--c::
+- Get the commit objects.
+--t::
+- Get trees associated with the commit objects.
+--a::
+- Get all the objects.
+--d::
+- Do not check for delta base objects (use this option
+- only when you know the remote repository is not
+- deltified).
+---recover::
+- Check dependency of deltified object more carefully than
+- usual, to recover after earlier pull that was interrupted.
+--v::
+- Report what is downloaded.
+-
+-
+-Author
+-------
+-Written by Linus Torvalds <torvalds@osdl.org>
+-
+-Documentation
+---------------
+-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+-
+-GIT
+----
+-Part of the link:git.html[git] suite
+-
+diff a/Documentation/git-rpush.txt b/Documentation/git-rpush.txt
+--- a/Documentation/git-rpush.txt
++++ /dev/null
+@@ -1,30 +0,0 @@
+-git-rpush(1)
+-============
+-v0.1, May 2005
+-
+-NAME
+-----
+-git-rpush - Helper "server-side" program used by git-rpull
+-
+-
+-SYNOPSIS
+---------
+-'git-rpush'
+-
+-DESCRIPTION
+------------
+-Helper "server-side" program used by git-rpull.
+-
+-
+-Author
+-------
+-Written by Linus Torvalds <torvalds@osdl.org>
+-
+-Documentation
+---------------
+-Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+-
+-GIT
+----
+-Part of the link:git.html[git] suite
+-
+diff a/Documentation/git-ssh-pull.txt b/Documentation/git-ssh-pull.txt
+--- /dev/null
++++ b/Documentation/git-ssh-pull.txt
+@@ -0,0 +1,50 @@
++git-ssh-pull(1)
++===============
++v0.1, May 2005
++
++NAME
++----
++git-ssh-pull - Pulls from a remote repository over ssh connection
++
++
++
++SYNOPSIS
++--------
++'git-ssh-pull' [-c] [-t] [-a] [-d] [-v] [--recover] commit-id url
++
++DESCRIPTION
++-----------
++Pulls from a remote repository over ssh connection, invoking git-ssh-push
++on the other end.
++
++OPTIONS
++-------
++-c::
++ Get the commit objects.
++-t::
++ Get trees associated with the commit objects.
++-a::
++ Get all the objects.
++-d::
++ Do not check for delta base objects (use this option
++ only when you know the remote repository is not
++ deltified).
++--recover::
++ Check dependency of deltified object more carefully than
++ usual, to recover after earlier pull that was interrupted.
++-v::
++ Report what is downloaded.
++
++
++Author
++------
++Written by Linus Torvalds <torvalds@osdl.org>
++
++Documentation
++--------------
++Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
++
++GIT
++---
++Part of the link:git.html[git] suite
++
+diff a/Documentation/git-ssh-push.txt b/Documentation/git-ssh-push.txt
+--- /dev/null
++++ b/Documentation/git-ssh-push.txt
+@@ -0,0 +1,30 @@
++git-ssh-push(1)
++===============
++v0.1, May 2005
++
++NAME
++----
++git-ssh-push - Helper "server-side" program used by git-ssh-pull
++
++
++SYNOPSIS
++--------
++'git-ssh-push'
++
++DESCRIPTION
++-----------
++Helper "server-side" program used by git-ssh-pull.
++
++
++Author
++------
++Written by Linus Torvalds <torvalds@osdl.org>
++
++Documentation
++--------------
++Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
++
++GIT
++---
++Part of the link:git.html[git] suite
++
+diff a/Documentation/git.txt b/Documentation/git.txt
+--- a/Documentation/git.txt
++++ b/Documentation/git.txt
+@@ -148,7 +148,7 @@ link:git-resolve-script.html[git-resolve
+ link:git-tag-script.html[git-tag-script]::
+ An example script to create a tag object signed with GPG
+
+-link:git-rpull.html[git-rpull]::
++link:git-ssh-pull.html[git-ssh-pull]::
+ Pulls from a remote repository over ssh connection
+
+ Interogators:
+@@ -156,8 +156,8 @@ Interogators:
+ link:git-diff-helper.html[git-diff-helper]::
+ Generates patch format output for git-diff-*
+
+-link:git-rpush.html[git-rpush]::
+- Helper "server-side" program used by git-rpull
++link:git-ssh-push.html[git-ssh-push]::
++ Helper "server-side" program used by git-ssh-pull
+
+
+
+diff a/Makefile b/Makefile
+--- a/Makefile
++++ b/Makefile
+@@ -30,7 +30,7 @@ PROG= git-update-index git-diff-files
+ git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
+ git-check-files git-ls-tree git-merge-base git-merge-cache \
+ git-unpack-file git-export git-diff-cache git-convert-cache \
+- git-http-pull git-rpush git-rpull git-rev-list git-mktag \
++ git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \
+ git-diff-helper git-tar-tree git-local-pull git-write-blob \
+ git-get-tar-commit-id git-mkdelta git-apply git-stripspace
+
+@@ -105,8 +105,8 @@ git-diff-cache: diff-cache.c
+ git-convert-cache: convert-cache.c
+ git-http-pull: http-pull.c pull.c
+ git-local-pull: local-pull.c pull.c
+-git-rpush: rsh.c
+-git-rpull: rsh.c pull.c
++git-ssh-push: rsh.c
++git-ssh-pull: rsh.c pull.c
+ git-rev-list: rev-list.c
+ git-mktag: mktag.c
+ git-diff-helper: diff-helper.c
+diff a/rpull.c b/rpull.c
+--- a/rpull.c
++++ /dev/null
+@@ -1,83 +0,0 @@
+-#include "cache.h"
+-#include "commit.h"
+-#include "rsh.h"
+-#include "pull.h"
+-
+-static int fd_in;
+-static int fd_out;
+-
+-static unsigned char remote_version = 0;
+-static unsigned char local_version = 1;
+-
+-int fetch(unsigned char *sha1)
+-{
+- int ret;
+- signed char remote;
+- char type = 'o';
+- if (has_sha1_file(sha1))
+- return 0;
+- write(fd_out, &type, 1);
+- write(fd_out, sha1, 20);
+- if (read(fd_in, &remote, 1) < 1)
+- return -1;
+- if (remote < 0)
+- return remote;
+- ret = write_sha1_from_fd(sha1, fd_in);
+- if (!ret)
+- pull_say("got %s\n", sha1_to_hex(sha1));
+- return ret;
+-}
+-
+-int get_version(void)
+-{
+- char type = 'v';
+- write(fd_out, &type, 1);
+- write(fd_out, &local_version, 1);
+- if (read(fd_in, &remote_version, 1) < 1) {
+- return error("Couldn't read version from remote end");
+- }
+- return 0;
+-}
+-
+-int main(int argc, char **argv)
+-{
+- char *commit_id;
+- char *url;
+- int arg = 1;
+-
+- while (arg < argc && argv[arg][0] == '-') {
+- if (argv[arg][1] == 't') {
+- get_tree = 1;
+- } else if (argv[arg][1] == 'c') {
+- get_history = 1;
+- } else if (argv[arg][1] == 'd') {
+- get_delta = 0;
+- } else if (!strcmp(argv[arg], "--recover")) {
+- get_delta = 2;
+- } else if (argv[arg][1] == 'a') {
+- get_all = 1;
+- get_tree = 1;
+- get_history = 1;
+- } else if (argv[arg][1] == 'v') {
+- get_verbosely = 1;
+- }
+- arg++;
+- }
+- if (argc < arg + 2) {
+- usage("git-rpull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url");
+- return 1;
+- }
+- commit_id = argv[arg];
+- url = argv[arg + 1];
+-
+- if (setup_connection(&fd_in, &fd_out, "git-rpush", url, arg, argv + 1))
+- return 1;
+-
+- if (get_version())
+- return 1;
+-
+- if (pull(commit_id))
+- return 1;
+-
+- return 0;
+-}
+diff a/rpush.c b/rpush.c
+--- a/rpush.c
++++ /dev/null
+@@ -1,104 +0,0 @@
+-#include "cache.h"
+-#include "rsh.h"
+-#include <sys/socket.h>
+-#include <errno.h>
+-
+-unsigned char local_version = 1;
+-unsigned char remote_version = 0;
+-
+-int serve_object(int fd_in, int fd_out) {
+- ssize_t size;
+- int posn = 0;
+- char sha1[20];
+- unsigned long objsize;
+- void *buf;
+- signed char remote;
+- do {
+- size = read(fd_in, sha1 + posn, 20 - posn);
+- if (size < 0) {
+- perror("git-rpush: read ");
+- return -1;
+- }
+- if (!size)
+- return -1;
+- posn += size;
+- } while (posn < 20);
+-
+- /* fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1)); */
+- remote = 0;
+-
+- buf = map_sha1_file(sha1, &objsize);
+-
+- if (!buf) {
+- fprintf(stderr, "git-rpush: could not find %s\n",
+- sha1_to_hex(sha1));
+- remote = -1;
+- }
+-
+- write(fd_out, &remote, 1);
+-
+- if (remote < 0)
+- return 0;
+-
+- posn = 0;
+- do {
+- size = write(fd_out, buf + posn, objsize - posn);
+- if (size <= 0) {
+- if (!size) {
+- fprintf(stderr, "git-rpush: write closed");
+- } else {
+- perror("git-rpush: write ");
+- }
+- return -1;
+- }
+- posn += size;
+- } while (posn < objsize);
+- return 0;
+-}
+-
+-int serve_version(int fd_in, int fd_out)
+-{
+- if (read(fd_in, &remote_version, 1) < 1)
+- return -1;
+- write(fd_out, &local_version, 1);
+- return 0;
+-}
+-
+-void service(int fd_in, int fd_out) {
+- char type;
+- int retval;
+- do {
+- retval = read(fd_in, &type, 1);
+- if (retval < 1) {
+- if (retval < 0)
+- perror("rpush: read ");
+- return;
+- }
+- if (type == 'v' && serve_version(fd_in, fd_out))
+- return;
+- if (type == 'o' && serve_object(fd_in, fd_out))
+- return;
+- } while (1);
+-}
+-
+-int main(int argc, char **argv)
+-{
+- int arg = 1;
+- char *commit_id;
+- char *url;
+- int fd_in, fd_out;
+- while (arg < argc && argv[arg][0] == '-') {
+- arg++;
+- }
+- if (argc < arg + 2) {
+- usage("git-rpush [-c] [-t] [-a] commit-id url");
+- return 1;
+- }
+- commit_id = argv[arg];
+- url = argv[arg + 1];
+- if (setup_connection(&fd_in, &fd_out, "git-rpull", url, arg, argv + 1))
+- return 1;
+-
+- service(fd_in, fd_out);
+- return 0;
+-}
+diff a/ssh-pull.c b/ssh-pull.c
+--- /dev/null
++++ b/ssh-pull.c
+@@ -0,0 +1,83 @@
++#include "cache.h"
++#include "commit.h"
++#include "rsh.h"
++#include "pull.h"
++
++static int fd_in;
++static int fd_out;
++
++static unsigned char remote_version = 0;
++static unsigned char local_version = 1;
++
++int fetch(unsigned char *sha1)
++{
++ int ret;
++ signed char remote;
++ char type = 'o';
++ if (has_sha1_file(sha1))
++ return 0;
++ write(fd_out, &type, 1);
++ write(fd_out, sha1, 20);
++ if (read(fd_in, &remote, 1) < 1)
++ return -1;
++ if (remote < 0)
++ return remote;
++ ret = write_sha1_from_fd(sha1, fd_in);
++ if (!ret)
++ pull_say("got %s\n", sha1_to_hex(sha1));
++ return ret;
++}
++
++int get_version(void)
++{
++ char type = 'v';
++ write(fd_out, &type, 1);
++ write(fd_out, &local_version, 1);
++ if (read(fd_in, &remote_version, 1) < 1) {
++ return error("Couldn't read version from remote end");
++ }
++ return 0;
++}
++
++int main(int argc, char **argv)
++{
++ char *commit_id;
++ char *url;
++ int arg = 1;
++
++ while (arg < argc && argv[arg][0] == '-') {
++ if (argv[arg][1] == 't') {
++ get_tree = 1;
++ } else if (argv[arg][1] == 'c') {
++ get_history = 1;
++ } else if (argv[arg][1] == 'd') {
++ get_delta = 0;
++ } else if (!strcmp(argv[arg], "--recover")) {
++ get_delta = 2;
++ } else if (argv[arg][1] == 'a') {
++ get_all = 1;
++ get_tree = 1;
++ get_history = 1;
++ } else if (argv[arg][1] == 'v') {
++ get_verbosely = 1;
++ }
++ arg++;
++ }
++ if (argc < arg + 2) {
++ usage("git-ssh-pull [-c] [-t] [-a] [-v] [-d] [--recover] commit-id url");
++ return 1;
++ }
++ commit_id = argv[arg];
++ url = argv[arg + 1];
++
++ if (setup_connection(&fd_in, &fd_out, "git-ssh-push", url, arg, argv + 1))
++ return 1;
++
++ if (get_version())
++ return 1;
++
++ if (pull(commit_id))
++ return 1;
++
++ return 0;
++}
+diff a/ssh-push.c b/ssh-push.c
+--- /dev/null
++++ b/ssh-push.c
+@@ -0,0 +1,104 @@
++#include "cache.h"
++#include "rsh.h"
++#include <sys/socket.h>
++#include <errno.h>
++
++unsigned char local_version = 1;
++unsigned char remote_version = 0;
++
++int serve_object(int fd_in, int fd_out) {
++ ssize_t size;
++ int posn = 0;
++ char sha1[20];
++ unsigned long objsize;
++ void *buf;
++ signed char remote;
++ do {
++ size = read(fd_in, sha1 + posn, 20 - posn);
++ if (size < 0) {
++ perror("git-ssh-push: read ");
++ return -1;
++ }
++ if (!size)
++ return -1;
++ posn += size;
++ } while (posn < 20);
++
++ /* fprintf(stderr, "Serving %s\n", sha1_to_hex(sha1)); */
++ remote = 0;
++
++ buf = map_sha1_file(sha1, &objsize);
++
++ if (!buf) {
++ fprintf(stderr, "git-ssh-push: could not find %s\n",
++ sha1_to_hex(sha1));
++ remote = -1;
++ }
++
++ write(fd_out, &remote, 1);
++
++ if (remote < 0)
++ return 0;
++
++ posn = 0;
++ do {
++ size = write(fd_out, buf + posn, objsize - posn);
++ if (size <= 0) {
++ if (!size) {
++ fprintf(stderr, "git-ssh-push: write closed");
++ } else {
++ perror("git-ssh-push: write ");
++ }
++ return -1;
++ }
++ posn += size;
++ } while (posn < objsize);
++ return 0;
++}
++
++int serve_version(int fd_in, int fd_out)
++{
++ if (read(fd_in, &remote_version, 1) < 1)
++ return -1;
++ write(fd_out, &local_version, 1);
++ return 0;
++}
++
++void service(int fd_in, int fd_out) {
++ char type;
++ int retval;
++ do {
++ retval = read(fd_in, &type, 1);
++ if (retval < 1) {
++ if (retval < 0)
++ perror("git-ssh-push: read ");
++ return;
++ }
++ if (type == 'v' && serve_version(fd_in, fd_out))
++ return;
++ if (type == 'o' && serve_object(fd_in, fd_out))
++ return;
++ } while (1);
++}
++
++int main(int argc, char **argv)
++{
++ int arg = 1;
++ char *commit_id;
++ char *url;
++ int fd_in, fd_out;
++ while (arg < argc && argv[arg][0] == '-') {
++ arg++;
++ }
++ if (argc < arg + 2) {
++ usage("git-ssh-push [-c] [-t] [-a] commit-id url");
++ return 1;
++ }
++ commit_id = argv[arg];
++ url = argv[arg + 1];
++ if (setup_connection(&fd_in, &fd_out, "git-ssh-pull", url, arg, argv + 1))
++ return 1;
++
++ service(fd_in, fd_out);
++ return 0;
++}
diff --git a/t/t4100/t-apply-6.expect b/t/t4100/t-apply-6.expect
new file mode 100644
index 0000000..1c343d4
--- /dev/null
+++ b/t/t4100/t-apply-6.expect
@@ -0,0 +1,5 @@
+ Makefile | 2 +-
+ git-fetch-script | 41 +++++++++++++++++++++++++++++++++++++++++
+ git-pull-script | 34 +---------------------------------
+ 3 files changed, 43 insertions(+), 34 deletions(-)
+ create git-fetch-script
diff --git a/t/t4100/t-apply-6.patch b/t/t4100/t-apply-6.patch
new file mode 100644
index 0000000..a72729a
--- /dev/null
+++ b/t/t4100/t-apply-6.patch
@@ -0,0 +1,101 @@
+diff a/Makefile b/Makefile
+--- a/Makefile
++++ b/Makefile
+@@ -20,7 +20,7 @@ INSTALL=install
+
+ SCRIPTS=git-apply-patch-script git-merge-one-file-script git-prune-script \
+ git-pull-script git-tag-script git-resolve-script git-whatchanged \
+- git-deltafy-script
++ git-deltafy-script git-fetch-script
+
+ PROG= git-update-index git-diff-files git-init-db git-write-tree \
+ git-read-tree git-commit-tree git-cat-file git-fsck-cache \
+diff a/git-fetch-script b/git-fetch-script
+--- /dev/null
++++ b/git-fetch-script
+@@ -0,0 +1,41 @@
++#!/bin/sh
++#
++merge_repo=$1
++merge_name=${2:-HEAD}
++
++: ${GIT_DIR=.git}
++: ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"}
++
++download_one () {
++ # remote_path="$1" local_file="$2"
++ case "$1" in
++ http://*)
++ wget -q -O "$2" "$1" ;;
++ /*)
++ test -f "$1" && cat >"$2" "$1" ;;
++ *)
++ rsync -L "$1" "$2" ;;
++ esac
++}
++
++download_objects () {
++ # remote_repo="$1" head_sha1="$2"
++ case "$1" in
++ http://*)
++ git-http-pull -a "$2" "$1/"
++ ;;
++ /*)
++ git-local-pull -l -a "$2" "$1/"
++ ;;
++ *)
++ rsync -avz --ignore-existing \
++ "$1/objects/." "$GIT_OBJECT_DIRECTORY"/.
++ ;;
++ esac
++}
++
++echo "Getting remote $merge_name"
++download_one "$merge_repo/$merge_name" "$GIT_DIR"/MERGE_HEAD
++
++echo "Getting object database"
++download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)"
+diff a/git-pull-script b/git-pull-script
+--- a/git-pull-script
++++ b/git-pull-script
+@@ -6,39 +6,7 @@ merge_name=${2:-HEAD}
+ : ${GIT_DIR=.git}
+ : ${GIT_OBJECT_DIRECTORY="${SHA1_FILE_DIRECTORY-"$GIT_DIR/objects"}"}
+
+-download_one () {
+- # remote_path="$1" local_file="$2"
+- case "$1" in
+- http://*)
+- wget -q -O "$2" "$1" ;;
+- /*)
+- test -f "$1" && cat >"$2" "$1" ;;
+- *)
+- rsync -L "$1" "$2" ;;
+- esac
+-}
+-
+-download_objects () {
+- # remote_repo="$1" head_sha1="$2"
+- case "$1" in
+- http://*)
+- git-http-pull -a "$2" "$1/"
+- ;;
+- /*)
+- git-local-pull -l -a "$2" "$1/"
+- ;;
+- *)
+- rsync -avz --ignore-existing \
+- "$1/objects/." "$GIT_OBJECT_DIRECTORY"/.
+- ;;
+- esac
+-}
+-
+-echo "Getting remote $merge_name"
+-download_one "$merge_repo/$merge_name" "$GIT_DIR"/MERGE_HEAD
+-
+-echo "Getting object database"
+-download_objects "$merge_repo" "$(cat "$GIT_DIR"/MERGE_HEAD)"
++git-fetch-script "$merge_repo" "$merge_name"
+
+ git-resolve-script \
+ "$(cat "$GIT_DIR"/HEAD)" \
diff --git a/t/t4100/t-apply-7.expect b/t/t4100/t-apply-7.expect
new file mode 100644
index 0000000..1283164
--- /dev/null
+++ b/t/t4100/t-apply-7.expect
@@ -0,0 +1,6 @@
+ Documentation/git-ls-tree.txt | 20 +-
+ ls-tree.c | 333 +++++++++++++++++++++++------------------
+ t/t3100-ls-tree-restrict.sh | 3
+ tree.c | 2
+ tree.h | 1
+ 5 files changed, 199 insertions(+), 160 deletions(-)
diff --git a/t/t4100/t-apply-7.patch b/t/t4100/t-apply-7.patch
new file mode 100644
index 0000000..07c6589
--- /dev/null
+++ b/t/t4100/t-apply-7.patch
@@ -0,0 +1,494 @@
+diff a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
+--- a/Documentation/git-ls-tree.txt
++++ b/Documentation/git-ls-tree.txt
+@@ -4,23 +4,26 @@ v0.1, May 2005
+
+ NAME
+ ----
+-git-ls-tree - Displays a tree object in human readable form
++git-ls-tree - Lists the contents of a tree object.
+
+
+ SYNOPSIS
+ --------
+-'git-ls-tree' [-r] [-z] <tree-ish> [paths...]
++'git-ls-tree' [-d] [-r] [-z] <tree-ish> [paths...]
+
+ DESCRIPTION
+ -----------
+-Converts the tree object to a human readable (and script processable)
+-form.
++Lists the contents of a tree object, like what "/bin/ls -a" does
++in the current working directory.
+
+ OPTIONS
+ -------
+ <tree-ish>::
+ Id of a tree.
+
++-d::
++ show only the named tree entry itself, not its children
++
+ -r::
+ recurse into sub-trees
+
+@@ -28,18 +31,19 @@ OPTIONS
+ \0 line termination on output
+
+ paths::
+- Optionally, restrict the output of git-ls-tree to specific
+- paths. Directories will only list their tree blob ids.
+- Implies -r.
++ When paths are given, shows them. Otherwise implicitly
++ uses the root level of the tree as the sole path argument.
++
+
+ Output Format
+ -------------
+- <mode>\t <type>\t <object>\t <file>
++ <mode> SP <type> SP <object> TAB <file>
+
+
+ Author
+ ------
+ Written by Linus Torvalds <torvalds@osdl.org>
++Completely rewritten from scratch by Junio C Hamano <junkio@cox.net>
+
+ Documentation
+ --------------
+diff a/ls-tree.c b/ls-tree.c
+--- a/ls-tree.c
++++ b/ls-tree.c
+@@ -4,188 +4,217 @@
+ * Copyright (C) Linus Torvalds, 2005
+ */
+ #include "cache.h"
++#include "blob.h"
++#include "tree.h"
+
+ static int line_termination = '\n';
+-static int recursive = 0;
++#define LS_RECURSIVE 1
++#define LS_TREE_ONLY 2
++static int ls_options = 0;
+
+-struct path_prefix {
+- struct path_prefix *prev;
+- const char *name;
+-};
+-
+-#define DEBUG(fmt, ...)
+-
+-static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix)
+-{
+- int len = 0;
+- if (prefix) {
+- if (prefix->prev) {
+- len = string_path_prefix(buff,blen,prefix->prev);
+- buff += len;
+- blen -= len;
+- if (blen > 0) {
+- *buff = '/';
+- len++;
+- buff++;
+- blen--;
+- }
+- }
+- strncpy(buff,prefix->name,blen);
+- return len + strlen(prefix->name);
+- }
++static struct tree_entry_list root_entry;
+
+- return 0;
++static void prepare_root(unsigned char *sha1)
++{
++ unsigned char rsha[20];
++ unsigned long size;
++ void *buf;
++ struct tree *root_tree;
++
++ buf = read_object_with_reference(sha1, "tree", &size, rsha);
++ free(buf);
++ if (!buf)
++ die("Could not read %s", sha1_to_hex(sha1));
++
++ root_tree = lookup_tree(rsha);
++ if (!root_tree)
++ die("Could not read %s", sha1_to_hex(sha1));
++
++ /* Prepare a fake entry */
++ root_entry.directory = 1;
++ root_entry.executable = root_entry.symlink = 0;
++ root_entry.mode = S_IFDIR;
++ root_entry.name = "";
++ root_entry.item.tree = root_tree;
++ root_entry.parent = NULL;
+ }
+
+-static void print_path_prefix(struct path_prefix *prefix)
++static int prepare_children(struct tree_entry_list *elem)
+ {
+- if (prefix) {
+- if (prefix->prev) {
+- print_path_prefix(prefix->prev);
+- putchar('/');
+- }
+- fputs(prefix->name, stdout);
++ if (!elem->directory)
++ return -1;
++ if (!elem->item.tree->object.parsed) {
++ struct tree_entry_list *e;
++ if (parse_tree(elem->item.tree))
++ return -1;
++ /* Set up the parent link */
++ for (e = elem->item.tree->entries; e; e = e->next)
++ e->parent = elem;
+ }
++ return 0;
+ }
+
+-/*
+- * return:
+- * -1 if prefix is *not* a subset of path
+- * 0 if prefix == path
+- * 1 if prefix is a subset of path
+- */
+-static int pathcmp(const char *path, struct path_prefix *prefix)
+-{
+- char buff[PATH_MAX];
+- int len,slen;
++static struct tree_entry_list *find_entry_0(struct tree_entry_list *elem,
++ const char *path,
++ const char *path_end)
++{
++ const char *ep;
++ int len;
++
++ while (path < path_end) {
++ if (prepare_children(elem))
++ return NULL;
+
+- if (prefix == NULL)
+- return 1;
++ /* In elem->tree->entries, find the one that has name
++ * that matches what is between path and ep.
++ */
++ elem = elem->item.tree->entries;
+
+- len = string_path_prefix(buff, sizeof buff, prefix);
+- slen = strlen(path);
++ ep = strchr(path, '/');
++ if (!ep || path_end <= ep)
++ ep = path_end;
++ len = ep - path;
++
++ while (elem) {
++ if ((strlen(elem->name) == len) &&
++ !strncmp(elem->name, path, len))
++ break;
++ elem = elem->next;
++ }
++ if (path_end <= ep || !elem)
++ return elem;
++ while (*ep == '/' && ep < path_end)
++ ep++;
++ path = ep;
++ }
++ return NULL;
++}
+
+- if (slen < len)
+- return -1;
++static struct tree_entry_list *find_entry(const char *path,
++ const char *path_end)
++{
++ /* Find tree element, descending from root, that
++ * corresponds to the named path, lazily expanding
++ * the tree if possible.
++ */
++ if (path == path_end) {
++ /* Special. This is the root level */
++ return &root_entry;
++ }
++ return find_entry_0(&root_entry, path, path_end);
++}
+
+- if (strncmp(path,buff,len) == 0) {
+- if (slen == len)
+- return 0;
+- else
+- return 1;
++static void show_entry_name(struct tree_entry_list *e)
++{
++ /* This is yucky. The root level is there for
++ * our convenience but we really want to do a
++ * forest.
++ */
++ if (e->parent && e->parent != &root_entry) {
++ show_entry_name(e->parent);
++ putchar('/');
+ }
++ printf("%s", e->name);
++}
+
+- return -1;
+-}
++static const char *entry_type(struct tree_entry_list *e)
++{
++ return (e->directory ? "tree" : "blob");
++}
+
+-/*
+- * match may be NULL, or a *sorted* list of paths
+- */
+-static void list_recursive(void *buffer,
+- const char *type,
+- unsigned long size,
+- struct path_prefix *prefix,
+- char **match, int matches)
+-{
+- struct path_prefix this_prefix;
+- this_prefix.prev = prefix;
+-
+- if (strcmp(type, "tree"))
+- die("expected a 'tree' node");
+-
+- if (matches)
+- recursive = 1;
+-
+- while (size) {
+- int namelen = strlen(buffer)+1;
+- void *eltbuf = NULL;
+- char elttype[20];
+- unsigned long eltsize;
+- unsigned char *sha1 = buffer + namelen;
+- char *path = strchr(buffer, ' ') + 1;
+- unsigned int mode;
+- const char *matched = NULL;
+- int mtype = -1;
+- int mindex;
+-
+- if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
+- die("corrupt 'tree' file");
+- buffer = sha1 + 20;
+- size -= namelen + 20;
+-
+- this_prefix.name = path;
+- for ( mindex = 0; mindex < matches; mindex++) {
+- mtype = pathcmp(match[mindex],&this_prefix);
+- if (mtype >= 0) {
+- matched = match[mindex];
+- break;
+- }
+- }
++static const char *entry_hex(struct tree_entry_list *e)
++{
++ return sha1_to_hex(e->directory
++ ? e->item.tree->object.sha1
++ : e->item.blob->object.sha1);
++}
+
+- /*
+- * If we're not matching, or if this is an exact match,
+- * print out the info
+- */
+- if (!matches || (matched != NULL && mtype == 0)) {
+- printf("%06o %s %s\t", mode,
+- S_ISDIR(mode) ? "tree" : "blob",
+- sha1_to_hex(sha1));
+- print_path_prefix(&this_prefix);
+- putchar(line_termination);
+- }
++/* forward declaration for mutually recursive routines */
++static int show_entry(struct tree_entry_list *, int);
+
+- if (! recursive || ! S_ISDIR(mode))
+- continue;
++static int show_children(struct tree_entry_list *e, int level)
++{
++ if (prepare_children(e))
++ die("internal error: ls-tree show_children called with non tree");
++ e = e->item.tree->entries;
++ while (e) {
++ show_entry(e, level);
++ e = e->next;
++ }
++ return 0;
++}
+
+- if (matches && ! matched)
+- continue;
++static int show_entry(struct tree_entry_list *e, int level)
++{
++ int err = 0;
+
+- if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) {
+- error("cannot read %s", sha1_to_hex(sha1));
+- continue;
+- }
++ if (e != &root_entry) {
++ printf("%06o %s %s ", e->mode, entry_type(e),
++ entry_hex(e));
++ show_entry_name(e);
++ putchar(line_termination);
++ }
+
+- /* If this is an exact directory match, we may have
+- * directory files following this path. Match on them.
+- * Otherwise, we're at a pach subcomponent, and we need
+- * to try to match again.
++ if (e->directory) {
++ /* If this is a directory, we have the following cases:
++ * (1) This is the top-level request (explicit path from the
++ * command line, or "root" if there is no command line).
++ * a. Without any flag. We show direct children. We do not
++ * recurse into them.
++ * b. With -r. We do recurse into children.
++ * c. With -d. We do not recurse into children.
++ * (2) We came here because our caller is either (1-a) or
++ * (1-b).
++ * a. Without any flag. We do not show our children (which
++ * are grandchildren for the original request).
++ * b. With -r. We continue to recurse into our children.
++ * c. With -d. We should not have come here to begin with.
+ */
+- if (mtype == 0)
+- mindex++;
+-
+- list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex);
+- free(eltbuf);
++ if (level == 0 && !(ls_options & LS_TREE_ONLY))
++ /* case (1)-a and (1)-b */
++ err = err | show_children(e, level+1);
++ else if (level && ls_options & LS_RECURSIVE)
++ /* case (2)-b */
++ err = err | show_children(e, level+1);
+ }
++ return err;
+ }
+
+-static int qcmp(const void *a, const void *b)
++static int list_one(const char *path, const char *path_end)
+ {
+- return strcmp(*(char **)a, *(char **)b);
++ int err = 0;
++ struct tree_entry_list *e = find_entry(path, path_end);
++ if (!e) {
++ /* traditionally ls-tree does not complain about
++ * missing path. We may change this later to match
++ * what "/bin/ls -a" does, which is to complain.
++ */
++ return err;
++ }
++ err = err | show_entry(e, 0);
++ return err;
+ }
+
+-static int list(unsigned char *sha1,char **path)
++static int list(char **path)
+ {
+- void *buffer;
+- unsigned long size;
+- int npaths;
+-
+- for (npaths = 0; path[npaths] != NULL; npaths++)
+- ;
+-
+- qsort(path,npaths,sizeof(char *),qcmp);
+-
+- buffer = read_object_with_reference(sha1, "tree", &size, NULL);
+- if (!buffer)
+- die("unable to read sha1 file");
+- list_recursive(buffer, "tree", size, NULL, path, npaths);
+- free(buffer);
+- return 0;
++ int i;
++ int err = 0;
++ for (i = 0; path[i]; i++) {
++ int len = strlen(path[i]);
++ while (0 <= len && path[i][len] == '/')
++ len--;
++ err = err | list_one(path[i], path[i] + len);
++ }
++ return err;
+ }
+
+-static const char *ls_tree_usage = "git-ls-tree [-r] [-z] <key> [paths...]";
++static const char *ls_tree_usage =
++ "git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
+
+ int main(int argc, char **argv)
+ {
++ static char *path0[] = { "", NULL };
++ char **path;
+ unsigned char sha1[20];
+
+ while (1 < argc && argv[1][0] == '-') {
+@@ -194,7 +223,10 @@ int main(int argc, char **argv)
+ line_termination = 0;
+ break;
+ case 'r':
+- recursive = 1;
++ ls_options |= LS_RECURSIVE;
++ break;
++ case 'd':
++ ls_options |= LS_TREE_ONLY;
+ break;
+ default:
+ usage(ls_tree_usage);
+@@ -206,7 +238,10 @@ int main(int argc, char **argv)
+ usage(ls_tree_usage);
+ if (get_sha1(argv[1], sha1) < 0)
+ usage(ls_tree_usage);
+- if (list(sha1, &argv[2]) < 0)
++
++ path = (argc == 2) ? path0 : (argv + 2);
++ prepare_root(sha1);
++ if (list(path) < 0)
+ die("list failed");
+ return 0;
+ }
+diff a/t/t3100-ls-tree-restrict.sh b/t/t3100-ls-tree-restrict.sh
+--- a/t/t3100-ls-tree-restrict.sh
++++ b/t/t3100-ls-tree-restrict.sh
+@@ -74,8 +74,8 @@ test_expect_success \
+ 'ls-tree filtered' \
+ 'git-ls-tree $tree path1 path0 >current &&
+ cat >expected <<\EOF &&
+-100644 blob X path0
+ 120000 blob X path1
++100644 blob X path0
+ EOF
+ test_output'
+
+@@ -85,7 +85,6 @@ test_expect_success \
+ cat >expected <<\EOF &&
+ 040000 tree X path2
+ 040000 tree X path2/baz
+-100644 blob X path2/baz/b
+ 120000 blob X path2/bazbo
+ 100644 blob X path2/foo
+ EOF
+diff a/tree.c b/tree.c
+--- a/tree.c
++++ b/tree.c
+@@ -133,7 +133,7 @@ int parse_tree_buffer(struct tree *item,
+ }
+ if (obj)
+ add_ref(&item->object, obj);
+-
++ entry->parent = NULL; /* needs to be filled by the user */
+ *list_p = entry;
+ list_p = &entry->next;
+ }
+diff a/tree.h b/tree.h
+--- a/tree.h
++++ b/tree.h
+@@ -16,6 +16,7 @@ struct tree_entry_list {
+ struct tree *tree;
+ struct blob *blob;
+ } item;
++ struct tree_entry_list *parent;
+ };
+
+ struct tree {
diff --git a/t/t4100/t-apply-8.expect b/t/t4100/t-apply-8.expect
new file mode 100644
index 0000000..eef7f2e
--- /dev/null
+++ b/t/t4100/t-apply-8.expect
@@ -0,0 +1,2 @@
+ t/t4100-apply-stat.sh | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/t/t4100/t-apply-8.patch b/t/t4100/t-apply-8.patch
new file mode 100644
index 0000000..5ca13e6
--- /dev/null
+++ b/t/t4100/t-apply-8.patch
@@ -0,0 +1,11 @@
+diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
+index be837bb..0798c64 100755
+--- a/t/t4100-apply-stat.sh
++++ b/t/t4100-apply-stat.sh
+@@ -35,4 +35,4 @@ non git (2)
+ non git (3)
+ EOF
+
+-test_done
++test_done
+\ No newline at end of file
diff --git a/t/t4100/t-apply-9.expect b/t/t4100/t-apply-9.expect
new file mode 100644
index 0000000..eef7f2e
--- /dev/null
+++ b/t/t4100/t-apply-9.expect
@@ -0,0 +1,2 @@
+ t/t4100-apply-stat.sh | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/t/t4100/t-apply-9.patch b/t/t4100/t-apply-9.patch
new file mode 100644
index 0000000..875d57d
--- /dev/null
+++ b/t/t4100/t-apply-9.patch
@@ -0,0 +1,11 @@
+diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
+index 0798c64..be837bb 100755
+--- a/t/t4100-apply-stat.sh
++++ b/t/t4100-apply-stat.sh
+@@ -35,4 +35,4 @@ non git (2)
+ non git (3)
+ EOF
+
+-test_done
+\ No newline at end of file
++test_done
diff --git a/t/t4101-apply-nonl.sh b/t/t4101-apply-nonl.sh
new file mode 100755
index 0000000..e3443d0
--- /dev/null
+++ b/t/t4101-apply-nonl.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git apply should handle files with incomplete lines.
+
+'
+. ./test-lib.sh
+
+# setup
+
+(echo a; echo b) >frotz.0
+(echo a; echo b; echo c) >frotz.1
+(echo a; echo b | tr -d '\012') >frotz.2
+(echo a; echo c; echo b | tr -d '\012') >frotz.3
+
+for i in 0 1 2 3
+do
+ for j in 0 1 2 3
+ do
+ test $i -eq $j && continue
+ cat frotz.$i >frotz
+ test_expect_success "apply diff between $i and $j" '
+ git apply <"$TEST_DIRECTORY"/t4101/diff.$i-$j &&
+ test_cmp frotz.$j frotz
+ '
+ done
+done
+
+test_done
diff --git a/t/t4101/diff.0-1 b/t/t4101/diff.0-1
new file mode 100644
index 0000000..1010a88
--- /dev/null
+++ b/t/t4101/diff.0-1
@@ -0,0 +1,6 @@
+--- a/frotz
++++ b/frotz
+@@ -1,2 +1,3 @@
+ a
+ b
++c
diff --git a/t/t4101/diff.0-2 b/t/t4101/diff.0-2
new file mode 100644
index 0000000..36460a2
--- /dev/null
+++ b/t/t4101/diff.0-2
@@ -0,0 +1,7 @@
+--- a/frotz
++++ b/frotz
+@@ -1,2 +1,2 @@
+ a
+-b
++b
+\ No newline at end of file
diff --git a/t/t4101/diff.0-3 b/t/t4101/diff.0-3
new file mode 100644
index 0000000..b281c43
--- /dev/null
+++ b/t/t4101/diff.0-3
@@ -0,0 +1,8 @@
+--- a/frotz
++++ b/frotz
+@@ -1,2 +1,3 @@
+ a
+-b
++c
++b
+\ No newline at end of file
diff --git a/t/t4101/diff.1-0 b/t/t4101/diff.1-0
new file mode 100644
index 0000000..f0a2e92
--- /dev/null
+++ b/t/t4101/diff.1-0
@@ -0,0 +1,6 @@
+--- a/frotz
++++ b/frotz
+@@ -1,3 +1,2 @@
+ a
+ b
+-c
diff --git a/t/t4101/diff.1-2 b/t/t4101/diff.1-2
new file mode 100644
index 0000000..2a440a5
--- /dev/null
+++ b/t/t4101/diff.1-2
@@ -0,0 +1,8 @@
+--- a/frotz
++++ b/frotz
+@@ -1,3 +1,2 @@
+ a
+-b
+-c
++b
+\ No newline at end of file
diff --git a/t/t4101/diff.1-3 b/t/t4101/diff.1-3
new file mode 100644
index 0000000..61aff97
--- /dev/null
+++ b/t/t4101/diff.1-3
@@ -0,0 +1,8 @@
+--- a/frotz
++++ b/frotz
+@@ -1,3 +1,3 @@
+ a
+-b
+ c
++b
+\ No newline at end of file
diff --git a/t/t4101/diff.2-0 b/t/t4101/diff.2-0
new file mode 100644
index 0000000..c2e71ee
--- /dev/null
+++ b/t/t4101/diff.2-0
@@ -0,0 +1,7 @@
+--- a/frotz
++++ b/frotz
+@@ -1,2 +1,2 @@
+ a
+-b
+\ No newline at end of file
++b
diff --git a/t/t4101/diff.2-1 b/t/t4101/diff.2-1
new file mode 100644
index 0000000..a66d9fd
--- /dev/null
+++ b/t/t4101/diff.2-1
@@ -0,0 +1,8 @@
+--- a/frotz
++++ b/frotz
+@@ -1,2 +1,3 @@
+ a
+-b
+\ No newline at end of file
++b
++c
diff --git a/t/t4101/diff.2-3 b/t/t4101/diff.2-3
new file mode 100644
index 0000000..5633c83
--- /dev/null
+++ b/t/t4101/diff.2-3
@@ -0,0 +1,7 @@
+--- a/frotz
++++ b/frotz
+@@ -1,2 +1,3 @@
+ a
++c
+ b
+\ No newline at end of file
diff --git a/t/t4101/diff.3-0 b/t/t4101/diff.3-0
new file mode 100644
index 0000000..10b1a41
--- /dev/null
+++ b/t/t4101/diff.3-0
@@ -0,0 +1,8 @@
+--- a/frotz
++++ b/frotz
+@@ -1,3 +1,2 @@
+ a
+-c
+-b
+\ No newline at end of file
++b
diff --git a/t/t4101/diff.3-1 b/t/t4101/diff.3-1
new file mode 100644
index 0000000..c799c60
--- /dev/null
+++ b/t/t4101/diff.3-1
@@ -0,0 +1,8 @@
+--- a/frotz
++++ b/frotz
+@@ -1,3 +1,3 @@
+ a
++b
+ c
+-b
+\ No newline at end of file
diff --git a/t/t4101/diff.3-2 b/t/t4101/diff.3-2
new file mode 100644
index 0000000..f8d1ba6
--- /dev/null
+++ b/t/t4101/diff.3-2
@@ -0,0 +1,7 @@
+--- a/frotz
++++ b/frotz
+@@ -1,3 +1,2 @@
+ a
+-c
+ b
+\ No newline at end of file
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
new file mode 100755
index 0000000..1597965
--- /dev/null
+++ b/t/t4102-apply-rename.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git apply handling copy/rename patch.
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat >test-patch <<\EOF
+diff --git a/foo b/bar
+similarity index 47%
+rename from foo
+rename to bar
+--- a/foo
++++ b/bar
+@@ -1 +1 @@
+-This is foo
++This is bar
+EOF
+
+echo 'This is foo' >foo
+chmod +x foo
+
+test_expect_success setup \
+ 'git update-index --add foo'
+
+test_expect_success apply \
+ 'git apply --index --stat --summary --apply test-patch'
+
+if test "$(git config --bool core.filemode)" = false
+then
+ say 'filemode disabled on the filesystem'
+else
+ test_set_prereq FILEMODE
+fi
+
+test_expect_success FILEMODE validate \
+ 'test -f bar && ls -l bar | grep "^-..x......"'
+
+test_expect_success 'apply reverse' \
+ 'git apply -R --index --stat --summary --apply test-patch &&
+ test "$(cat foo)" = "This is foo"'
+
+cat >test-patch <<\EOF
+diff --git a/foo b/bar
+similarity index 47%
+copy from foo
+copy to bar
+--- a/foo
++++ b/bar
+@@ -1 +1 @@
+-This is foo
++This is bar
+EOF
+
+test_expect_success 'apply copy' \
+ 'git apply --index --stat --summary --apply test-patch &&
+ test "$(cat bar)" = "This is bar" -a "$(cat foo)" = "This is foo"'
+
+test_done
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
new file mode 100755
index 0000000..9692f16
--- /dev/null
+++ b/t/t4103-apply-binary.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git apply handling binary patches
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat >file1 <<EOF
+A quick brown fox jumps over the lazy dog.
+A tiny little penguin runs around in circles.
+There is a flag with Linux written on it.
+A slow black-and-white panda just sits there,
+munching on his bamboo.
+EOF
+cat file1 >file2
+cat file1 >file4
+
+test_expect_success 'setup' "
+ git update-index --add --remove file1 file2 file4 &&
+ git commit -m 'Initial Version' 2>/dev/null &&
+
+ git checkout -b binary &&
+ perl -pe 'y/x/\000/' <file1 >file3 &&
+ cat file3 >file4 &&
+ git add file2 &&
+ perl -pe 'y/\000/v/' <file3 >file1 &&
+ rm -f file2 &&
+ git update-index --add --remove file1 file2 file3 file4 &&
+ git commit -m 'Second Version' &&
+
+ git diff-tree -p master binary >B.diff &&
+ git diff-tree -p -C master binary >C.diff &&
+
+ git diff-tree -p --binary master binary >BF.diff &&
+ git diff-tree -p --binary -C master binary >CF.diff
+"
+
+test_expect_success 'stat binary diff -- should not fail.' \
+ 'git checkout master
+ git apply --stat --summary B.diff'
+
+test_expect_success 'stat binary diff (copy) -- should not fail.' \
+ 'git checkout master
+ git apply --stat --summary C.diff'
+
+test_expect_success 'check binary diff -- should fail.' \
+ 'git checkout master &&
+ test_must_fail git apply --check B.diff'
+
+test_expect_success 'check binary diff (copy) -- should fail.' \
+ 'git checkout master &&
+ test_must_fail git apply --check C.diff'
+
+test_expect_success \
+ 'check incomplete binary diff with replacement -- should fail.' '
+ git checkout master &&
+ test_must_fail git apply --check --allow-binary-replacement B.diff
+'
+
+test_expect_success \
+ 'check incomplete binary diff with replacement (copy) -- should fail.' '
+ git checkout master &&
+ test_must_fail git apply --check --allow-binary-replacement C.diff
+'
+
+test_expect_success 'check binary diff with replacement.' \
+ 'git checkout master
+ git apply --check --allow-binary-replacement BF.diff'
+
+test_expect_success 'check binary diff with replacement (copy).' \
+ 'git checkout master
+ git apply --check --allow-binary-replacement CF.diff'
+
+# Now we start applying them.
+
+do_reset () {
+ rm -f file? &&
+ git reset --hard &&
+ git checkout -f master
+}
+
+test_expect_success 'apply binary diff -- should fail.' \
+ 'do_reset &&
+ test_must_fail git apply B.diff'
+
+test_expect_success 'apply binary diff -- should fail.' \
+ 'do_reset &&
+ test_must_fail git apply --index B.diff'
+
+test_expect_success 'apply binary diff (copy) -- should fail.' \
+ 'do_reset &&
+ test_must_fail git apply C.diff'
+
+test_expect_success 'apply binary diff (copy) -- should fail.' \
+ 'do_reset &&
+ test_must_fail git apply --index C.diff'
+
+test_expect_success 'apply binary diff without replacement.' \
+ 'do_reset &&
+ git apply BF.diff'
+
+test_expect_success 'apply binary diff without replacement (copy).' \
+ 'do_reset &&
+ git apply CF.diff'
+
+test_expect_success 'apply binary diff.' \
+ 'do_reset &&
+ git apply --allow-binary-replacement --index BF.diff &&
+ test -z "$(git diff --name-status binary)"'
+
+test_expect_success 'apply binary diff (copy).' \
+ 'do_reset &&
+ git apply --allow-binary-replacement --index CF.diff &&
+ test -z "$(git diff --name-status binary)"'
+
+test_done
diff --git a/t/t4104-apply-boundary.sh b/t/t4104-apply-boundary.sh
new file mode 100755
index 0000000..c617c2a
--- /dev/null
+++ b/t/t4104-apply-boundary.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git apply boundary tests
+
+'
+. ./test-lib.sh
+
+L="c d e f g h i j k l m n o p q r s t u v w x"
+
+test_expect_success setup '
+ for i in b '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >original &&
+ git update-index --add victim &&
+
+ : add to the head
+ for i in a b '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >add-a-expect &&
+ git diff victim >add-a-patch.with &&
+ git diff --unified=0 >add-a-patch.without &&
+
+ : insert at line two
+ for i in b a '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >insert-a-expect &&
+ git diff victim >insert-a-patch.with &&
+ git diff --unified=0 >insert-a-patch.without &&
+
+ : modify at the head
+ for i in a '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >mod-a-expect &&
+ git diff victim >mod-a-patch.with &&
+ git diff --unified=0 >mod-a-patch.without &&
+
+ : remove from the head
+ for i in '"$L"' y
+ do
+ echo $i
+ done >victim &&
+ cat victim >del-a-expect &&
+ git diff victim >del-a-patch.with
+ git diff --unified=0 >del-a-patch.without &&
+
+ : add to the tail
+ for i in b '"$L"' y z
+ do
+ echo $i
+ done >victim &&
+ cat victim >add-z-expect &&
+ git diff victim >add-z-patch.with &&
+ git diff --unified=0 >add-z-patch.without &&
+
+ : modify at the tail
+ for i in b '"$L"' z
+ do
+ echo $i
+ done >victim &&
+ cat victim >mod-z-expect &&
+ git diff victim >mod-z-patch.with &&
+ git diff --unified=0 >mod-z-patch.without &&
+
+ : remove from the tail
+ for i in b '"$L"'
+ do
+ echo $i
+ done >victim &&
+ cat victim >del-z-expect &&
+ git diff victim >del-z-patch.with
+ git diff --unified=0 >del-z-patch.without &&
+
+ : done
+'
+
+for with in with without
+do
+ case "$with" in
+ with) u= ;;
+ without) u='--unidiff-zero ' ;;
+ esac
+ for kind in add-a add-z insert-a mod-a mod-z del-a del-z
+ do
+ test_expect_success "apply $kind-patch $with context" '
+ cat original >victim &&
+ git update-index victim &&
+ git apply --index '"$u$kind-patch.$with"' || {
+ cat '"$kind-patch.$with"'
+ (exit 1)
+ } &&
+ test_cmp '"$kind"'-expect victim
+ '
+ done
+done
+
+for kind in add-a add-z insert-a mod-a mod-z del-a del-z
+do
+ rm -f $kind-ng.without
+ sed -e "s/^diff --git /diff /" \
+ -e '/^index /d' \
+ <$kind-patch.without >$kind-ng.without
+ test_expect_success "apply non-git $kind-patch without context" '
+ cat original >victim &&
+ git update-index victim &&
+ git apply --unidiff-zero --index '"$kind-ng.without"' || {
+ cat '"$kind-ng.without"'
+ (exit 1)
+ } &&
+ test_cmp '"$kind"'-expect victim
+ '
+done
+
+test_expect_success 'two lines' '
+
+ >file &&
+ git add file &&
+ echo aaa >file &&
+ git diff >patch &&
+ git add file &&
+ echo bbb >file &&
+ git add file &&
+ test_must_fail git apply --check patch
+
+'
+
+test_expect_success 'apply patch with 3 context lines matching at end' '
+ { echo a; echo b; echo c; echo d; } >file &&
+ git add file &&
+ echo e >>file &&
+ git diff >patch &&
+ >file &&
+ test_must_fail git apply patch
+'
+
+test_done
diff --git a/t/t4105-apply-fuzz.sh b/t/t4105-apply-fuzz.sh
new file mode 100755
index 0000000..3266e39
--- /dev/null
+++ b/t/t4105-apply-fuzz.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+test_description='apply with fuzz and offset'
+
+. ./test-lib.sh
+
+dotest () {
+ name="$1" && shift &&
+ test_expect_success "$name" "
+ git checkout-index -f -q -u file &&
+ git apply $* &&
+ test_cmp expect file
+ "
+}
+
+test_expect_success setup '
+
+ for i in 1 2 3 4 5 6 7 8 9 10 11 12
+ do
+ echo $i
+ done >file &&
+ git update-index --add file &&
+ for i in 1 2 3 4 5 6 7 a b c d e 8 9 10 11 12
+ do
+ echo $i
+ done >file &&
+ cat file >expect &&
+ git diff >O0.diff &&
+
+ sed -e "s/@@ -5,6 +5,11 @@/@@ -2,6 +2,11 @@/" >O1.diff O0.diff &&
+ sed -e "s/@@ -5,6 +5,11 @@/@@ -7,6 +7,11 @@/" >O2.diff O0.diff &&
+ sed -e "s/@@ -5,6 +5,11 @@/@@ -19,6 +19,11 @@/" >O3.diff O0.diff &&
+
+ sed -e "s/^ 5/ S/" >F0.diff O0.diff &&
+ sed -e "s/^ 5/ S/" >F1.diff O1.diff &&
+ sed -e "s/^ 5/ S/" >F2.diff O2.diff &&
+ sed -e "s/^ 5/ S/" >F3.diff O3.diff
+
+'
+
+dotest 'unmodified patch' O0.diff
+
+dotest 'minus offset' O1.diff
+
+dotest 'plus offset' O2.diff
+
+dotest 'big offset' O3.diff
+
+dotest 'fuzz with no offset' -C2 F0.diff
+
+dotest 'fuzz with minus offset' -C2 F1.diff
+
+dotest 'fuzz with plus offset' -C2 F2.diff
+
+dotest 'fuzz with big offset' -C2 F3.diff
+
+test_done
diff --git a/t/t4106-apply-stdin.sh b/t/t4106-apply-stdin.sh
new file mode 100755
index 0000000..72467a1
--- /dev/null
+++ b/t/t4106-apply-stdin.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description='git apply --numstat - <patch'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo hello >text &&
+ git add text &&
+ echo goodbye >text &&
+ git diff >patch
+'
+
+test_expect_success 'git apply --numstat - < patch' '
+ echo "1 1 text" >expect &&
+ git apply --numstat - <patch >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git apply --numstat - < patch patch' '
+ for i in 1 2; do echo "1 1 text"; done >expect &&
+ git apply --numstat - < patch patch >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh
new file mode 100755
index 0000000..b04fc8f
--- /dev/null
+++ b/t/t4107-apply-ignore-whitespace.sh
@@ -0,0 +1,185 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Giuseppe Bilotta
+#
+
+test_description='git-apply --ignore-whitespace.
+
+'
+. ./test-lib.sh
+
+# This primes main.c file that indents without using HT at all.
+# Various patches with HT and other spaces are attempted in the test.
+
+cat > patch1.patch <<\EOF
+diff --git a/main.c b/main.c
+new file mode 100644
+--- /dev/null
++++ b/main.c
+@@ -0,0 +1,22 @@
++#include <stdio.h>
++
++void print_int(int num);
++int func(int num);
++
++int main() {
++ int i;
++
++ for (i = 0; i < 10; i++) {
++ print_int(func(i)); /* stuff */
++ }
++
++ return 0;
++}
++
++int func(int num) {
++ return num * num;
++}
++
++void print_int(int num) {
++ printf("%d", num);
++}
+EOF
+
+# Since whitespace is very significant and we want to prevent whitespace
+# mangling when creating this test from a patch, we protect 'fixable'
+# whitespace by replacing spaces with Z and replacing them at patch
+# creation time, hence the sed trick.
+
+# This patch will fail unless whitespace differences are being ignored
+
+sed -e 's/Z/ /g' > patch2.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,6 +10,8 @@
+Z print_int(func(i)); /* stuff */
+Z }
+Z
++ printf("\n");
++
+Z return 0;
+Z}
+Z
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing string at EOL. TODO: this testcase should be
+# improved by creating a line that has the same hash with and without
+# the final string.
+
+sed -e 's/Z/ /g' > patch3.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,3 +10,4 @@
+Z for (i = 0; i < 10; i++) {
+Z print_int(func(i));Z
++ /* stuff */
+Z }
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing EOL at EOF.
+
+sed -e 's/Z/ /g' > patch4.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -21,1 +21,1 @@
+- };Z
+\ No newline at end of file
++ };
+EOF
+
+# This patch will fail unless whitespace differences are being ignored.
+
+sed -e 's/Z/ /g' > patch5.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -2,2 +2,3 @@
+Z void print_int(int num);
++ /* a comment */
+Z int func(int num);
+EOF
+
+# And this is how the final output should be. Patches introduce
+# HTs but the original SP indents are mostly kept.
+
+sed -e 's/T/ /g' > main.c.final <<\EOF
+#include <stdio.h>
+
+void print_int(int num);
+T/* a comment */
+int func(int num);
+
+int main() {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ print_int(func(i)); /* stuff */
+ }
+
+Tprintf("\n");
+
+ return 0;
+}
+
+int func(int num) {
+ return num * num;
+}
+
+void print_int(int num) {
+ printf("%d", num);
+}
+EOF
+
+test_expect_success 'file creation' '
+ git apply patch1.patch
+'
+
+test_expect_success 'patch2 fails (retab)' '
+ test_must_fail git apply patch2.patch
+'
+
+test_expect_success 'patch2 applies with --ignore-whitespace' '
+ git apply --ignore-whitespace patch2.patch
+'
+
+test_expect_success 'patch2 reverse applies with --ignore-space-change' '
+ git apply -R --ignore-space-change patch2.patch
+'
+
+git config apply.ignorewhitespace change
+
+test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' '
+ git apply patch2.patch
+'
+
+test_expect_success 'patch3 fails (missing string at EOL)' '
+ test_must_fail git apply patch3.patch
+'
+
+test_expect_success 'patch4 fails (missing EOL at EOF)' '
+ test_must_fail git apply patch4.patch
+'
+
+test_expect_success 'patch5 applies (leading whitespace)' '
+ git apply patch5.patch
+'
+
+test_expect_success 'patches do not mangle whitespace' '
+ test_cmp main.c main.c.final
+'
+
+test_expect_success 're-create file (with --ignore-whitespace)' '
+ rm -f main.c &&
+ git apply patch1.patch
+'
+
+test_expect_success 'patch5 fails (--no-ignore-whitespace)' '
+ test_must_fail git apply --no-ignore-whitespace patch5.patch
+'
+
+test_done
diff --git a/t/t4109-apply-multifrag.sh b/t/t4109-apply-multifrag.sh
new file mode 100755
index 0000000..ac58083
--- /dev/null
+++ b/t/t4109-apply-multifrag.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+# Copyright (c) 2005 Robert Fitzsimons
+#
+
+test_description='git apply test patches with multiple fragments.'
+
+. ./test-lib.sh
+
+cp "$TEST_DIRECTORY/t4109/patch1.patch" .
+cp "$TEST_DIRECTORY/t4109/patch2.patch" .
+cp "$TEST_DIRECTORY/t4109/patch3.patch" .
+cp "$TEST_DIRECTORY/t4109/patch4.patch" .
+
+test_expect_success 'git apply (1)' '
+ git apply patch1.patch patch2.patch &&
+ test_cmp "$TEST_DIRECTORY/t4109/expect-1" main.c
+'
+rm -f main.c
+
+test_expect_success 'git apply (2)' '
+ git apply patch1.patch patch2.patch patch3.patch &&
+ test_cmp "$TEST_DIRECTORY/t4109/expect-2" main.c
+'
+rm -f main.c
+
+test_expect_success 'git apply (3)' '
+ git apply patch1.patch patch4.patch &&
+ test_cmp "$TEST_DIRECTORY/t4109/expect-3" main.c
+'
+mv main.c main.c.git
+
+test_done
+
diff --git a/t/t4109/expect-1 b/t/t4109/expect-1
new file mode 100644
index 0000000..1db5ff1
--- /dev/null
+++ b/t/t4109/expect-1
@@ -0,0 +1,31 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+int func(int num);
+void print_int(int num);
+void print_ln();
+
+int main() {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ print_int(func(i));
+ }
+
+ print_ln();
+
+ return 0;
+}
+
+int func(int num) {
+ return num * num;
+}
+
+void print_int(int num) {
+ printf("%d", num);
+}
+
+void print_ln() {
+ printf("\n");
+}
+
diff --git a/t/t4109/expect-2 b/t/t4109/expect-2
new file mode 100644
index 0000000..bc52924
--- /dev/null
+++ b/t/t4109/expect-2
@@ -0,0 +1,23 @@
+#include <stdio.h>
+
+int func(int num);
+void print_int(int num);
+
+int main() {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ print_int(func(i));
+ }
+
+ return 0;
+}
+
+int func(int num) {
+ return num * num;
+}
+
+void print_int(int num) {
+ printf("%d", num);
+}
+
diff --git a/t/t4109/expect-3 b/t/t4109/expect-3
new file mode 100644
index 0000000..cd2a475
--- /dev/null
+++ b/t/t4109/expect-3
@@ -0,0 +1,24 @@
+#include <stdio.h>
+
+int func(int num);
+int func2(int num);
+
+int main() {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ printf("%d", func(i));
+ printf("%d", func3(i));
+ }
+
+ return 0;
+}
+
+int func(int num) {
+ return num * num;
+}
+
+int func2(int num) {
+ return num * num * num;
+}
+
diff --git a/t/t4109/patch1.patch b/t/t4109/patch1.patch
new file mode 100644
index 0000000..1d411fc
--- /dev/null
+++ b/t/t4109/patch1.patch
@@ -0,0 +1,28 @@
+diff --git a/main.c b/main.c
+new file mode 100644
+--- /dev/null
++++ b/main.c
+@@ -0,0 +1,23 @@
++#include <stdio.h>
++
++int func(int num);
++void print_int(int num);
++
++int main() {
++ int i;
++
++ for (i = 0; i < 10; i++) {
++ print_int(func(i));
++ }
++
++ return 0;
++}
++
++int func(int num) {
++ return num * num;
++}
++
++void print_int(int num) {
++ printf("%d", num);
++}
++
diff --git a/t/t4109/patch2.patch b/t/t4109/patch2.patch
new file mode 100644
index 0000000..8c6b06d
--- /dev/null
+++ b/t/t4109/patch2.patch
@@ -0,0 +1,30 @@
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -1,7 +1,9 @@
++#include <stdlib.h>
+ #include <stdio.h>
+
+ int func(int num);
+ void print_int(int num);
++void print_ln();
+
+ int main() {
+ int i;
+@@ -10,6 +12,8 @@
+ print_int(func(i));
+ }
+
++ print_ln();
++
+ return 0;
+ }
+
+@@ -21,3 +25,7 @@
+ printf("%d", num);
+ }
+
++void print_ln() {
++ printf("\n");
++}
++
diff --git a/t/t4109/patch3.patch b/t/t4109/patch3.patch
new file mode 100644
index 0000000..d696c55
--- /dev/null
+++ b/t/t4109/patch3.patch
@@ -0,0 +1,31 @@
+cat > patch3.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -1,9 +1,7 @@
+-#include <stdlib.h>
+ #include <stdio.h>
+
+ int func(int num);
+ void print_int(int num);
+-void print_ln();
+
+ int main() {
+ int i;
+@@ -12,8 +10,6 @@
+ print_int(func(i));
+ }
+
+- print_ln();
+-
+ return 0;
+ }
+
+@@ -25,7 +21,3 @@
+ printf("%d", num);
+ }
+
+-void print_ln() {
+- printf("\n");
+-}
+-
diff --git a/t/t4109/patch4.patch b/t/t4109/patch4.patch
new file mode 100644
index 0000000..4b08590
--- /dev/null
+++ b/t/t4109/patch4.patch
@@ -0,0 +1,30 @@
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -1,13 +1,14 @@
+ #include <stdio.h>
+
+ int func(int num);
+-void print_int(int num);
++int func2(int num);
+
+ int main() {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+- print_int(func(i));
++ printf("%d", func(i));
++ printf("%d", func3(i));
+ }
+
+ return 0;
+@@ -17,7 +18,7 @@
+ return num * num;
+ }
+
+-void print_int(int num) {
+- printf("%d", num);
++int func2(int num) {
++ return num * num * num;
+ }
+
diff --git a/t/t4110-apply-scan.sh b/t/t4110-apply-scan.sh
new file mode 100755
index 0000000..09f5811
--- /dev/null
+++ b/t/t4110-apply-scan.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+# Copyright (c) 2005 Robert Fitzsimons
+#
+
+test_description='git apply test for patches which require scanning forwards and backwards.
+
+'
+. ./test-lib.sh
+
+test_expect_success 'git apply scan' '
+ git apply \
+ "$TEST_DIRECTORY/t4110/patch1.patch" \
+ "$TEST_DIRECTORY/t4110/patch2.patch" \
+ "$TEST_DIRECTORY/t4110/patch3.patch" \
+ "$TEST_DIRECTORY/t4110/patch4.patch" \
+ "$TEST_DIRECTORY/t4110/patch5.patch" &&
+ test_cmp new.txt "$TEST_DIRECTORY/t4110/expect"
+'
+
+test_done
diff --git a/t/t4110/expect b/t/t4110/expect
new file mode 100644
index 0000000..87cc493
--- /dev/null
+++ b/t/t4110/expect
@@ -0,0 +1,20 @@
+a1
+a11
+a111
+a1111
+b1
+b11
+b111
+b1111
+b2
+b22
+b222
+b2222
+c1
+c11
+c111
+c1111
+c2
+c22
+c222
+c2222
diff --git a/t/t4110/patch1.patch b/t/t4110/patch1.patch
new file mode 100644
index 0000000..5613908
--- /dev/null
+++ b/t/t4110/patch1.patch
@@ -0,0 +1,17 @@
+diff --git a/new.txt b/new.txt
+new file mode 100644
+--- /dev/null
++++ b/new.txt
+@@ -0,0 +1,12 @@
++a1
++a11
++a111
++a1111
++b1
++b11
++b111
++b1111
++c1
++c11
++c111
++c1111
diff --git a/t/t4110/patch2.patch b/t/t4110/patch2.patch
new file mode 100644
index 0000000..0497424
--- /dev/null
+++ b/t/t4110/patch2.patch
@@ -0,0 +1,11 @@
+diff --git a/new.txt b/new.txt
+--- a/new.txt
++++ b/new.txt
+@@ -1,7 +1,3 @@
+-a1
+-a11
+-a111
+-a1111
+ b1
+ b11
+ b111
diff --git a/t/t4110/patch3.patch b/t/t4110/patch3.patch
new file mode 100644
index 0000000..26bd442
--- /dev/null
+++ b/t/t4110/patch3.patch
@@ -0,0 +1,14 @@
+diff --git a/new.txt b/new.txt
+--- a/new.txt
++++ b/new.txt
+@@ -6,6 +6,10 @@
+ b11
+ b111
+ b1111
++b2
++b22
++b222
++b2222
+ c1
+ c11
+ c111
diff --git a/t/t4110/patch4.patch b/t/t4110/patch4.patch
new file mode 100644
index 0000000..9ffb9c2
--- /dev/null
+++ b/t/t4110/patch4.patch
@@ -0,0 +1,11 @@
+diff --git a/new.txt b/new.txt
+--- a/new.txt
++++ b/new.txt
+@@ -1,3 +1,7 @@
++a1
++a11
++a111
++a1111
+ b1
+ b11
+ b111
diff --git a/t/t4110/patch5.patch b/t/t4110/patch5.patch
new file mode 100644
index 0000000..c5ac691
--- /dev/null
+++ b/t/t4110/patch5.patch
@@ -0,0 +1,11 @@
+diff --git a/new.txt b/new.txt
+--- a/new.txt
++++ b/new.txt
+@@ -10,3 +10,7 @@
+ c11
+ c111
+ c1111
++c2
++c22
++c222
++c2222
diff --git a/t/t4112-apply-renames.sh b/t/t4112-apply-renames.sh
new file mode 100755
index 0000000..f9ad183
--- /dev/null
+++ b/t/t4112-apply-renames.sh
@@ -0,0 +1,144 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git apply should not get confused with rename/copy.
+
+'
+
+. ./test-lib.sh
+
+# setup
+
+mkdir -p klibc/arch/x86_64/include/klibc
+
+cat >klibc/arch/x86_64/include/klibc/archsetjmp.h <<\EOF
+/*
+ * arch/x86_64/include/klibc/archsetjmp.h
+ */
+
+#ifndef _KLIBC_ARCHSETJMP_H
+#define _KLIBC_ARCHSETJMP_H
+
+struct __jmp_buf {
+ unsigned long __rbx;
+ unsigned long __rsp;
+ unsigned long __rbp;
+ unsigned long __r12;
+ unsigned long __r13;
+ unsigned long __r14;
+ unsigned long __r15;
+ unsigned long __rip;
+};
+
+typedef struct __jmp_buf jmp_buf[1];
+
+#endif /* _SETJMP_H */
+EOF
+cat >klibc/README <<\EOF
+This is a simple readme file.
+EOF
+
+cat >patch <<\EOF
+diff --git a/klibc/arch/x86_64/include/klibc/archsetjmp.h b/include/arch/cris/klibc/archsetjmp.h
+similarity index 76%
+copy from klibc/arch/x86_64/include/klibc/archsetjmp.h
+copy to include/arch/cris/klibc/archsetjmp.h
+--- a/klibc/arch/x86_64/include/klibc/archsetjmp.h
++++ b/include/arch/cris/klibc/archsetjmp.h
+@@ -1,21 +1,24 @@
+ /*
+- * arch/x86_64/include/klibc/archsetjmp.h
++ * arch/cris/include/klibc/archsetjmp.h
+ */
+
+ #ifndef _KLIBC_ARCHSETJMP_H
+ #define _KLIBC_ARCHSETJMP_H
+
+ struct __jmp_buf {
+- unsigned long __rbx;
+- unsigned long __rsp;
+- unsigned long __rbp;
+- unsigned long __r12;
+- unsigned long __r13;
+- unsigned long __r14;
+- unsigned long __r15;
+- unsigned long __rip;
++ unsigned long __r0;
++ unsigned long __r1;
++ unsigned long __r2;
++ unsigned long __r3;
++ unsigned long __r4;
++ unsigned long __r5;
++ unsigned long __r6;
++ unsigned long __r7;
++ unsigned long __r8;
++ unsigned long __sp;
++ unsigned long __srp;
+ };
+
+ typedef struct __jmp_buf jmp_buf[1];
+
+-#endif /* _SETJMP_H */
++#endif /* _KLIBC_ARCHSETJMP_H */
+diff --git a/klibc/arch/x86_64/include/klibc/archsetjmp.h b/include/arch/m32r/klibc/archsetjmp.h
+similarity index 66%
+rename from klibc/arch/x86_64/include/klibc/archsetjmp.h
+rename to include/arch/m32r/klibc/archsetjmp.h
+--- a/klibc/arch/x86_64/include/klibc/archsetjmp.h
++++ b/include/arch/m32r/klibc/archsetjmp.h
+@@ -1,21 +1,21 @@
+ /*
+- * arch/x86_64/include/klibc/archsetjmp.h
++ * arch/m32r/include/klibc/archsetjmp.h
+ */
+
+ #ifndef _KLIBC_ARCHSETJMP_H
+ #define _KLIBC_ARCHSETJMP_H
+
+ struct __jmp_buf {
+- unsigned long __rbx;
+- unsigned long __rsp;
+- unsigned long __rbp;
++ unsigned long __r8;
++ unsigned long __r9;
++ unsigned long __r10;
++ unsigned long __r11;
+ unsigned long __r12;
+ unsigned long __r13;
+ unsigned long __r14;
+ unsigned long __r15;
+- unsigned long __rip;
+ };
+
+ typedef struct __jmp_buf jmp_buf[1];
+
+-#endif /* _SETJMP_H */
++#endif /* _KLIBC_ARCHSETJMP_H */
+diff --git a/klibc/README b/klibc/README
+--- a/klibc/README
++++ b/klibc/README
+@@ -1,1 +1,4 @@
+ This is a simple readme file.
++And we add a few
++lines at the
++end of it.
+diff --git a/klibc/README b/klibc/arch/README
+copy from klibc/README
+copy to klibc/arch/README
+--- a/klibc/README
++++ b/klibc/arch/README
+@@ -1,1 +1,3 @@
+ This is a simple readme file.
++And we copy it to one level down, and
++add a few lines at the end of it.
+EOF
+
+find klibc -type f -print | xargs git update-index --add --
+
+test_expect_success 'check rename/copy patch' 'git apply --check patch'
+
+test_expect_success 'apply rename/copy patch' 'git apply --index patch'
+
+test_done
diff --git a/t/t4113-apply-ending.sh b/t/t4113-apply-ending.sh
new file mode 100755
index 0000000..66fa515
--- /dev/null
+++ b/t/t4113-apply-ending.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Catalin Marinas
+#
+
+test_description='git apply trying to add an ending line.
+
+'
+. ./test-lib.sh
+
+# setup
+
+cat >test-patch <<\EOF
+diff --git a/file b/file
+--- a/file
++++ b/file
+@@ -1,2 +1,3 @@
+ a
+ b
++c
+EOF
+
+echo 'a' >file
+echo 'b' >>file
+echo 'c' >>file
+
+test_expect_success setup \
+ 'git update-index --add file'
+
+# test
+
+test_expect_success 'apply at the end' \
+ 'test_must_fail git apply --index test-patch'
+
+cat >test-patch <<\EOF
+diff a/file b/file
+--- a/file
++++ b/file
+@@ -1,2 +1,3 @@
++a
+ b
+ c
+EOF
+
+echo >file 'a
+b
+c'
+git update-index file
+
+test_expect_success 'apply at the beginning' \
+ 'test_must_fail git apply --index test-patch'
+
+test_done
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
new file mode 100755
index 0000000..164d58c
--- /dev/null
+++ b/t/t4114-apply-typechange.sh
@@ -0,0 +1,129 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='git apply should not get confused with type changes.
+
+'
+
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='Symbolic links not supported, skipping tests.'
+ test_done
+fi
+
+test_expect_success 'setup repository and commits' '
+ echo "hello world" > foo &&
+ echo "hi planet" > bar &&
+ git update-index --add foo bar &&
+ git commit -m initial &&
+ git branch initial &&
+ rm -f foo &&
+ ln -s bar foo &&
+ git update-index foo &&
+ git commit -m "foo symlinked to bar" &&
+ git branch foo-symlinked-to-bar &&
+ rm -f foo &&
+ echo "how far is the sun?" > foo &&
+ git update-index foo &&
+ git commit -m "foo back to file" &&
+ git branch foo-back-to-file &&
+ printf "\0" > foo &&
+ git update-index foo &&
+ git commit -m "foo becomes binary" &&
+ git branch foo-becomes-binary &&
+ rm -f foo &&
+ git update-index --remove foo &&
+ mkdir foo &&
+ echo "if only I knew" > foo/baz &&
+ git update-index --add foo/baz &&
+ git commit -m "foo becomes a directory" &&
+ git branch "foo-becomes-a-directory" &&
+ echo "hello world" > foo/baz &&
+ git update-index foo/baz &&
+ git commit -m "foo/baz is the original foo" &&
+ git branch foo-baz-renamed-from-foo
+ '
+
+test_expect_success 'file renamed from foo to foo/baz' '
+ git checkout -f initial &&
+ git diff-tree -M -p HEAD foo-baz-renamed-from-foo > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+
+test_expect_success 'file renamed from foo/baz to foo' '
+ git checkout -f foo-baz-renamed-from-foo &&
+ git diff-tree -M -p HEAD initial > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+
+test_expect_success 'directory becomes file' '
+ git checkout -f foo-becomes-a-directory &&
+ git diff-tree -p HEAD initial > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+
+test_expect_success 'file becomes directory' '
+ git checkout -f initial &&
+ git diff-tree -p HEAD foo-becomes-a-directory > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+
+test_expect_success 'file becomes symlink' '
+ git checkout -f initial &&
+ git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+
+test_expect_success 'symlink becomes file' '
+ git checkout -f foo-symlinked-to-bar &&
+ git diff-tree -p HEAD foo-back-to-file > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+test_expect_success 'binary file becomes symlink' '
+ git checkout -f foo-becomes-binary &&
+ git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+test_expect_success 'symlink becomes binary file' '
+ git checkout -f foo-symlinked-to-bar &&
+ git diff-tree -p --binary HEAD foo-becomes-binary > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+
+test_expect_success 'symlink becomes directory' '
+ git checkout -f foo-symlinked-to-bar &&
+ git diff-tree -p HEAD foo-becomes-a-directory > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+
+test_expect_success 'directory becomes symlink' '
+ git checkout -f foo-becomes-a-directory &&
+ git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+
+test_done
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
new file mode 100755
index 0000000..aff4348
--- /dev/null
+++ b/t/t4115-apply-symlink.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git apply symlinks and partial files
+
+'
+
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='Symbolic links not supported, skipping tests.'
+ test_done
+fi
+
+test_expect_success setup '
+
+ ln -s path1/path2/path3/path4/path5 link1 &&
+ git add link? &&
+ git commit -m initial &&
+
+ git branch side &&
+
+ rm -f link? &&
+
+ ln -s htap6 link1 &&
+ git update-index link? &&
+ git commit -m second &&
+
+ git diff-tree -p HEAD^ HEAD >patch &&
+ git apply --stat --summary patch
+
+'
+
+test_expect_success 'apply symlink patch' '
+
+ git checkout side &&
+ git apply patch &&
+ git diff-files -p >patched &&
+ test_cmp patch patched
+
+'
+
+test_expect_success 'apply --index symlink patch' '
+
+ git checkout -f side &&
+ git apply --index patch &&
+ git diff-index --cached -p HEAD >patched &&
+ test_cmp patch patched
+
+'
+
+test_done
diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
new file mode 100755
index 0000000..2298ece
--- /dev/null
+++ b/t/t4116-apply-reverse.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git apply in reverse
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ for i in a b c d e f g h i j k l m n; do echo $i; done >file1 &&
+ perl -pe "y/ijk/\\000\\001\\002/" <file1 >file2 &&
+
+ git add file1 file2 &&
+ git commit -m initial &&
+ git tag initial &&
+
+ for i in a b c g h i J K L m o n p q; do echo $i; done >file1 &&
+ perl -pe "y/mon/\\000\\001\\002/" <file1 >file2 &&
+
+ git commit -a -m second &&
+ git tag second &&
+
+ git diff --binary initial second >patch
+
+'
+
+test_expect_success 'apply in forward' '
+
+ T0=`git rev-parse "second^{tree}"` &&
+ git reset --hard initial &&
+ git apply --index --binary patch &&
+ T1=`git write-tree` &&
+ test "$T0" = "$T1"
+'
+
+test_expect_success 'apply in reverse' '
+
+ git reset --hard second &&
+ git apply --reverse --binary --index patch &&
+ git diff >diff &&
+ test_cmp /dev/null diff
+
+'
+
+test_expect_success 'setup separate repository lacking postimage' '
+
+ git tar-tree initial initial | $TAR xf - &&
+ (
+ cd initial && git init && git add .
+ ) &&
+
+ git tar-tree second second | $TAR xf - &&
+ (
+ cd second && git init && git add .
+ )
+
+'
+
+test_expect_success 'apply in forward without postimage' '
+
+ T0=`git rev-parse "second^{tree}"` &&
+ (
+ cd initial &&
+ git apply --index --binary ../patch &&
+ T1=`git write-tree` &&
+ test "$T0" = "$T1"
+ )
+'
+
+test_expect_success 'apply in reverse without postimage' '
+
+ T0=`git rev-parse "initial^{tree}"` &&
+ (
+ cd second &&
+ git apply --index --binary --reverse ../patch &&
+ T1=`git write-tree` &&
+ test "$T0" = "$T1"
+ )
+'
+
+test_expect_success 'reversing a whitespace introduction' '
+ sed "s/a/a /" < file1 > file1.new &&
+ mv file1.new file1 &&
+ git diff | git apply --reverse --whitespace=error
+'
+
+test_done
diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
new file mode 100755
index 0000000..e9ccd16
--- /dev/null
+++ b/t/t4117-apply-reject.sh
@@ -0,0 +1,157 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git apply with rejects
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
+ do
+ echo $i
+ done >file1 &&
+ cat file1 >saved.file1 &&
+ git update-index --add file1 &&
+ git commit -m initial &&
+
+ for i in 1 2 A B 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 D 21
+ do
+ echo $i
+ done >file1 &&
+ git diff >patch.1 &&
+ cat file1 >clean &&
+
+ for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 C 13 14 15 16 17 18 19 20 F 21
+ do
+ echo $i
+ done >expected &&
+
+ mv file1 file2 &&
+ git update-index --add --remove file1 file2 &&
+ git diff -M HEAD >patch.2 &&
+
+ rm -f file1 file2 &&
+ mv saved.file1 file1 &&
+ git update-index --add --remove file1 file2 &&
+
+ for i in 1 E 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 F 21
+ do
+ echo $i
+ done >file1 &&
+
+ cat file1 >saved.file1
+'
+
+test_expect_success 'apply without --reject should fail' '
+
+ if git apply patch.1
+ then
+ echo "Eh? Why?"
+ exit 1
+ fi
+
+ test_cmp file1 saved.file1
+'
+
+test_expect_success 'apply without --reject should fail' '
+
+ if git apply --verbose patch.1
+ then
+ echo "Eh? Why?"
+ exit 1
+ fi
+
+ test_cmp file1 saved.file1
+'
+
+test_expect_success 'apply with --reject should fail but update the file' '
+
+ cat saved.file1 >file1 &&
+ rm -f file1.rej file2.rej &&
+
+ if git apply --reject patch.1
+ then
+ echo "succeeds with --reject?"
+ exit 1
+ fi
+
+ test_cmp file1 expected &&
+
+ cat file1.rej &&
+
+ if test -f file2.rej
+ then
+ echo "file2 should not have been touched"
+ exit 1
+ fi
+'
+
+test_expect_success 'apply with --reject should fail but update the file' '
+
+ cat saved.file1 >file1 &&
+ rm -f file1.rej file2.rej file2 &&
+
+ if git apply --reject patch.2 >rejects
+ then
+ echo "succeeds with --reject?"
+ exit 1
+ fi
+
+ test -f file1 && {
+ echo "file1 still exists?"
+ exit 1
+ }
+ test_cmp file2 expected &&
+
+ cat file2.rej &&
+
+ if test -f file1.rej
+ then
+ echo "file2 should not have been touched"
+ exit 1
+ fi
+
+'
+
+test_expect_success 'the same test with --verbose' '
+
+ cat saved.file1 >file1 &&
+ rm -f file1.rej file2.rej file2 &&
+
+ if git apply --reject --verbose patch.2 >rejects
+ then
+ echo "succeeds with --reject?"
+ exit 1
+ fi
+
+ test -f file1 && {
+ echo "file1 still exists?"
+ exit 1
+ }
+ test_cmp file2 expected &&
+
+ cat file2.rej &&
+
+ if test -f file1.rej
+ then
+ echo "file2 should not have been touched"
+ exit 1
+ fi
+
+'
+
+test_expect_success 'apply cleanly with --verbose' '
+
+ git cat-file -p HEAD:file1 >file1 &&
+ rm -f file?.rej file2 &&
+
+ git apply --verbose patch.1 &&
+
+ test_cmp file1 clean
+'
+
+test_done
diff --git a/t/t4118-apply-empty-context.sh b/t/t4118-apply-empty-context.sh
new file mode 100755
index 0000000..65f2e4c
--- /dev/null
+++ b/t/t4118-apply-empty-context.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git apply with new style GNU diff with empty context
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ {
+ echo; echo;
+ echo A; echo B; echo C;
+ echo;
+ } >file1 &&
+ cat file1 >file1.orig &&
+ {
+ cat file1 &&
+ echo Q | tr -d "\\012"
+ } >file2 &&
+ cat file2 >file2.orig &&
+ git add file1 file2 &&
+ sed -e "/^B/d" <file1.orig >file1 &&
+ cat file1 > file2 &&
+ echo Q | tr -d "\\012" >>file2 &&
+ cat file1 >file1.mods &&
+ cat file2 >file2.mods &&
+ git diff |
+ sed -e "s/^ \$//" >diff.output
+'
+
+test_expect_success 'apply --numstat' '
+
+ git apply --numstat diff.output >actual &&
+ {
+ echo "0 1 file1" &&
+ echo "0 1 file2"
+ } >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'apply --apply' '
+
+ cat file1.orig >file1 &&
+ cat file2.orig >file2 &&
+ git update-index file1 file2 &&
+ git apply --index diff.output &&
+ test_cmp file1.mods file1 &&
+ test_cmp file2.mods file2
+'
+
+test_done
diff --git a/t/t4119-apply-config.sh b/t/t4119-apply-config.sh
new file mode 100755
index 0000000..3c73a78
--- /dev/null
+++ b/t/t4119-apply-config.sh
@@ -0,0 +1,162 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Junio C Hamano
+#
+
+test_description='git apply --whitespace=strip and configuration file.
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir sub &&
+ echo A >sub/file1 &&
+ cp sub/file1 saved &&
+ git add sub/file1 &&
+ echo "B " >sub/file1 &&
+ git diff >patch.file
+'
+
+# Also handcraft GNU diff output; note this has trailing whitespace.
+tr '_' ' ' >gpatch.file <<\EOF &&
+--- file1 2007-02-21 01:04:24.000000000 -0800
++++ file1+ 2007-02-21 01:07:44.000000000 -0800
+@@ -1 +1 @@
+-A
++B_
+EOF
+
+sed -e 's|file1|sub/&|' gpatch.file >gpatch-sub.file &&
+sed -e '
+ /^--- /s|file1|a/sub/&|
+ /^+++ /s|file1|b/sub/&|
+' gpatch.file >gpatch-ab-sub.file &&
+
+check_result () {
+ if grep " " "$1"
+ then
+ echo "Eh?"
+ false
+ elif grep B "$1"
+ then
+ echo Happy
+ else
+ echo "Huh?"
+ false
+ fi
+}
+
+test_expect_success 'apply --whitespace=strip' '
+
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ git apply --whitespace=strip patch.file &&
+ check_result sub/file1
+'
+
+test_expect_success 'apply --whitespace=strip from config' '
+
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ git config apply.whitespace strip &&
+ git apply patch.file &&
+ check_result sub/file1
+'
+
+D=`pwd`
+
+test_expect_success 'apply --whitespace=strip in subdir' '
+
+ cd "$D" &&
+ git config --unset-all apply.whitespace
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ cd sub &&
+ git apply --whitespace=strip ../patch.file &&
+ check_result file1
+'
+
+test_expect_success 'apply --whitespace=strip from config in subdir' '
+
+ cd "$D" &&
+ git config apply.whitespace strip &&
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ cd sub &&
+ git apply ../patch.file &&
+ check_result file1
+'
+
+test_expect_success 'same in subdir but with traditional patch input' '
+
+ cd "$D" &&
+ git config apply.whitespace strip &&
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ cd sub &&
+ git apply ../gpatch.file &&
+ check_result file1
+'
+
+test_expect_success 'same but with traditional patch input of depth 1' '
+
+ cd "$D" &&
+ git config apply.whitespace strip &&
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ cd sub &&
+ git apply ../gpatch-sub.file &&
+ check_result file1
+'
+
+test_expect_success 'same but with traditional patch input of depth 2' '
+
+ cd "$D" &&
+ git config apply.whitespace strip &&
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ cd sub &&
+ git apply ../gpatch-ab-sub.file &&
+ check_result file1
+'
+
+test_expect_success 'same but with traditional patch input of depth 1' '
+
+ cd "$D" &&
+ git config apply.whitespace strip &&
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ git apply -p0 gpatch-sub.file &&
+ check_result sub/file1
+'
+
+test_expect_success 'same but with traditional patch input of depth 2' '
+
+ cd "$D" &&
+ git config apply.whitespace strip &&
+ rm -f sub/file1 &&
+ cp saved sub/file1 &&
+ git update-index --refresh &&
+
+ git apply gpatch-ab-sub.file &&
+ check_result sub/file1
+'
+
+test_done
diff --git a/t/t4120-apply-popt.sh b/t/t4120-apply-popt.sh
new file mode 100755
index 0000000..b463b4f
--- /dev/null
+++ b/t/t4120-apply-popt.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Shawn O. Pearce
+#
+
+test_description='git apply -p handling.'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir sub &&
+ echo A >sub/file1 &&
+ cp sub/file1 file1 &&
+ git add sub/file1 &&
+ echo B >sub/file1 &&
+ git diff >patch.file &&
+ rm sub/file1 &&
+ rmdir sub
+'
+
+test_expect_success 'apply git diff with -p2' '
+ git apply -p2 patch.file
+'
+
+test_expect_success 'apply with too large -p' '
+ test_must_fail git apply --stat -p3 patch.file 2>err &&
+ grep "removing 3 leading" err
+'
+
+test_done
diff --git a/t/t4121-apply-diffs.sh b/t/t4121-apply-diffs.sh
new file mode 100755
index 0000000..aff551a
--- /dev/null
+++ b/t/t4121-apply-diffs.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='git apply for contextually independent diffs'
+. ./test-lib.sh
+
+echo '1
+2
+3
+4
+5
+6
+7
+8' >file
+
+test_expect_success 'setup' \
+ 'git add file &&
+ git commit -q -m 1 &&
+ git checkout -b test &&
+ mv file file.tmp &&
+ echo 0 >file &&
+ cat file.tmp >>file &&
+ rm file.tmp &&
+ git commit -a -q -m 2 &&
+ echo 9 >>file &&
+ git commit -a -q -m 3 &&
+ git checkout master'
+
+test_expect_success \
+ 'check if contextually independent diffs for the same file apply' \
+ '( git diff test~2 test~1; git diff test~1 test~0 )| git apply'
+
+test_done
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
new file mode 100755
index 0000000..923fcab
--- /dev/null
+++ b/t/t4122-apply-symlink-inside.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+test_description='apply to deeper directory without getting fooled with symlink'
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='Symbolic links not supported, skipping tests.'
+ test_done
+fi
+
+lecho () {
+ for l_
+ do
+ echo "$l_"
+ done
+}
+
+test_expect_success setup '
+
+ mkdir -p arch/i386/boot arch/x86_64 &&
+ lecho 1 2 3 4 5 >arch/i386/boot/Makefile &&
+ ln -s ../i386/boot arch/x86_64/boot &&
+ git add . &&
+ test_tick &&
+ git commit -m initial &&
+ git branch test &&
+
+ rm arch/x86_64/boot &&
+ mkdir arch/x86_64/boot &&
+ lecho 2 3 4 5 6 >arch/x86_64/boot/Makefile &&
+ git add . &&
+ test_tick &&
+ git commit -a -m second &&
+
+ git format-patch --binary -1 --stdout >test.patch
+
+'
+
+test_expect_success apply '
+
+ git checkout test &&
+ git diff --exit-code test &&
+ git diff --exit-code --cached test &&
+ git apply --index test.patch
+
+'
+
+test_expect_success 'check result' '
+
+ git diff --exit-code master &&
+ git diff --exit-code --cached master &&
+ test_tick &&
+ git commit -m replay &&
+ T1=$(git rev-parse "master^{tree}") &&
+ T2=$(git rev-parse "HEAD^{tree}") &&
+ test "z$T1" = "z$T2"
+
+'
+
+test_done
diff --git a/t/t4123-apply-shrink.sh b/t/t4123-apply-shrink.sh
new file mode 100755
index 0000000..984157f
--- /dev/null
+++ b/t/t4123-apply-shrink.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='apply a patch that is larger than the preimage'
+
+. ./test-lib.sh
+
+cat >F <<\EOF
+1
+2
+3
+4
+5
+6
+7
+8
+999999
+A
+B
+C
+D
+E
+F
+G
+H
+I
+J
+
+EOF
+
+test_expect_success setup '
+
+ git add F &&
+ mv F G &&
+ sed -e "s/1/11/" -e "s/999999/9/" -e "s/H/HH/" <G >F &&
+ git diff >patch &&
+ sed -e "/^\$/d" <G >F &&
+ git add F
+
+'
+
+test_expect_success 'apply should fail gracefully' '
+
+ if git apply --index patch
+ then
+ echo Oops, should not have succeeded
+ false
+ else
+ status=$?
+ echo "Status was $status"
+ if test -f .git/index.lock
+ then
+ echo Oops, should not have crashed
+ false
+ fi
+ fi
+'
+
+test_done
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
new file mode 100755
index 0000000..8a676a5
--- /dev/null
+++ b/t/t4124-apply-ws-rule.sh
@@ -0,0 +1,451 @@
+#!/bin/sh
+
+test_description='core.whitespace rules and git apply'
+
+. ./test-lib.sh
+
+prepare_test_file () {
+
+ # A line that has character X is touched iff RULE is in effect:
+ # X RULE
+ # ! trailing-space
+ # @ space-before-tab
+ # # indent-with-non-tab
+ # % tab-in-indent
+ sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF
+ An_SP in an ordinary line>and a HT.
+ >A HT (%).
+ _>A SP and a HT (@%).
+ _>_A SP, a HT and a SP (@%).
+ _______Seven SP.
+ ________Eight SP (#).
+ _______>Seven SP and a HT (@%).
+ ________>Eight SP and a HT (@#%).
+ _______>_Seven SP, a HT and a SP (@%).
+ ________>_Eight SP, a HT and a SP (@#%).
+ _______________Fifteen SP (#).
+ _______________>Fifteen SP and a HT (@#%).
+ ________________Sixteen SP (#).
+ ________________>Sixteen SP and a HT (@#%).
+ _____a__Five SP, a non WS, two SP.
+ A line with a (!) trailing SP_
+ A line with a (!) trailing HT>
+ EOF
+}
+
+apply_patch () {
+ >target &&
+ sed -e "s|\([ab]\)/file|\1/target|" <patch |
+ git apply "$@"
+}
+
+test_fix () {
+ # fix should not barf
+ apply_patch --whitespace=fix || return 1
+
+ # find touched lines
+ $DIFF file target | sed -n -e "s/^> //p" >fixed
+
+ # the changed lines are all expeced to change
+ fixed_cnt=$(wc -l <fixed)
+ case "$1" in
+ '') expect_cnt=$fixed_cnt ;;
+ ?*) expect_cnt=$(grep "[$1]" <fixed | wc -l) ;;
+ esac
+ test $fixed_cnt -eq $expect_cnt || return 1
+
+ # and we are not missing anything
+ case "$1" in
+ '') expect_cnt=0 ;;
+ ?*) expect_cnt=$(grep "[$1]" <file | wc -l) ;;
+ esac
+ test $fixed_cnt -eq $expect_cnt || return 1
+
+ # Get the patch actually applied
+ git diff-files -p target >fixed-patch
+ test -s fixed-patch && return 0
+
+ # Make sure it is complaint-free
+ >target
+ git apply --whitespace=error-all <fixed-patch
+
+}
+
+test_expect_success setup '
+
+ >file &&
+ git add file &&
+ prepare_test_file >file &&
+ git diff-files -p >patch &&
+ >target &&
+ git add target
+
+'
+
+test_expect_success 'whitespace=nowarn, default rule' '
+
+ apply_patch --whitespace=nowarn &&
+ test_cmp file target
+
+'
+
+test_expect_success 'whitespace=warn, default rule' '
+
+ apply_patch --whitespace=warn &&
+ test_cmp file target
+
+'
+
+test_expect_success 'whitespace=error-all, default rule' '
+
+ apply_patch --whitespace=error-all && return 1
+ test -s target && return 1
+ : happy
+
+'
+
+test_expect_success 'whitespace=error-all, no rule' '
+
+ git config core.whitespace -trailing,-space-before,-indent &&
+ apply_patch --whitespace=error-all &&
+ test_cmp file target
+
+'
+
+test_expect_success 'whitespace=error-all, no rule (attribute)' '
+
+ git config --unset core.whitespace &&
+ echo "target -whitespace" >.gitattributes &&
+ apply_patch --whitespace=error-all &&
+ test_cmp file target
+
+'
+
+for t in - ''
+do
+ case "$t" in '') tt='!' ;; *) tt= ;; esac
+ for s in - ''
+ do
+ case "$s" in '') ts='@' ;; *) ts= ;; esac
+ for i in - ''
+ do
+ case "$i" in '') ti='#' ;; *) ti= ;; esac
+ for h in - ''
+ do
+ [ -z "$h$i" ] && continue
+ case "$h" in '') th='%' ;; *) th= ;; esac
+ rule=${t}trailing,${s}space,${i}indent,${h}tab
+
+ rm -f .gitattributes
+ test_expect_success "rule=$rule" '
+ git config core.whitespace "$rule" &&
+ test_fix "$tt$ts$ti$th"
+ '
+
+ test_expect_success "rule=$rule (attributes)" '
+ git config --unset core.whitespace &&
+ echo "target whitespace=$rule" >.gitattributes &&
+ test_fix "$tt$ts$ti$th"
+ '
+
+ done
+ done
+ done
+done
+
+create_patch () {
+ sed -e "s/_/ /" <<-\EOF
+ diff --git a/target b/target
+ index e69de29..8bd6648 100644
+ --- a/target
+ +++ b/target
+ @@ -0,0 +1,3 @@
+ +An empty line follows
+ +
+ +A line with trailing whitespace and no newline_
+ \ No newline at end of file
+ EOF
+}
+
+test_expect_success 'trailing whitespace & no newline at the end of file' '
+ >target &&
+ create_patch >patch-file &&
+ git apply --whitespace=fix patch-file &&
+ grep "newline$" target &&
+ grep "^$" target
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (1)' '
+ : these can fail depending on what we did before
+ git config --unset core.whitespace
+ rm -f .gitattributes
+
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ { echo a; echo b; echo c; } >expect &&
+ { cat expect; echo; } >one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=fix patch &&
+ test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (2)' '
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ { echo a; echo c; } >expect &&
+ { cat expect; echo; echo; } >one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=fix patch &&
+ test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (3)' '
+ { echo a; echo b; echo; } >one &&
+ git add one &&
+ { echo a; echo c; echo; } >expect &&
+ { cat expect; echo; echo; } >one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=fix patch &&
+ test_cmp expect one
+'
+
+test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
+ { echo a; echo b; echo; echo; echo; echo; echo; echo d; } >one &&
+ git add one &&
+ { echo a; echo c; echo; echo; echo; echo; echo; echo; echo d; } >expect &&
+ cp expect one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=fix patch &&
+ test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=warn' '
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ echo >>one &&
+ cat one >expect &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=warn patch 2>error &&
+ test_cmp expect one &&
+ grep "new blank line at EOF" error
+'
+
+test_expect_success 'blank at EOF with --whitespace=error' '
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ cat one >expect &&
+ echo >>one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ test_must_fail git apply --whitespace=error patch 2>error &&
+ test_cmp expect one &&
+ grep "new blank line at EOF" error
+'
+
+test_expect_success 'blank but not empty at EOF' '
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ echo " " >>one &&
+ cat one >expect &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=warn patch 2>error &&
+ test_cmp expect one &&
+ grep "new blank line at EOF" error
+'
+
+test_expect_success 'applying beyond EOF requires one non-blank context line' '
+ { echo; echo; echo; echo; } >one &&
+ git add one &&
+ { echo b; } >>one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ { echo a; echo; } >one &&
+ cp one expect &&
+ test_must_fail git apply --whitespace=fix patch &&
+ test_cmp one expect &&
+ test_must_fail git apply --ignore-space-change --whitespace=fix patch &&
+ test_cmp one expect
+'
+
+test_expect_success 'tons of blanks at EOF should not apply' '
+ for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
+ echo; echo; echo; echo;
+ done >one &&
+ git add one &&
+ echo a >>one &&
+ git diff -- one >patch &&
+
+ >one &&
+ test_must_fail git apply --whitespace=fix patch &&
+ test_must_fail git apply --ignore-space-change --whitespace=fix patch
+'
+
+test_expect_success 'missing blank line at end with --whitespace=fix' '
+ echo a >one &&
+ echo >>one &&
+ git add one &&
+ echo b >>one &&
+ cp one expect &&
+ git diff -- one >patch &&
+ echo a >one &&
+ cp one saved-one &&
+ test_must_fail git apply patch &&
+ git apply --whitespace=fix patch &&
+ test_cmp one expect &&
+ mv saved-one one &&
+ git apply --ignore-space-change --whitespace=fix patch &&
+ test_cmp one expect
+'
+
+test_expect_success 'two missing blank lines at end with --whitespace=fix' '
+ { echo a; echo; echo b; echo c; } >one &&
+ cp one no-blank-lines &&
+ { echo; echo; } >>one &&
+ git add one &&
+ echo d >>one &&
+ cp one expect &&
+ echo >>one &&
+ git diff -- one >patch &&
+ cp no-blank-lines one &&
+ test_must_fail git apply patch &&
+ git apply --whitespace=fix patch &&
+ test_cmp one expect &&
+ mv no-blank-lines one &&
+ test_must_fail git apply patch &&
+ git apply --ignore-space-change --whitespace=fix patch &&
+ test_cmp one expect
+'
+
+test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '
+ { echo a; echo; } >one &&
+ git add one &&
+ { echo b; echo a; echo; } >one &&
+ cp one expect &&
+ git diff -- one >patch &&
+ echo a >one &&
+ test_must_fail git apply patch &&
+ git apply --whitespace=fix patch &&
+ test_cmp one expect
+'
+
+test_expect_success 'shrink file with tons of missing blanks at end of file' '
+ { echo a; echo b; echo c; } >one &&
+ cp one no-blank-lines &&
+ for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
+ echo; echo; echo; echo;
+ done >>one &&
+ git add one &&
+ echo a >one &&
+ cp one expect &&
+ git diff -- one >patch &&
+ cp no-blank-lines one &&
+ test_must_fail git apply patch &&
+ git apply --whitespace=fix patch &&
+ test_cmp one expect &&
+ mv no-blank-lines one &&
+ git apply --ignore-space-change --whitespace=fix patch &&
+ test_cmp one expect
+'
+
+test_expect_success 'missing blanks at EOF must only match blank lines' '
+ { echo a; echo b; } >one &&
+ git add one &&
+ { echo c; echo d; } >>one &&
+ git diff -- one >patch &&
+
+ echo a >one &&
+ test_must_fail git apply patch
+ test_must_fail git apply --whitespace=fix patch &&
+ test_must_fail git apply --ignore-space-change --whitespace=fix patch
+'
+
+sed -e's/Z//' >one <<EOF
+a
+b
+c
+ Z
+EOF
+
+test_expect_success 'missing blank line should match context line with spaces' '
+ git add one &&
+ echo d >>one &&
+ git diff -- one >patch &&
+ { echo a; echo b; echo c; } >one &&
+ cp one expect &&
+ { echo; echo d; } >>expect &&
+ git add one &&
+
+ git apply --whitespace=fix patch &&
+ test_cmp one expect
+'
+
+sed -e's/Z//' >one <<EOF
+a
+b
+c
+ Z
+EOF
+
+test_expect_success 'same, but with the --ignore-space-option' '
+ git add one &&
+ echo d >>one &&
+ cp one expect &&
+ git diff -- one >patch &&
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+
+ git checkout-index -f one &&
+ git apply --ignore-space-change --whitespace=fix patch &&
+ test_cmp one expect
+'
+
+test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
+ git config core.whitespace cr-at-eol &&
+ printf "a\r\n" >one &&
+ printf "b\r\n" >>one &&
+ printf "c\r\n" >>one &&
+ cp one save-one &&
+ printf " \r\n" >>one
+ git add one &&
+ printf "d\r\n" >>one &&
+ cp one expect &&
+ git diff -- one >patch &&
+ mv save-one one &&
+
+ git apply --ignore-space-change --whitespace=fix patch &&
+ test_cmp one expect
+'
+
+test_expect_success 'same, but with CR-LF line endings && cr-at-eol unset' '
+ git config --unset core.whitespace &&
+ printf "a\r\n" >one &&
+ printf "b\r\n" >>one &&
+ printf "c\r\n" >>one &&
+ cp one save-one &&
+ printf " \r\n" >>one
+ git add one &&
+ cp one expect &&
+ printf "d\r\n" >>one &&
+ git diff -- one >patch &&
+ mv save-one one &&
+ echo d >>expect &&
+
+ git apply --ignore-space-change --whitespace=fix patch &&
+ test_cmp one expect
+'
+
+test_done
diff --git a/t/t4125-apply-ws-fuzz.sh b/t/t4125-apply-ws-fuzz.sh
new file mode 100755
index 0000000..9671de7
--- /dev/null
+++ b/t/t4125-apply-ws-fuzz.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='applying patch that has broken whitespaces in context'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >file &&
+ git add file &&
+
+ # file-0 is full of whitespace breakages
+ for l in a bb c d eeee f ggg h
+ do
+ echo "$l "
+ done >file-0 &&
+
+ # patch-0 creates a whitespace broken file
+ cat file-0 >file &&
+ git diff >patch-0 &&
+ git add file &&
+
+ # file-1 is still full of whitespace breakages,
+ # but has one line updated, without fixing any
+ # whitespaces.
+ # patch-1 records that change.
+ sed -e "s/d/D/" file-0 >file-1 &&
+ cat file-1 >file &&
+ git diff >patch-1 &&
+
+ # patch-all is the effect of both patch-0 and patch-1
+ >file &&
+ git add file &&
+ cat file-1 >file &&
+ git diff >patch-all &&
+
+ # patch-2 is the same as patch-1 but is based
+ # on a version that already has whitespace fixed,
+ # and does not introduce whitespace breakages.
+ sed -e "s/ \$//" patch-1 >patch-2 &&
+
+ # If all whitespace breakages are fixed the contents
+ # should look like file-fixed
+ sed -e "s/ \$//" file-1 >file-fixed
+
+'
+
+test_expect_success nofix '
+
+ >file &&
+ git add file &&
+
+ # Baseline. Applying without fixing any whitespace
+ # breakages.
+ git apply --whitespace=nowarn patch-0 &&
+ git apply --whitespace=nowarn patch-1 &&
+
+ # The result should obviously match.
+ test_cmp file-1 file
+'
+
+test_expect_success 'withfix (forward)' '
+
+ >file &&
+ git add file &&
+
+ # The first application will munge the context lines
+ # the second patch depends on. We should be able to
+ # adjust and still apply.
+ git apply --whitespace=fix patch-0 &&
+ git apply --whitespace=fix patch-1 &&
+
+ test_cmp file-fixed file
+'
+
+test_expect_success 'withfix (backward)' '
+
+ >file &&
+ git add file &&
+
+ # Now we have a whitespace breakages on our side.
+ git apply --whitespace=nowarn patch-0 &&
+
+ # And somebody sends in a patch based on image
+ # with whitespace already fixed.
+ git apply --whitespace=fix patch-2 &&
+
+ # The result should accept the whitespace fixed
+ # postimage. But the line with "h" is beyond context
+ # horizon and left unfixed.
+
+ sed -e /h/d file-fixed >fixed-head &&
+ sed -e /h/d file >file-head &&
+ test_cmp fixed-head file-head &&
+
+ sed -n -e /h/p file-fixed >fixed-tail &&
+ sed -n -e /h/p file >file-tail &&
+
+ ! test_cmp fixed-tail file-tail
+
+'
+
+test_done
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
new file mode 100755
index 0000000..ceb6a79
--- /dev/null
+++ b/t/t4126-apply-empty.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+test_description='apply empty'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ >empty &&
+ git add empty &&
+ test_tick &&
+ git commit -m initial &&
+ for i in a b c d e
+ do
+ echo $i
+ done >empty &&
+ cat empty >expect &&
+ git diff |
+ sed -e "/^diff --git/d" \
+ -e "/^index /d" \
+ -e "s|a/empty|empty.orig|" \
+ -e "s|b/empty|empty|" >patch0 &&
+ sed -e "s|empty|missing|" patch0 >patch1 &&
+ >empty &&
+ git update-index --refresh
+'
+
+test_expect_success 'apply empty' '
+ git reset --hard &&
+ rm -f missing &&
+ git apply patch0 &&
+ test_cmp expect empty
+'
+
+test_expect_success 'apply --index empty' '
+ git reset --hard &&
+ rm -f missing &&
+ git apply --index patch0 &&
+ test_cmp expect empty &&
+ git diff --exit-code
+'
+
+test_expect_success 'apply create' '
+ git reset --hard &&
+ rm -f missing &&
+ git apply patch1 &&
+ test_cmp expect missing
+'
+
+test_expect_success 'apply --index create' '
+ git reset --hard &&
+ rm -f missing &&
+ git apply --index patch1 &&
+ test_cmp expect missing &&
+ git diff --exit-code
+'
+
+test_done
diff --git a/t/t4127-apply-same-fn.sh b/t/t4127-apply-same-fn.sh
new file mode 100755
index 0000000..77200c0
--- /dev/null
+++ b/t/t4127-apply-same-fn.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='apply same filename'
+
+. ./test-lib.sh
+
+modify () {
+ sed -e "$1" < "$2" > "$2".x &&
+ mv "$2".x "$2"
+}
+
+test_expect_success setup '
+ for i in a b c d e f g h i j k l m
+ do
+ echo $i
+ done >same_fn &&
+ cp same_fn other_fn &&
+ git add same_fn other_fn &&
+ git commit -m initial
+'
+test_expect_success 'apply same filename with independent changes' '
+ modify "s/^d/z/" same_fn &&
+ git diff > patch0 &&
+ git add same_fn &&
+ modify "s/^i/y/" same_fn &&
+ git diff >> patch0 &&
+ cp same_fn same_fn2 &&
+ git reset --hard &&
+ git apply patch0 &&
+ test_cmp same_fn same_fn2
+'
+
+test_expect_success 'apply same filename with overlapping changes' '
+ git reset --hard
+ modify "s/^d/z/" same_fn &&
+ git diff > patch0 &&
+ git add same_fn &&
+ modify "s/^e/y/" same_fn &&
+ git diff >> patch0 &&
+ cp same_fn same_fn2 &&
+ git reset --hard &&
+ git apply patch0 &&
+ test_cmp same_fn same_fn2
+'
+
+test_expect_success 'apply same new filename after rename' '
+ git reset --hard
+ git mv same_fn new_fn
+ modify "s/^d/z/" new_fn &&
+ git add new_fn &&
+ git diff -M --cached > patch1 &&
+ modify "s/^e/y/" new_fn &&
+ git diff >> patch1 &&
+ cp new_fn new_fn2 &&
+ git reset --hard &&
+ git apply --index patch1 &&
+ test_cmp new_fn new_fn2
+'
+
+test_expect_success 'apply same old filename after rename -- should fail.' '
+ git reset --hard
+ git mv same_fn new_fn
+ modify "s/^d/z/" new_fn &&
+ git add new_fn &&
+ git diff -M --cached > patch1 &&
+ git mv new_fn same_fn
+ modify "s/^e/y/" same_fn &&
+ git diff >> patch1 &&
+ git reset --hard &&
+ test_must_fail git apply patch1
+'
+
+test_expect_success 'apply A->B (rename), C->A (rename), A->A -- should pass.' '
+ git reset --hard
+ git mv same_fn new_fn
+ modify "s/^d/z/" new_fn &&
+ git add new_fn &&
+ git diff -M --cached > patch1 &&
+ git commit -m "a rename" &&
+ git mv other_fn same_fn
+ modify "s/^e/y/" same_fn &&
+ git add same_fn &&
+ git diff -M --cached >> patch1 &&
+ modify "s/^g/x/" same_fn &&
+ git diff >> patch1 &&
+ git reset --hard HEAD^ &&
+ git apply patch1
+'
+
+test_done
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
new file mode 100755
index 0000000..6cc741a
--- /dev/null
+++ b/t/t4128-apply-root.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+
+test_description='apply same filename'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ mkdir -p some/sub/dir &&
+ echo Hello > some/sub/dir/file &&
+ git add some/sub/dir/file &&
+ git commit -m initial &&
+ git tag initial
+
+'
+
+cat > patch << EOF
+diff a/bla/blub/dir/file b/bla/blub/dir/file
+--- a/bla/blub/dir/file
++++ b/bla/blub/dir/file
+@@ -1,1 +1,1 @@
+-Hello
++Bello
+EOF
+
+test_expect_success 'apply --directory -p (1)' '
+
+ git apply --directory=some/sub -p3 --index patch &&
+ test Bello = $(git show :some/sub/dir/file) &&
+ test Bello = $(cat some/sub/dir/file)
+
+'
+
+test_expect_success 'apply --directory -p (2) ' '
+
+ git reset --hard initial &&
+ git apply --directory=some/sub/ -p3 --index patch &&
+ test Bello = $(git show :some/sub/dir/file) &&
+ test Bello = $(cat some/sub/dir/file)
+
+'
+
+cat > patch << EOF
+diff --git a/newfile b/newfile
+new file mode 100644
+index 0000000..d95f3ad
+--- /dev/null
++++ b/newfile
+@@ -0,0 +1 @@
++content
+EOF
+
+test_expect_success 'apply --directory (new file)' '
+ git reset --hard initial &&
+ git apply --directory=some/sub/dir/ --index patch &&
+ test content = $(git show :some/sub/dir/newfile) &&
+ test content = $(cat some/sub/dir/newfile)
+'
+
+cat > patch << EOF
+diff --git a/c/newfile2 b/c/newfile2
+new file mode 100644
+index 0000000..d95f3ad
+--- /dev/null
++++ b/c/newfile2
+@@ -0,0 +1 @@
++content
+EOF
+
+test_expect_success 'apply --directory -p (new file)' '
+ git reset --hard initial &&
+ git apply -p2 --directory=some/sub/dir/ --index patch &&
+ test content = $(git show :some/sub/dir/newfile2) &&
+ test content = $(cat some/sub/dir/newfile2)
+'
+
+cat > patch << EOF
+diff --git a/delfile b/delfile
+deleted file mode 100644
+index d95f3ad..0000000
+--- a/delfile
++++ /dev/null
+@@ -1 +0,0 @@
+-content
+EOF
+
+test_expect_success 'apply --directory (delete file)' '
+ git reset --hard initial &&
+ echo content >some/sub/dir/delfile &&
+ git add some/sub/dir/delfile &&
+ git apply --directory=some/sub/dir/ --index patch &&
+ ! (git ls-files | grep delfile)
+'
+
+cat > patch << 'EOF'
+diff --git "a/qu\157tefile" "b/qu\157tefile"
+new file mode 100644
+index 0000000..d95f3ad
+--- /dev/null
++++ "b/qu\157tefile"
+@@ -0,0 +1 @@
++content
+EOF
+
+test_expect_success 'apply --directory (quoted filename)' '
+ git reset --hard initial &&
+ git apply --directory=some/sub/dir/ --index patch &&
+ test content = $(git show :some/sub/dir/quotefile) &&
+ test content = $(cat some/sub/dir/quotefile)
+'
+
+test_done
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
new file mode 100755
index 0000000..fc7af04
--- /dev/null
+++ b/t/t4129-apply-samemode.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='applying patch with mode bits'
+
+. ./test-lib.sh
+
+if test "$(git config --bool core.filemode)" = false
+then
+ say 'filemode disabled on the filesystem'
+else
+ test_set_prereq FILEMODE
+fi
+
+test_expect_success setup '
+ echo original >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git tag initial &&
+ echo modified >file &&
+ git diff --stat -p >patch-0.txt &&
+ chmod +x file &&
+ git diff --stat -p >patch-1.txt
+'
+
+test_expect_success FILEMODE 'same mode (no index)' '
+ git reset --hard &&
+ chmod +x file &&
+ git apply patch-0.txt &&
+ test -x file
+'
+
+test_expect_success FILEMODE 'same mode (with index)' '
+ git reset --hard &&
+ chmod +x file &&
+ git add file &&
+ git apply --index patch-0.txt &&
+ test -x file &&
+ git diff --exit-code
+'
+
+test_expect_success FILEMODE 'same mode (index only)' '
+ git reset --hard &&
+ chmod +x file &&
+ git add file &&
+ git apply --cached patch-0.txt &&
+ git ls-files -s file | grep "^100755"
+'
+
+test_expect_success FILEMODE 'mode update (no index)' '
+ git reset --hard &&
+ git apply patch-1.txt &&
+ test -x file
+'
+
+test_expect_success FILEMODE 'mode update (with index)' '
+ git reset --hard &&
+ git apply --index patch-1.txt &&
+ test -x file &&
+ git diff --exit-code
+'
+
+test_expect_success FILEMODE 'mode update (index only)' '
+ git reset --hard &&
+ git apply --cached patch-1.txt &&
+ git ls-files -s file | grep "^100755"
+'
+
+test_done
diff --git a/t/t4130-apply-criss-cross-rename.sh b/t/t4130-apply-criss-cross-rename.sh
new file mode 100755
index 0000000..7cfa2d6
--- /dev/null
+++ b/t/t4130-apply-criss-cross-rename.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='git apply handling criss-cross rename patch.'
+. ./test-lib.sh
+
+create_file() {
+ cnt=0
+ while test $cnt -le 100
+ do
+ cnt=$(($cnt + 1))
+ echo "$2" >> "$1"
+ done
+}
+
+test_expect_success 'setup' '
+ create_file file1 "File1 contents" &&
+ create_file file2 "File2 contents" &&
+ create_file file3 "File3 contents" &&
+ git add file1 file2 file3 &&
+ git commit -m 1
+'
+
+test_expect_success 'criss-cross rename' '
+ mv file1 tmp &&
+ mv file2 file1 &&
+ mv tmp file2 &&
+ cp file1 file1-swapped &&
+ cp file2 file2-swapped
+'
+
+test_expect_success 'diff -M -B' '
+ git diff -M -B > diff &&
+ git reset --hard
+
+'
+
+test_expect_success 'apply' '
+ git apply diff &&
+ test_cmp file1 file1-swapped &&
+ test_cmp file2 file2-swapped
+'
+
+test_expect_success 'criss-cross rename' '
+ git reset --hard &&
+ mv file1 tmp &&
+ mv file2 file1 &&
+ mv file3 file2
+ mv tmp file3 &&
+ cp file1 file1-swapped &&
+ cp file2 file2-swapped &&
+ cp file3 file3-swapped
+'
+
+test_expect_success 'diff -M -B' '
+ git diff -M -B > diff &&
+ git reset --hard
+'
+
+test_expect_success 'apply' '
+ git apply diff &&
+ test_cmp file1 file1-swapped &&
+ test_cmp file2 file2-swapped &&
+ test_cmp file3 file3-swapped
+'
+
+test_done
diff --git a/t/t4131-apply-fake-ancestor.sh b/t/t4131-apply-fake-ancestor.sh
new file mode 100755
index 0000000..94373ca
--- /dev/null
+++ b/t/t4131-apply-fake-ancestor.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Stephen Boyd
+#
+
+test_description='git apply --build-fake-ancestor handling.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit 1 &&
+ test_commit 2 &&
+ mkdir sub &&
+ test_commit 3 sub/3 &&
+ test_commit 4
+'
+
+test_expect_success 'apply --build-fake-ancestor' '
+ git checkout 2 &&
+ echo "A" > 1.t &&
+ git diff > 1.patch &&
+ git reset --hard &&
+ git checkout 1 &&
+ git apply --build-fake-ancestor 1.ancestor 1.patch
+'
+
+test_expect_success 'apply --build-fake-ancestor in a subdirectory' '
+ git checkout 3 &&
+ echo "C" > sub/3.t &&
+ git diff > 3.patch &&
+ git reset --hard &&
+ git checkout 4 &&
+ (
+ cd sub &&
+ git apply --build-fake-ancestor 3.ancestor ../3.patch &&
+ test -f 3.ancestor
+ ) &&
+ git apply --build-fake-ancestor 3.ancestor 3.patch &&
+ test_cmp sub/3.ancestor 3.ancestor
+'
+
+test_done
diff --git a/t/t4132-apply-removal.sh b/t/t4132-apply-removal.sh
new file mode 100755
index 0000000..bb1ffe3
--- /dev/null
+++ b/t/t4132-apply-removal.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Junio C Hamano
+
+test_description='git-apply notices removal patches generated by GNU diff'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ cat <<-EOF >c &&
+ diff -ruN a/file b/file
+ --- a/file TS0
+ +++ b/file TS1
+ @@ -0,0 +1 @@
+ +something
+ EOF
+
+ cat <<-EOF >d &&
+ diff -ruN a/file b/file
+ --- a/file TS0
+ +++ b/file TS1
+ @@ -1 +0,0 @@
+ -something
+ EOF
+
+ timeWest="1982-09-16 07:00:00.000000000 -0800" &&
+ timeGMT="1982-09-16 15:00:00.000000000 +0000" &&
+ timeEast="1982-09-17 00:00:00.000000000 +0900" &&
+
+ epocWest="1969-12-31 16:00:00.000000000 -0800" &&
+ epocGMT="1970-01-01 00:00:00.000000000 +0000" &&
+ epocEast="1970-01-01 09:00:00.000000000 +0900" &&
+
+ sed -e "s/TS0/$epocWest/" -e "s/TS1/$timeWest/" <c >createWest.patch &&
+ sed -e "s/TS0/$epocEast/" -e "s/TS1/$timeEast/" <c >createEast.patch &&
+ sed -e "s/TS0/$epocGMT/" -e "s/TS1/$timeGMT/" <c >createGMT.patch &&
+
+ sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" <c >addWest.patch &&
+ sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" <c >addEast.patch &&
+ sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" <c >addGMT.patch &&
+
+ sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" <d >emptyWest.patch &&
+ sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" <d >emptyEast.patch &&
+ sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" <d >emptyGMT.patch &&
+
+ sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest/" <d >removeWest.patch &&
+ sed -e "s/TS0/$timeEast/" -e "s/TS1/$epocEast/" <d >removeEast.patch &&
+ sed -e "s/TS0/$timeGMT/" -e "s/TS1/$epocGMT/" <d >removeGMT.patch &&
+
+ echo something >something &&
+ >empty
+'
+
+for patch in *.patch
+do
+ test_expect_success "test $patch" '
+ rm -f file .git/index &&
+ case "$patch" in
+ create*)
+ # must be able to create
+ git apply --index $patch &&
+ test_cmp file something &&
+ # must notice the file is already there
+ >file &&
+ git add file &&
+ test_must_fail git apply $patch
+ ;;
+ add*)
+ # must be able to create or patch
+ git apply $patch &&
+ test_cmp file something &&
+ >file &&
+ git apply $patch &&
+ test_cmp file something
+ ;;
+ empty*)
+ # must leave an empty file
+ cat something >file &&
+ git add file &&
+ git apply --index $patch &&
+ test -f file &&
+ test_cmp empty file
+ ;;
+ remove*)
+ # must remove the file
+ cat something >file &&
+ git add file &&
+ git apply --index $patch &&
+ ! test -f file
+ ;;
+ esac
+ '
+done
+
+test_done
diff --git a/t/t4133-apply-filenames.sh b/t/t4133-apply-filenames.sh
new file mode 100755
index 0000000..3421807
--- /dev/null
+++ b/t/t4133-apply-filenames.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Andreas Gruenbacher
+#
+
+test_description='git apply filename consistency check'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ cat > bad1.patch <<EOF
+diff --git a/f b/f
+new file mode 100644
+index 0000000..d00491f
+--- /dev/null
++++ b/f-blah
+@@ -0,0 +1 @@
++1
+EOF
+ cat > bad2.patch <<EOF
+diff --git a/f b/f
+deleted file mode 100644
+index d00491f..0000000
+--- b/f-blah
++++ /dev/null
+@@ -1 +0,0 @@
+-1
+EOF
+'
+
+test_expect_success 'apply diff with inconsistent filenames in headers' '
+ test_must_fail git apply bad1.patch 2>err
+ grep "inconsistent new filename" err
+ test_must_fail git apply bad2.patch 2>err
+ grep "inconsistent old filename" err
+'
+
+test_done
diff --git a/t/t4134-apply-submodule.sh b/t/t4134-apply-submodule.sh
new file mode 100755
index 0000000..1b82f93
--- /dev/null
+++ b/t/t4134-apply-submodule.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Peter Collingbourne
+#
+
+test_description='git apply submodule tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ cat > create-sm.patch <<EOF
+diff --git a/dir/sm b/dir/sm
+new file mode 160000
+index 0000000..0123456
+--- /dev/null
++++ b/dir/sm
+@@ -0,0 +1 @@
++Subproject commit 0123456789abcdef0123456789abcdef01234567
+EOF
+ cat > remove-sm.patch <<EOF
+diff --git a/dir/sm b/dir/sm
+deleted file mode 160000
+index 0123456..0000000
+--- a/dir/sm
++++ /dev/null
+@@ -1 +0,0 @@
+-Subproject commit 0123456789abcdef0123456789abcdef01234567
+EOF
+'
+
+test_expect_success 'removing a submodule also removes all leading subdirectories' '
+ git apply --index create-sm.patch &&
+ test -d dir/sm &&
+ git apply --index remove-sm.patch &&
+ test \! -d dir
+'
+
+test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
new file mode 100755
index 0000000..810b04b
--- /dev/null
+++ b/t/t4150-am.sh
@@ -0,0 +1,364 @@
+#!/bin/sh
+
+test_description='git am running'
+
+. ./test-lib.sh
+
+cat >msg <<EOF
+second
+
+Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, sed diam nonumy
+eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
+voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
+kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
+ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+vero eos et accusam et justo duo dolores et ea rebum.
+
+ Duis autem vel eum iriure dolor in hendrerit in vulputate velit
+ esse molestie consequat, vel illum dolore eu feugiat nulla facilisis
+ at vero eros et accumsan et iusto odio dignissim qui blandit
+ praesent luptatum zzril delenit augue duis dolore te feugait nulla
+ facilisi.
+
+
+Lorem ipsum dolor sit amet,
+consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut
+laoreet dolore magna aliquam erat volutpat.
+
+ git
+ ---
+ +++
+
+Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
+lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
+dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
+dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
+dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
+feugait nulla facilisi.
+EOF
+
+cat >failmail <<EOF
+From foo@example.com Fri May 23 10:43:49 2008
+From: foo@example.com
+To: bar@example.com
+Subject: Re: [RFC/PATCH] git-foo.sh
+Date: Fri, 23 May 2008 05:23:42 +0200
+
+Sometimes we have to find out that there's nothing left.
+
+EOF
+
+cat >pine <<EOF
+From MAILER-DAEMON Fri May 23 10:43:49 2008
+Date: 23 May 2008 05:23:42 +0200
+From: Mail System Internal Data <MAILER-DAEMON@example.com>
+Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
+Message-ID: <foo-0001@example.com>
+
+This text is part of the internal format of your mail folder, and is not
+a real message. It is created automatically by the mail system software.
+If deleted, important folder data will be lost, and it will be re-created
+with the data reset to initial values.
+
+EOF
+
+echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected
+
+test_expect_success setup '
+ echo hello >file &&
+ git add file &&
+ test_tick &&
+ git commit -m first &&
+ git tag first &&
+ echo world >>file &&
+ git add file &&
+ test_tick &&
+ git commit -s -F msg &&
+ git tag second &&
+ git format-patch --stdout first >patch1 &&
+ {
+ echo "X-Fake-Field: Line One" &&
+ echo "X-Fake-Field: Line Two" &&
+ echo "X-Fake-Field: Line Three" &&
+ git format-patch --stdout first | sed -e "1d"
+ } > patch1.eml &&
+ {
+ echo "X-Fake-Field: Line One" &&
+ echo "X-Fake-Field: Line Two" &&
+ echo "X-Fake-Field: Line Three" &&
+ git format-patch --stdout first | sed -e "1d"
+ } | append_cr >patch1-crlf.eml &&
+ sed -n -e "3,\$p" msg >file &&
+ git add file &&
+ test_tick &&
+ git commit -m third &&
+ git format-patch --stdout first >patch2 &&
+ git checkout -b lorem &&
+ sed -n -e "11,\$p" msg >file &&
+ head -n 9 msg >>file &&
+ test_tick &&
+ git commit -a -m "moved stuff" &&
+ echo goodbye >another &&
+ git add another &&
+ test_tick &&
+ git commit -m "added another file" &&
+ git format-patch --stdout master >lorem-move.patch
+'
+
+# reset time
+unset test_tick
+test_tick
+
+test_expect_success 'am applies patch correctly' '
+ git checkout first &&
+ test_tick &&
+ git am <patch1 &&
+ ! test -d .git/rebase-apply &&
+ test -z "$(git diff second)" &&
+ test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
+test_expect_success 'am applies patch e-mail not in a mbox' '
+ git checkout first &&
+ git am patch1.eml &&
+ ! test -d .git/rebase-apply &&
+ test -z "$(git diff second)" &&
+ test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
+test_expect_success 'am applies patch e-mail not in a mbox with CRLF' '
+ git checkout first &&
+ git am patch1-crlf.eml &&
+ ! test -d .git/rebase-apply &&
+ test -z "$(git diff second)" &&
+ test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
+GIT_AUTHOR_NAME="Another Thor"
+GIT_AUTHOR_EMAIL="a.thor@example.com"
+GIT_COMMITTER_NAME="Co M Miter"
+GIT_COMMITTER_EMAIL="c.miter@example.com"
+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+
+compare () {
+ test "$(git cat-file commit "$2" | grep "^$1 ")" = \
+ "$(git cat-file commit "$3" | grep "^$1 ")"
+}
+
+test_expect_success 'am changes committer and keeps author' '
+ test_tick &&
+ git checkout first &&
+ git am patch2 &&
+ ! test -d .git/rebase-apply &&
+ test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
+ test -z "$(git diff master..HEAD)" &&
+ test -z "$(git diff master^..HEAD^)" &&
+ compare author master HEAD &&
+ compare author master^ HEAD^ &&
+ test "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = \
+ "$(git log -1 --pretty=format:"%cn <%ce>" HEAD)"
+'
+
+test_expect_success 'am --signoff adds Signed-off-by: line' '
+ git checkout -b master2 first &&
+ git am --signoff <patch2 &&
+ echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >>expected &&
+ git cat-file commit HEAD^ | grep "Signed-off-by:" >actual &&
+ test_cmp actual expected &&
+ echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
+ git cat-file commit HEAD | grep "Signed-off-by:" >actual &&
+ test_cmp actual expected
+'
+
+test_expect_success 'am stays in branch' '
+ test "refs/heads/master2" = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
+ git format-patch --stdout HEAD^ >patch3 &&
+ sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4
+ git checkout HEAD^ &&
+ git am --signoff patch4 &&
+ test "$(git cat-file commit HEAD | grep -c "^Signed-off-by:")" -eq 1
+'
+
+test_expect_success 'am without --keep removes Re: and [PATCH] stuff' '
+ test "$(git rev-parse HEAD)" = "$(git rev-parse master2)"
+'
+
+test_expect_success 'am --keep really keeps the subject' '
+ git checkout HEAD^ &&
+ git am --keep patch4 &&
+ ! test -d .git/rebase-apply &&
+ git cat-file commit HEAD |
+ fgrep "Re: Re: Re: [PATCH 1/5 v2] third"
+'
+
+test_expect_success 'am -3 falls back to 3-way merge' '
+ git checkout -b lorem2 master2 &&
+ sed -n -e "3,\$p" msg >file &&
+ head -n 9 msg >>file &&
+ git add file &&
+ test_tick &&
+ git commit -m "copied stuff" &&
+ git am -3 lorem-move.patch &&
+ ! test -d .git/rebase-apply &&
+ test -z "$(git diff lorem)"
+'
+
+test_expect_success 'am -3 -q is quiet' '
+ git reset master2 --hard &&
+ sed -n -e "3,\$p" msg >file &&
+ head -n 9 msg >>file &&
+ git add file &&
+ test_tick &&
+ git commit -m "copied stuff" &&
+ git am -3 -q lorem-move.patch > output.out 2>&1 &&
+ ! test -s output.out
+'
+
+test_expect_success 'am pauses on conflict' '
+ git checkout lorem2^^ &&
+ test_must_fail git am lorem-move.patch &&
+ test -d .git/rebase-apply
+'
+
+test_expect_success 'am --skip works' '
+ git am --skip &&
+ ! test -d .git/rebase-apply &&
+ test -z "$(git diff lorem2^^ -- file)" &&
+ test goodbye = "$(cat another)"
+'
+
+test_expect_success 'am --resolved works' '
+ git checkout lorem2^^ &&
+ test_must_fail git am lorem-move.patch &&
+ test -d .git/rebase-apply &&
+ echo resolved >>file &&
+ git add file &&
+ git am --resolved &&
+ ! test -d .git/rebase-apply &&
+ test goodbye = "$(cat another)"
+'
+
+test_expect_success 'am takes patches from a Pine mailbox' '
+ git checkout first &&
+ cat pine patch1 | git am &&
+ ! test -d .git/rebase-apply &&
+ test -z "$(git diff master^..HEAD)"
+'
+
+test_expect_success 'am fails on mail without patch' '
+ test_must_fail git am <failmail &&
+ rm -r .git/rebase-apply/
+'
+
+test_expect_success 'am fails on empty patch' '
+ echo "---" >>failmail &&
+ test_must_fail git am <failmail &&
+ git am --skip &&
+ ! test -d .git/rebase-apply
+'
+
+test_expect_success 'am works from stdin in subdirectory' '
+ rm -fr subdir &&
+ git checkout first &&
+ (
+ mkdir -p subdir &&
+ cd subdir &&
+ git am <../patch1
+ ) &&
+ test -z "$(git diff second)"
+'
+
+test_expect_success 'am works from file (relative path given) in subdirectory' '
+ rm -fr subdir &&
+ git checkout first &&
+ (
+ mkdir -p subdir &&
+ cd subdir &&
+ git am ../patch1
+ ) &&
+ test -z "$(git diff second)"
+'
+
+test_expect_success 'am works from file (absolute path given) in subdirectory' '
+ rm -fr subdir &&
+ git checkout first &&
+ P=$(pwd) &&
+ (
+ mkdir -p subdir &&
+ cd subdir &&
+ git am "$P/patch1"
+ ) &&
+ test -z "$(git diff second)"
+'
+
+test_expect_success 'am --committer-date-is-author-date' '
+ git checkout first &&
+ test_tick &&
+ git am --committer-date-is-author-date patch1 &&
+ git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
+ at=$(sed -ne "/^author /s/.*> //p" head1) &&
+ ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
+ test "$at" = "$ct"
+'
+
+test_expect_success 'am without --committer-date-is-author-date' '
+ git checkout first &&
+ test_tick &&
+ git am patch1 &&
+ git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
+ at=$(sed -ne "/^author /s/.*> //p" head1) &&
+ ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
+ test "$at" != "$ct"
+'
+
+# This checks for +0000 because TZ is set to UTC and that should
+# show up when the current time is used. The date in message is set
+# by test_tick that uses -0700 timezone; if this feature does not
+# work, we will see that instead of +0000.
+test_expect_success 'am --ignore-date' '
+ git checkout first &&
+ test_tick &&
+ git am --ignore-date patch1 &&
+ git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
+ at=$(sed -ne "/^author /s/.*> //p" head1) &&
+ echo "$at" | grep "+0000"
+'
+
+test_expect_success 'am into an unborn branch' '
+ rm -fr subdir &&
+ mkdir -p subdir &&
+ git format-patch --numbered-files -o subdir -1 first &&
+ (
+ cd subdir &&
+ git init &&
+ git am 1
+ ) &&
+ result=$(
+ cd subdir && git rev-parse HEAD^{tree}
+ ) &&
+ test "z$result" = "z$(git rev-parse first^{tree})"
+'
+
+test_expect_success 'am newline in subject' '
+ git checkout first &&
+ test_tick &&
+ sed -e "s/second/second \\\n foo/" patch1 > patchnl &&
+ git am < patchnl > output.out 2>&1 &&
+ grep "^Applying: second \\\n foo$" output.out
+'
+
+test_expect_success 'am -q is quiet' '
+ git checkout first &&
+ test_tick &&
+ git am -q < patch1 > output.out 2>&1 &&
+ ! test -s output.out
+'
+
+test_done
diff --git a/t/t4151-am-abort.sh b/t/t4151-am-abort.sh
new file mode 100755
index 0000000..b55c411
--- /dev/null
+++ b/t/t4151-am-abort.sh
@@ -0,0 +1,65 @@
+#!/bin/sh
+
+test_description='am --abort'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in a b c d e f g
+ do
+ echo $i
+ done >file-1 &&
+ cp file-1 file-2 &&
+ test_tick &&
+ git add file-1 file-2 &&
+ git commit -m initial &&
+ git tag initial &&
+ for i in 2 3 4 5 6
+ do
+ echo $i >>file-1 &&
+ echo $i >otherfile-$i &&
+ git add otherfile-$i &&
+ test_tick &&
+ git commit -a -m $i || break
+ done &&
+ git format-patch --no-numbered initial &&
+ git checkout -b side initial &&
+ echo local change >file-2-expect
+'
+
+for with3 in '' ' -3'
+do
+ test_expect_success "am$with3 stops at a patch that does not apply" '
+
+ git reset --hard initial &&
+ cp file-2-expect file-2 &&
+
+ test_must_fail git am$with3 000[1245]-*.patch &&
+ git log --pretty=tformat:%s >actual &&
+ for i in 3 2 initial
+ do
+ echo $i
+ done >expect &&
+ test_cmp expect actual
+ '
+
+ test_expect_success "am$with3 --skip continue after failed am$with3" '
+ test_must_fail git am$with3 --skip >output &&
+ test "$(grep "^Applying" output)" = "Applying: 6" &&
+ test_cmp file-2-expect file-2 &&
+ test ! -f .git/MERGE_RR
+ '
+
+ test_expect_success "am --abort goes back after failed am$with3" '
+ git am --abort &&
+ git rev-parse HEAD >actual &&
+ git rev-parse initial >expect &&
+ test_cmp expect actual &&
+ test_cmp file-2-expect file-2 &&
+ git diff-index --exit-code --cached HEAD &&
+ test ! -f .git/MERGE_RR
+ '
+
+done
+
+test_done
diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
new file mode 100755
index 0000000..70856d0
--- /dev/null
+++ b/t/t4200-rerere.sh
@@ -0,0 +1,240 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='git rerere
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' "
+ cat > a1 <<- EOF &&
+ Some title
+ ==========
+ Whether 'tis nobler in the mind to suffer
+ The slings and arrows of outrageous fortune,
+ Or to take arms against a sea of troubles,
+ And by opposing end them? To die: to sleep;
+ No more; and by a sleep to say we end
+ The heart-ache and the thousand natural shocks
+ That flesh is heir to, 'tis a consummation
+ Devoutly to be wish'd.
+ EOF
+
+ git add a1 &&
+ git commit -q -a -m initial &&
+
+ git checkout -b first &&
+ cat >> a1 <<- EOF &&
+ Some title
+ ==========
+ To die, to sleep;
+ To sleep: perchance to dream: ay, there's the rub;
+ For in that sleep of death what dreams may come
+ When we have shuffled off this mortal coil,
+ Must give us pause: there's the respect
+ That makes calamity of so long life;
+ EOF
+ git commit -q -a -m first &&
+
+ git checkout -b second master &&
+ git show first:a1 |
+ sed -e 's/To die, t/To die! T/' -e 's/Some title/Some Title/' > a1 &&
+ echo '* END *' >>a1 &&
+ git commit -q -a -m second
+"
+
+test_expect_success 'nothing recorded without rerere' '
+ (rm -rf .git/rr-cache; git config rerere.enabled false) &&
+ test_must_fail git merge first &&
+ ! test -d .git/rr-cache
+'
+
+# activate rerere, old style
+test_expect_success 'conflicting merge' '
+ git reset --hard &&
+ mkdir .git/rr-cache &&
+ git config --unset rerere.enabled &&
+ test_must_fail git merge first
+'
+
+sha1=$(perl -pe 's/ .*//' .git/MERGE_RR)
+rr=.git/rr-cache/$sha1
+test_expect_success 'recorded preimage' "grep ^=======$ $rr/preimage"
+
+test_expect_success 'rerere.enabled works, too' '
+ rm -rf .git/rr-cache &&
+ git config rerere.enabled true &&
+ git reset --hard &&
+ test_must_fail git merge first &&
+ grep ^=======$ $rr/preimage
+'
+
+test_expect_success 'no postimage or thisimage yet' \
+ "test ! -f $rr/postimage -a ! -f $rr/thisimage"
+
+test_expect_success 'preimage has right number of lines' '
+
+ cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
+ test $cnt = 13
+
+'
+
+git show first:a1 > a1
+
+cat > expect << EOF
+--- a/a1
++++ b/a1
+@@ -1,4 +1,4 @@
+-Some Title
++Some title
+ ==========
+ Whether 'tis nobler in the mind to suffer
+ The slings and arrows of outrageous fortune,
+@@ -8,21 +8,11 @@
+ The heart-ache and the thousand natural shocks
+ That flesh is heir to, 'tis a consummation
+ Devoutly to be wish'd.
+-<<<<<<<
+-Some Title
+-==========
+-To die! To sleep;
+-=======
+ Some title
+ ==========
+ To die, to sleep;
+->>>>>>>
+ To sleep: perchance to dream: ay, there's the rub;
+ For in that sleep of death what dreams may come
+ When we have shuffled off this mortal coil,
+ Must give us pause: there's the respect
+ That makes calamity of so long life;
+-<<<<<<<
+-=======
+-* END *
+->>>>>>>
+EOF
+git rerere diff > out
+
+test_expect_success 'rerere diff' 'test_cmp expect out'
+
+cat > expect << EOF
+a1
+EOF
+
+git rerere status > out
+
+test_expect_success 'rerere status' 'test_cmp expect out'
+
+test_expect_success 'commit succeeds' \
+ "git commit -q -a -m 'prefer first over second'"
+
+test_expect_success 'recorded postimage' "test -f $rr/postimage"
+
+test_expect_success 'another conflicting merge' '
+ git checkout -b third master &&
+ git show second^:a1 | sed "s/To die: t/To die! T/" > a1 &&
+ git commit -q -a -m third &&
+ test_must_fail git pull . first
+'
+
+git show first:a1 | sed 's/To die: t/To die! T/' > expect
+test_expect_success 'rerere kicked in' "! grep ^=======$ a1"
+
+test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'
+
+rm $rr/postimage
+echo "$sha1 a1" | perl -pe 'y/\012/\000/' > .git/MERGE_RR
+
+test_expect_success 'rerere clear' 'git rerere clear'
+
+test_expect_success 'clear removed the directory' "test ! -d $rr"
+
+mkdir $rr
+echo Hello > $rr/preimage
+echo World > $rr/postimage
+
+sha2=4000000000000000000000000000000000000000
+rr2=.git/rr-cache/$sha2
+mkdir $rr2
+echo Hello > $rr2/preimage
+
+almost_15_days_ago=$((60-15*86400))
+just_over_15_days_ago=$((-1-15*86400))
+almost_60_days_ago=$((60-60*86400))
+just_over_60_days_ago=$((-1-60*86400))
+
+test-chmtime =$almost_60_days_ago $rr/preimage
+test-chmtime =$almost_15_days_ago $rr2/preimage
+
+test_expect_success 'garbage collection (part1)' 'git rerere gc'
+
+test_expect_success 'young records still live' \
+ "test -f $rr/preimage && test -f $rr2/preimage"
+
+test-chmtime =$just_over_60_days_ago $rr/preimage
+test-chmtime =$just_over_15_days_ago $rr2/preimage
+
+test_expect_success 'garbage collection (part2)' 'git rerere gc'
+
+test_expect_success 'old records rest in peace' \
+ "test ! -f $rr/preimage && test ! -f $rr2/preimage"
+
+test_expect_success 'file2 added differently in two branches' '
+ git reset --hard &&
+ git checkout -b fourth &&
+ echo Hallo > file2 &&
+ git add file2 &&
+ git commit -m version1 &&
+ git checkout third &&
+ echo Bello > file2 &&
+ git add file2 &&
+ git commit -m version2 &&
+ test_must_fail git merge fourth &&
+ echo Cello > file2 &&
+ git add file2 &&
+ git commit -m resolution
+'
+
+test_expect_success 'resolution was recorded properly' '
+ git reset --hard HEAD~2 &&
+ git checkout -b fifth &&
+ echo Hallo > file3 &&
+ git add file3 &&
+ git commit -m version1 &&
+ git checkout third &&
+ echo Bello > file3 &&
+ git add file3 &&
+ git commit -m version2 &&
+ git tag version2 &&
+ test_must_fail git merge fifth &&
+ test Cello = "$(cat file3)" &&
+ test 0 != $(git ls-files -u | wc -l)
+'
+
+test_expect_success 'rerere.autoupdate' '
+ git config rerere.autoupdate true
+ git reset --hard &&
+ git checkout version2 &&
+ test_must_fail git merge fifth &&
+ test 0 = $(git ls-files -u | wc -l)
+'
+
+test_expect_success 'merge --rerere-autoupdate' '
+ git config --unset rerere.autoupdate
+ git reset --hard &&
+ git checkout version2 &&
+ test_must_fail git merge --rerere-autoupdate fifth &&
+ test 0 = $(git ls-files -u | wc -l)
+'
+
+test_expect_success 'merge --no-rerere-autoupdate' '
+ git config rerere.autoupdate true
+ git reset --hard &&
+ git checkout version2 &&
+ test_must_fail git merge --no-rerere-autoupdate fifth &&
+ test 2 = $(git ls-files -u | wc -l)
+'
+
+test_done
diff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh
new file mode 100755
index 0000000..cdb70b4
--- /dev/null
+++ b/t/t4201-shortlog.sh
@@ -0,0 +1,151 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='git shortlog
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo 1 >a1 &&
+ git add a1 &&
+ tree=$(git write-tree) &&
+ commit=$(printf "%s\n" "Test" "" | git commit-tree "$tree") &&
+ git update-ref HEAD "$commit" &&
+
+ echo 2 >a1 &&
+ git commit --quiet -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1 &&
+
+ # test if the wrapping is still valid
+ # when replacing all is by treble clefs.
+ echo 3 >a1 &&
+ git commit --quiet -m "$(
+ echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" |
+ sed "s/i/1234/g" |
+ tr 1234 "\360\235\204\236")" a1 &&
+
+ # now fsck up the utf8
+ git config i18n.commitencoding non-utf-8 &&
+ echo 4 >a1 &&
+ git commit --quiet -m "$(
+ echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" |
+ sed "s/i/1234/g" |
+ tr 1234 "\370\235\204\236")" a1 &&
+
+ echo 5 >a1 &&
+ git commit --quiet -m "a 12 34 56 78" a1
+
+ echo 6 >a1 &&
+ git commit --quiet -m "Commit by someone else" \
+ --author="Someone else <not!me>" a1 &&
+
+ cat >expect.template <<-\EOF
+ A U Thor (5):
+ SUBJECT
+ SUBJECT
+ SUBJECT
+ SUBJECT
+ SUBJECT
+
+ Someone else (1):
+ SUBJECT
+
+ EOF
+'
+
+fuzz() {
+ file=$1 &&
+ sed "
+ s/$_x40/OBJECT_NAME/g
+ s/$_x05/OBJID/g
+ s/^ \{6\}[CTa].*/ SUBJECT/g
+ s/^ \{8\}[^ ].*/ CONTINUATION/g
+ " <"$file" >"$file.fuzzy" &&
+ sed "/CONTINUATION/ d" <"$file.fuzzy"
+}
+
+test_expect_success 'default output format' '
+ git shortlog HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect.template log.predictable
+'
+
+test_expect_success 'pretty format' '
+ sed s/SUBJECT/OBJECT_NAME/ expect.template >expect &&
+ git shortlog --format="%H" HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect log.predictable
+'
+
+test_expect_success '--abbrev' '
+ sed s/SUBJECT/OBJID/ expect.template >expect &&
+ git shortlog --format="%h" --abbrev=5 HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect log.predictable
+'
+
+test_expect_success 'output from user-defined format is re-wrapped' '
+ sed "s/SUBJECT/two lines/" expect.template >expect &&
+ git shortlog --format="two%nlines" HEAD >log &&
+ fuzz log >log.predictable &&
+ test_cmp expect log.predictable
+'
+
+test_expect_success 'shortlog wrapping' '
+ cat >expect <<\EOF &&
+A U Thor (5):
+ Test
+ This is a very, very long first line for the commit message to see if
+ it is wrapped correctly
+ Thð„žs ð„žs a very, very long fð„žrst lð„žne for the commð„žt message to see ð„žf
+ ð„žt ð„žs wrapped correctly
+ Thø„žs ø„žs a very, very long fø„žrst lø„žne for the commø„žt
+ message to see ø„žf ø„žt ø„žs wrapped correctly
+ a 12 34
+ 56 78
+
+Someone else (1):
+ Commit by someone else
+
+EOF
+ git shortlog -w HEAD >out &&
+ test_cmp expect out
+'
+
+test_expect_success 'shortlog from non-git directory' '
+ git log HEAD >log &&
+ GIT_DIR=non-existing git shortlog -w <log >out &&
+ test_cmp expect out
+'
+
+iconvfromutf8toiso88591() {
+ printf "%s" "$*" | iconv -f UTF-8 -t ISO8859-1
+}
+
+DSCHO="Jöhännës \"Dschö\" Schindëlin"
+DSCHOE="$DSCHO <Johannes.Schindelin@gmx.de>"
+MSG1="set a1 to 2 and some non-ASCII chars: Äßø"
+MSG2="set a1 to 3 and some non-ASCII chars: áæï"
+cat > expect << EOF
+$DSCHO (2):
+ $MSG1
+ $MSG2
+
+EOF
+
+test_expect_success 'shortlog encoding' '
+ git reset --hard "$commit" &&
+ git config --unset i18n.commitencoding &&
+ echo 2 > a1 &&
+ git commit --quiet -m "$MSG1" --author="$DSCHOE" a1 &&
+ git config i18n.commitencoding "ISO8859-1" &&
+ echo 3 > a1 &&
+ git commit --quiet -m "$(iconvfromutf8toiso88591 "$MSG2")" \
+ --author="$(iconvfromutf8toiso88591 "$DSCHOE")" a1 &&
+ git config --unset i18n.commitencoding &&
+ git shortlog HEAD~2.. > out &&
+test_cmp expect out'
+
+test_done
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
new file mode 100755
index 0000000..2230e60
--- /dev/null
+++ b/t/t4202-log.sh
@@ -0,0 +1,440 @@
+#!/bin/sh
+
+test_description='git log'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ echo one >one &&
+ git add one &&
+ test_tick &&
+ git commit -m initial &&
+
+ echo ichi >one &&
+ git add one &&
+ test_tick &&
+ git commit -m second &&
+
+ git mv one ichi &&
+ test_tick &&
+ git commit -m third &&
+
+ cp ichi ein &&
+ git add ein &&
+ test_tick &&
+ git commit -m fourth &&
+
+ mkdir a &&
+ echo ni >a/two &&
+ git add a/two &&
+ test_tick &&
+ git commit -m fifth &&
+
+ git rm a/two &&
+ test_tick &&
+ git commit -m sixth
+
+'
+
+printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial" > expect
+test_expect_success 'pretty' '
+
+ git log --pretty="format:%s" > actual &&
+ test_cmp expect actual
+'
+
+printf "sixth\nfifth\nfourth\nthird\nsecond\ninitial\n" > expect
+test_expect_success 'pretty (tformat)' '
+
+ git log --pretty="tformat:%s" > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'pretty (shortcut)' '
+
+ git log --pretty="%s" > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'format' '
+
+ git log --format="%s" > actual &&
+ test_cmp expect actual
+'
+
+cat > expect << EOF
+ This is
+ the sixth
+ commit.
+ This is
+ the fifth
+ commit.
+EOF
+
+test_expect_success 'format %w(12,1,2)' '
+
+ git log -2 --format="%w(12,1,2)This is the %s commit." > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'format %w(,1,2)' '
+
+ git log -2 --format="%w(,1,2)This is%nthe %s%ncommit." > actual &&
+ test_cmp expect actual
+'
+
+cat > expect << EOF
+804a787 sixth
+394ef78 fifth
+5d31159 fourth
+2fbe8c0 third
+f7dab8e second
+3a2fdcb initial
+EOF
+test_expect_success 'oneline' '
+
+ git log --oneline > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'diff-filter=A' '
+
+ actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) &&
+ expect=$(echo fifth ; echo fourth ; echo third ; echo initial) &&
+ test "$actual" = "$expect" || {
+ echo Oops
+ echo "Actual: $actual"
+ false
+ }
+
+'
+
+test_expect_success 'diff-filter=M' '
+
+ actual=$(git log --pretty="format:%s" --diff-filter=M HEAD) &&
+ expect=$(echo second) &&
+ test "$actual" = "$expect" || {
+ echo Oops
+ echo "Actual: $actual"
+ false
+ }
+
+'
+
+test_expect_success 'diff-filter=D' '
+
+ actual=$(git log --pretty="format:%s" --diff-filter=D HEAD) &&
+ expect=$(echo sixth ; echo third) &&
+ test "$actual" = "$expect" || {
+ echo Oops
+ echo "Actual: $actual"
+ false
+ }
+
+'
+
+test_expect_success 'diff-filter=R' '
+
+ actual=$(git log -M --pretty="format:%s" --diff-filter=R HEAD) &&
+ expect=$(echo third) &&
+ test "$actual" = "$expect" || {
+ echo Oops
+ echo "Actual: $actual"
+ false
+ }
+
+'
+
+test_expect_success 'diff-filter=C' '
+
+ actual=$(git log -C -C --pretty="format:%s" --diff-filter=C HEAD) &&
+ expect=$(echo fourth) &&
+ test "$actual" = "$expect" || {
+ echo Oops
+ echo "Actual: $actual"
+ false
+ }
+
+'
+
+test_expect_success 'git log --follow' '
+
+ actual=$(git log --follow --pretty="format:%s" ichi) &&
+ expect=$(echo third ; echo second ; echo initial) &&
+ test "$actual" = "$expect" || {
+ echo Oops
+ echo "Actual: $actual"
+ false
+ }
+
+'
+
+cat > expect << EOF
+804a787 sixth
+394ef78 fifth
+5d31159 fourth
+EOF
+test_expect_success 'git log --no-walk <commits> sorts by commit time' '
+ git log --no-walk --oneline 5d31159 804a787 394ef78 > actual &&
+ test_cmp expect actual
+'
+
+cat > expect << EOF
+5d31159 fourth
+804a787 sixth
+394ef78 fifth
+EOF
+test_expect_success 'git show <commits> leaves list of commits as given' '
+ git show --oneline -s 5d31159 804a787 394ef78 > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup case sensitivity tests' '
+ echo case >one &&
+ test_tick &&
+ git add one
+ git commit -a -m Second
+'
+
+test_expect_success 'log --grep' '
+ echo second >expect &&
+ git log -1 --pretty="tformat:%s" --grep=sec >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log -i --grep' '
+ echo Second >expect &&
+ git log -1 --pretty="tformat:%s" -i --grep=sec >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --grep -i' '
+ echo Second >expect &&
+ git log -1 --pretty="tformat:%s" --grep=sec -i >actual &&
+ test_cmp expect actual
+'
+
+cat > expect <<EOF
+* Second
+* sixth
+* fifth
+* fourth
+* third
+* second
+* initial
+EOF
+
+test_expect_success 'simple log --graph' '
+ git log --graph --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 &&
+ test_commit side-2 2 2 &&
+ git checkout master &&
+ git merge side
+'
+
+cat > expect <<\EOF
+* Merge branch 'side'
+|\
+| * side-2
+| * side-1
+* | Second
+* | sixth
+* | fifth
+* | fourth
+|/
+* third
+* second
+* initial
+EOF
+
+test_expect_success 'log --graph with merge' '
+ git log --graph --date-order --pretty=tformat:%s |
+ sed "s/ *\$//" >actual &&
+ test_cmp expect actual
+'
+
+cat > expect <<\EOF
+* commit master
+|\ Merge: A B
+| | Author: A U Thor <author@example.com>
+| |
+| | Merge branch 'side'
+| |
+| * commit side
+| | Author: A U Thor <author@example.com>
+| |
+| | side-2
+| |
+| * commit tags/side-1
+| | Author: A U Thor <author@example.com>
+| |
+| | side-1
+| |
+* | commit master~1
+| | Author: A U Thor <author@example.com>
+| |
+| | Second
+| |
+* | commit master~2
+| | Author: A U Thor <author@example.com>
+| |
+| | sixth
+| |
+* | commit master~3
+| | Author: A U Thor <author@example.com>
+| |
+| | fifth
+| |
+* | commit master~4
+|/ Author: A U Thor <author@example.com>
+|
+| fourth
+|
+* commit tags/side-1~1
+| Author: A U Thor <author@example.com>
+|
+| third
+|
+* commit tags/side-1~2
+| Author: A U Thor <author@example.com>
+|
+| second
+|
+* commit tags/side-1~3
+ Author: A U Thor <author@example.com>
+
+ initial
+EOF
+
+test_expect_success 'log --graph with full output' '
+ git log --graph --date-order --pretty=short |
+ git name-rev --name-only --stdin |
+ sed "s/Merge:.*/Merge: A B/;s/ *\$//" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'set up more tangled history' '
+ git checkout -b tangle HEAD~6 &&
+ test_commit tangle-a tangle-a a &&
+ git merge master~3 &&
+ git merge side~1 &&
+ git checkout master &&
+ git merge tangle &&
+ git checkout -b reach &&
+ test_commit reach &&
+ git checkout master &&
+ git checkout -b octopus-a &&
+ test_commit octopus-a &&
+ git checkout master &&
+ git checkout -b octopus-b &&
+ test_commit octopus-b &&
+ git checkout master &&
+ test_commit seventh &&
+ git merge octopus-a octopus-b
+ git merge reach
+'
+
+cat > expect <<\EOF
+* Merge commit 'reach'
+|\
+| \
+| \
+*-. \ Merge commit 'octopus-a'; commit 'octopus-b'
+|\ \ \
+* | | | seventh
+| | * | octopus-b
+| |/ /
+|/| |
+| * | octopus-a
+|/ /
+| * reach
+|/
+* Merge branch 'tangle'
+|\
+| * Merge branch 'side' (early part) into tangle
+| |\
+| * \ Merge branch 'master' (early part) into tangle
+| |\ \
+| * | | tangle-a
+* | | | Merge branch 'side'
+|\ \ \ \
+| * | | | side-2
+| | |_|/
+| |/| |
+| * | | side-1
+* | | | Second
+* | | | sixth
+| |_|/
+|/| |
+* | | fifth
+* | | fourth
+|/ /
+* | third
+|/
+* second
+* initial
+EOF
+
+test_expect_success 'log --graph with merge' '
+ git log --graph --date-order --pretty=tformat:%s |
+ sed "s/ *\$//" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log.decorate configuration' '
+ git config --unset-all log.decorate || :
+
+ git log --oneline >expect.none &&
+ git log --oneline --decorate >expect.short &&
+ git log --oneline --decorate=full >expect.full &&
+
+ echo "[log] decorate" >>.git/config &&
+ git log --oneline >actual &&
+ test_cmp expect.short actual &&
+
+ git config --unset-all log.decorate &&
+ git config log.decorate true &&
+ git log --oneline >actual &&
+ test_cmp expect.short actual &&
+ git log --oneline --decorate=full >actual &&
+ test_cmp expect.full actual &&
+ git log --oneline --decorate=no >actual &&
+ test_cmp expect.none actual &&
+
+ git config --unset-all log.decorate &&
+ git config log.decorate no &&
+ git log --oneline >actual &&
+ test_cmp expect.none actual &&
+ git log --oneline --decorate >actual &&
+ test_cmp expect.short actual &&
+ git log --oneline --decorate=full >actual &&
+ test_cmp expect.full actual &&
+
+ git config --unset-all log.decorate &&
+ git config log.decorate short &&
+ git log --oneline >actual &&
+ test_cmp expect.short actual &&
+ git log --oneline --no-decorate >actual &&
+ test_cmp expect.none actual &&
+ git log --oneline --decorate=full >actual &&
+ test_cmp expect.full actual &&
+
+ git config --unset-all log.decorate &&
+ git config log.decorate full &&
+ git log --oneline >actual &&
+ test_cmp expect.full actual &&
+ git log --oneline --no-decorate >actual &&
+ test_cmp expect.none actual &&
+ git log --oneline --decorate >actual &&
+ test_cmp expect.short actual
+
+'
+
+test_done
+
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
new file mode 100755
index 0000000..9a7d1b4
--- /dev/null
+++ b/t/t4203-mailmap.sh
@@ -0,0 +1,215 @@
+#!/bin/sh
+
+test_description='.mailmap configurations'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo one >one &&
+ git add one &&
+ test_tick &&
+ git commit -m initial &&
+ echo two >>one &&
+ git add one &&
+ git commit --author "nick1 <bugs@company.xx>" -m second
+'
+
+cat >expect <<\EOF
+A U Thor (1):
+ initial
+
+nick1 (1):
+ second
+
+EOF
+
+test_expect_success 'No mailmap' '
+ git shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Repo Guy (1):
+ initial
+
+nick1 (1):
+ second
+
+EOF
+
+test_expect_success 'default .mailmap' '
+ echo "Repo Guy <author@example.com>" > .mailmap &&
+ git shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+# Using a mailmap file in a subdirectory of the repo here, but
+# could just as well have been a file outside of the repository
+cat >expect <<\EOF
+Internal Guy (1):
+ second
+
+Repo Guy (1):
+ initial
+
+EOF
+test_expect_success 'mailmap.file set' '
+ mkdir internal_mailmap &&
+ echo "Internal Guy <bugs@company.xx>" > internal_mailmap/.mailmap &&
+ git config mailmap.file internal_mailmap/.mailmap &&
+ git shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
+External Guy (1):
+ initial
+
+Internal Guy (1):
+ second
+
+EOF
+test_expect_success 'mailmap.file override' '
+ echo "External Guy <author@example.com>" >> internal_mailmap/.mailmap &&
+ git config mailmap.file internal_mailmap/.mailmap &&
+ git shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Repo Guy (1):
+ initial
+
+nick1 (1):
+ second
+
+EOF
+
+test_expect_success 'mailmap.file non-existant' '
+ rm internal_mailmap/.mailmap &&
+ rmdir internal_mailmap &&
+ git shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
+A U Thor (1):
+ initial
+
+nick1 (1):
+ second
+
+EOF
+test_expect_success 'No mailmap files, but configured' '
+ rm .mailmap &&
+ git shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+# Extended mailmap configurations should give us the following output for shortlog
+cat >expect <<\EOF
+A U Thor <author@example.com> (1):
+ initial
+
+CTO <cto@company.xx> (1):
+ seventh
+
+Other Author <other@author.xx> (2):
+ third
+ fourth
+
+Santa Claus <santa.claus@northpole.xx> (2):
+ fifth
+ sixth
+
+Some Dude <some@dude.xx> (1):
+ second
+
+EOF
+
+test_expect_success 'Shortlog output (complex mapping)' '
+ echo three >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "nick2 <bugs@company.xx>" -m third &&
+
+ echo four >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "nick2 <nick2@company.xx>" -m fourth &&
+
+ echo five >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "santa <me@company.xx>" -m fifth &&
+
+ echo six >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "claus <me@company.xx>" -m sixth &&
+
+ echo seven >>one &&
+ git add one &&
+ test_tick &&
+ git commit --author "CTO <cto@coompany.xx>" -m seventh &&
+
+ mkdir internal_mailmap &&
+ echo "Committed <committer@example.com>" > internal_mailmap/.mailmap &&
+ echo "<cto@company.xx> <cto@coompany.xx>" >> internal_mailmap/.mailmap &&
+ echo "Some Dude <some@dude.xx> nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
+ echo "Other Author <other@author.xx> nick2 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
+ echo "Other Author <other@author.xx> <nick2@company.xx>" >> internal_mailmap/.mailmap &&
+ echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
+ echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
+
+ git shortlog -e HEAD >actual &&
+ test_cmp expect actual
+
+'
+
+# git log with --pretty format which uses the name and email mailmap placemarkers
+cat >expect <<\EOF
+Author CTO <cto@coompany.xx> maps to CTO <cto@company.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author claus <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author santa <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick2 <nick2@company.xx> maps to Other Author <other@author.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick2 <bugs@company.xx> maps to Other Author <other@author.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick1 <bugs@company.xx> maps to Some Dude <some@dude.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author A U Thor <author@example.com> maps to A U Thor <author@example.com>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+EOF
+
+test_expect_success 'Log output (complex mapping)' '
+ git log --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+ test_cmp expect actual
+'
+
+# git blame
+cat >expect <<\EOF
+^3a2fdcb (A U Thor 2005-04-07 15:13:13 -0700 1) one
+7de6f99b (Some Dude 2005-04-07 15:13:13 -0700 2) two
+5815879d (Other Author 2005-04-07 15:14:13 -0700 3) three
+ff859d96 (Other Author 2005-04-07 15:15:13 -0700 4) four
+5ab6d4fa (Santa Claus 2005-04-07 15:16:13 -0700 5) five
+38a42d8b (Santa Claus 2005-04-07 15:17:13 -0700 6) six
+8ddc0386 (CTO 2005-04-07 15:18:13 -0700 7) seven
+EOF
+
+test_expect_success 'Blame output (complex mapping)' '
+ git blame one >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t4204-patch-id.sh b/t/t4204-patch-id.sh
new file mode 100755
index 0000000..68e2652
--- /dev/null
+++ b/t/t4204-patch-id.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+
+test_description='git patch-id'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit initial foo a &&
+ test_commit first foo b &&
+ git checkout -b same HEAD^ &&
+ test_commit same-msg foo b &&
+ git checkout -b notsame HEAD^ &&
+ test_commit notsame-msg foo c
+'
+
+test_expect_success 'patch-id output is well-formed' '
+ git log -p -1 | git patch-id > output &&
+ grep "^[a-f0-9]\{40\} $(git rev-parse HEAD)$" output
+'
+
+calc_patch_id () {
+ git patch-id |
+ sed "s# .*##" > patch-id_"$1"
+}
+
+get_patch_id () {
+ git log -p -1 "$1" | git patch-id |
+ sed "s# .*##" > patch-id_"$1"
+}
+
+test_expect_success 'patch-id detects equality' '
+ get_patch_id master &&
+ get_patch_id same &&
+ test_cmp patch-id_master patch-id_same
+'
+
+test_expect_success 'patch-id detects inequality' '
+ get_patch_id master &&
+ get_patch_id notsame &&
+ ! test_cmp patch-id_master patch-id_notsame
+'
+
+test_expect_success 'patch-id supports git-format-patch output' '
+ get_patch_id master &&
+ git checkout same &&
+ git format-patch -1 --stdout | calc_patch_id same &&
+ test_cmp patch-id_master patch-id_same &&
+ set `git format-patch -1 --stdout | git patch-id` &&
+ test "$2" = `git rev-parse HEAD`
+'
+
+test_expect_success 'whitespace is irrelevant in footer' '
+ get_patch_id master &&
+ git checkout same &&
+ git format-patch -1 --stdout | sed "s/ \$//" | calc_patch_id same &&
+ test_cmp patch-id_master patch-id_same
+'
+
+test_expect_success 'patch-id supports git-format-patch MIME output' '
+ get_patch_id master &&
+ git checkout same &&
+ git format-patch -1 --attach --stdout | calc_patch_id same &&
+ test_cmp patch-id_master patch-id_same
+'
+
+test_done
diff --git a/t/t4205-log-pretty-formats.sh b/t/t4205-log-pretty-formats.sh
new file mode 100755
index 0000000..cb9f2bd
--- /dev/null
+++ b/t/t4205-log-pretty-formats.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# Copyright (c) 2010, Will Palmer
+#
+
+test_description='Test pretty formats'
+. ./test-lib.sh
+
+test_expect_success 'set up basic repos' '
+ >foo &&
+ >bar &&
+ git add foo &&
+ test_tick &&
+ git commit -m initial &&
+ git add bar &&
+ test_tick &&
+ git commit -m "add bar"
+'
+
+test_expect_success 'alias builtin format' '
+ git log --pretty=oneline >expected &&
+ git config pretty.test-alias oneline &&
+ git log --pretty=test-alias >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'alias masking builtin format' '
+ git log --pretty=oneline >expected &&
+ git config pretty.oneline "%H" &&
+ git log --pretty=oneline >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'alias user-defined format' '
+ git log --pretty="format:%h" >expected &&
+ git config pretty.test-alias "format:%h" &&
+ git log --pretty=test-alias >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'alias user-defined tformat' '
+ git log --pretty="tformat:%h" >expected &&
+ git config pretty.test-alias "tformat:%h" &&
+ git log --pretty=test-alias >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'alias non-existant format' '
+ git config pretty.test-alias format-that-will-never-exist &&
+ test_must_fail git log --pretty=test-alias
+'
+
+test_expect_success 'alias of an alias' '
+ git log --pretty="tformat:%h" >expected &&
+ git config pretty.test-foo "tformat:%h" &&
+ git config pretty.test-bar test-foo &&
+ git log --pretty=test-bar >actual && test_cmp expected actual
+'
+
+test_expect_success 'alias masking an alias' '
+ git log --pretty=format:"Two %H" >expected &&
+ git config pretty.duplicate "format:One %H" &&
+ git config --add pretty.duplicate "format:Two %H" &&
+ git log --pretty=duplicate >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'alias loop' '
+ git config pretty.test-foo test-bar &&
+ git config pretty.test-bar test-foo &&
+ test_must_fail git log --pretty=test-foo
+'
+
+test_done
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
new file mode 100755
index 0000000..ad29e65
--- /dev/null
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Bo Yang
+#
+
+test_description='Test --follow should always find copies hard in git log.
+
+'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
+
+echo >path0 'Line 1
+Line 2
+Line 3
+'
+
+test_expect_success \
+ 'add a file path0 and commit.' \
+ 'git add path0 &&
+ git commit -m "Add path0"'
+
+echo >path0 'New line 1
+New line 2
+New line 3
+'
+test_expect_success \
+ 'Change path0.' \
+ 'git add path0 &&
+ git commit -m "Change path0"'
+
+cat <path0 >path1
+test_expect_success \
+ 'copy path0 to path1.' \
+ 'git add path1 &&
+ git commit -m "Copy path1 from path0"'
+
+test_expect_success \
+ 'find the copy path0 -> path1 harder' \
+ 'git log --follow --name-status --pretty="format:%s" path1 > current'
+
+cat >expected <<\EOF
+Copy path1 from path0
+C100 path0 path1
+
+Change path0
+M path0
+
+Add path0
+A path0
+EOF
+
+test_expect_success \
+ 'validate the output.' \
+ 'compare_diff_patch current expected'
+
+test_done
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
new file mode 100755
index 0000000..bbde31b
--- /dev/null
+++ b/t/t4207-log-decoration-colors.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Nazri Ramliy
+#
+
+test_description='Test for "git log --decorate" colors'
+
+. ./test-lib.sh
+
+get_color ()
+{
+ git config --get-color no.such.slot "$1"
+}
+
+test_expect_success setup '
+ git config diff.color.commit yellow &&
+ git config color.decorate.branch green &&
+ git config color.decorate.remoteBranch red &&
+ git config color.decorate.tag "reverse bold yellow" &&
+ git config color.decorate.stash magenta &&
+ git config color.decorate.HEAD cyan &&
+
+ c_reset=$(get_color reset) &&
+
+ c_commit=$(get_color yellow) &&
+ c_branch=$(get_color green) &&
+ c_remoteBranch=$(get_color red) &&
+ c_tag=$(get_color "reverse bold yellow") &&
+ c_stash=$(get_color magenta) &&
+ c_HEAD=$(get_color cyan) &&
+
+ test_commit A &&
+ git clone . other &&
+ (
+ cd other &&
+ test_commit A1
+ ) &&
+
+ git remote add -f other ./other &&
+ test_commit B &&
+ git tag v1.0 &&
+ echo >>A.t &&
+ git stash save Changes to A.t
+'
+
+cat >expected <<EOF
+${c_commit}COMMIT_ID (${c_HEAD}HEAD${c_reset}${c_commit},\
+ ${c_tag}tag: v1.0${c_reset}${c_commit},\
+ ${c_tag}tag: B${c_reset}${c_commit},\
+ ${c_branch}master${c_reset}${c_commit})${c_reset} B
+${c_commit}COMMIT_ID (${c_tag}tag: A1${c_reset}${c_commit},\
+ ${c_remoteBranch}other/master${c_reset}${c_commit})${c_reset} A1
+${c_commit}COMMIT_ID (${c_stash}refs/stash${c_reset}${c_commit})${c_reset}\
+ On master: Changes to A.t
+${c_commit}COMMIT_ID (${c_tag}tag: A${c_reset}${c_commit})${c_reset} A
+EOF
+
+# We want log to show all, but the second parent to refs/stash is irrelevant
+# to this test since it does not contain any decoration, hence --first-parent
+test_expect_success 'Commit Decorations Colored Correctly' '
+ git log --first-parent --abbrev=10 --all --decorate --oneline --color=always |
+ sed "s/[0-9a-f]\{10,10\}/COMMIT_ID/" >out &&
+ test_cmp expected out
+'
+
+test_done
diff --git a/t/t4252-am-options.sh b/t/t4252-am-options.sh
new file mode 100755
index 0000000..f603c1b
--- /dev/null
+++ b/t/t4252-am-options.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+test_description='git am with options and not losing them'
+. ./test-lib.sh
+
+tm="$TEST_DIRECTORY/t4252"
+
+test_expect_success setup '
+ cp "$tm/file-1-0" file-1 &&
+ cp "$tm/file-2-0" file-2 &&
+ git add file-1 file-2 &&
+ test_tick &&
+ git commit -m initial &&
+ git tag initial
+'
+
+test_expect_success 'interrupted am --whitespace=fix' '
+ rm -rf .git/rebase-apply &&
+ git reset --hard initial &&
+ test_must_fail git am --whitespace=fix "$tm"/am-test-1-? &&
+ git am --skip &&
+ grep 3 file-1 &&
+ grep "^Six$" file-2
+'
+
+test_expect_success 'interrupted am -C1' '
+ rm -rf .git/rebase-apply &&
+ git reset --hard initial &&
+ test_must_fail git am -C1 "$tm"/am-test-2-? &&
+ git am --skip &&
+ grep 3 file-1 &&
+ grep "^Three$" file-2
+'
+
+test_expect_success 'interrupted am -p2' '
+ rm -rf .git/rebase-apply &&
+ git reset --hard initial &&
+ test_must_fail git am -p2 "$tm"/am-test-3-? &&
+ git am --skip &&
+ grep 3 file-1 &&
+ grep "^Three$" file-2
+'
+
+test_expect_success 'interrupted am -C1 -p2' '
+ rm -rf .git/rebase-apply &&
+ git reset --hard initial &&
+ test_must_fail git am -p2 -C1 "$tm"/am-test-4-? &&
+ git am --skip &&
+ grep 3 file-1 &&
+ grep "^Three$" file-2
+'
+
+test_expect_success 'interrupted am --directory="frotz nitfol"' '
+ rm -rf .git/rebase-apply &&
+ git reset --hard initial &&
+ test_must_fail git am --directory="frotz nitfol" "$tm"/am-test-5-? &&
+ git am --skip &&
+ grep One "frotz nitfol/file-5"
+'
+
+test_expect_success 'apply to a funny path' '
+ with_sq="with'\''sq"
+ rm -fr .git/rebase-apply &&
+ git reset --hard initial &&
+ git am --directory="$with_sq" "$tm"/am-test-5-2 &&
+ test -f "$with_sq/file-5"
+'
+
+test_expect_success 'am --reject' '
+ rm -rf .git/rebase-apply &&
+ git reset --hard initial &&
+ test_must_fail git am --reject "$tm"/am-test-6-1 &&
+ grep "@@ -1,3 +1,3 @@" file-2.rej &&
+ test_must_fail git diff-files --exit-code --quiet file-2 &&
+ grep "[-]-reject" .git/rebase-apply/apply-opt
+'
+
+test_done
diff --git a/t/t4252/am-test-1-1 b/t/t4252/am-test-1-1
new file mode 100644
index 0000000..b0c09dc
--- /dev/null
+++ b/t/t4252/am-test-1-1
@@ -0,0 +1,19 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Three
+
+Application of this should be rejected because the first line in the
+context does not match.
+
+diff --git i/file-1 w/file-1
+index 06e567b..10f8342 100644
+--- i/file-1
++++ w/file-1
+@@ -1,6 +1,6 @@
+ One
+ 2
+-3
++Three
+ 4
+ 5
+ 6
diff --git a/t/t4252/am-test-1-2 b/t/t4252/am-test-1-2
new file mode 100644
index 0000000..1b874ae
--- /dev/null
+++ b/t/t4252/am-test-1-2
@@ -0,0 +1,21 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with --whitespace=fix should lose
+the trailing whitespace after "Six".
+
+diff --git i/file-2 w/file-2
+index 06e567b..b6f3a16 100644
+--- i/file-2
++++ w/file-2
+@@ -1,7 +1,7 @@
+ 1
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/am-test-2-1 b/t/t4252/am-test-2-1
new file mode 100644
index 0000000..feda94a
--- /dev/null
+++ b/t/t4252/am-test-2-1
@@ -0,0 +1,19 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Three
+
+Application of this should be rejected even with -C1 because the
+preimage line in the context does not match.
+
+diff --git i/file-1 w/file-1
+index 06e567b..10f8342 100644
+--- i/file-1
++++ w/file-1
+@@ -1,6 +1,6 @@
+ 1
+ 2
+-Tres
++Three
+ 4
+ 5
+ 6
diff --git a/t/t4252/am-test-2-2 b/t/t4252/am-test-2-2
new file mode 100644
index 0000000..2ac6600
--- /dev/null
+++ b/t/t4252/am-test-2-2
@@ -0,0 +1,21 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with -C1 should be successful even though
+the first line in the context does not match.
+
+diff --git i/file-2 w/file-2
+index 06e567b..b6f3a16 100644
+--- i/file-2
++++ w/file-2
+@@ -1,7 +1,7 @@
+ One
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/am-test-3-1 b/t/t4252/am-test-3-1
new file mode 100644
index 0000000..608e5ab
--- /dev/null
+++ b/t/t4252/am-test-3-1
@@ -0,0 +1,19 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Three
+
+Application of this should be rejected even with -p2 because the
+preimage line in the context does not match.
+
+diff --git i/junk/file-1 w/junk/file-1
+index 06e567b..10f8342 100644
+--- i/junk/file-1
++++ w/junk/file-1
+@@ -1,6 +1,6 @@
+ 1
+ 2
+-Tres
++Three
+ 4
+ 5
+ 6
diff --git a/t/t4252/am-test-3-2 b/t/t4252/am-test-3-2
new file mode 100644
index 0000000..0081b96
--- /dev/null
+++ b/t/t4252/am-test-3-2
@@ -0,0 +1,21 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with -p2 should be successful even though
+the patch is against a wrong level.
+
+diff --git i/junk/file-2 w/junk/file-2
+index 06e567b..b6f3a16 100644
+--- i/junk/file-2
++++ w/junk/file-2
+@@ -1,7 +1,7 @@
+ 1
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/am-test-4-1 b/t/t4252/am-test-4-1
new file mode 100644
index 0000000..e48cd6c
--- /dev/null
+++ b/t/t4252/am-test-4-1
@@ -0,0 +1,19 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Three
+
+Application of this should be rejected even with -C1 -p2 because
+the preimage line in the context does not match.
+
+diff --git i/junk/file-1 w/junk/file-1
+index 06e567b..10f8342 100644
+--- i/junk/file-1
++++ w/junk/file-1
+@@ -1,6 +1,6 @@
+ 1
+ 2
+-Tres
++Three
+ 4
+ 5
+ 6
diff --git a/t/t4252/am-test-4-2 b/t/t4252/am-test-4-2
new file mode 100644
index 0000000..0e69bfa
--- /dev/null
+++ b/t/t4252/am-test-4-2
@@ -0,0 +1,22 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with -C1 -p2 should be successful even though
+the patch is against a wrong level and the first context line does
+not match.
+
+diff --git i/junk/file-2 w/junk/file-2
+index 06e567b..b6f3a16 100644
+--- i/junk/file-2
++++ w/junk/file-2
+@@ -1,7 +1,7 @@
+ One
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/am-test-5-1 b/t/t4252/am-test-5-1
new file mode 100644
index 0000000..da7bf29
--- /dev/null
+++ b/t/t4252/am-test-5-1
@@ -0,0 +1,20 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with --directory='frotz nitfol' should fail
+
+diff --git i/junk/file-2 w/junk/file-2
+index 06e567b..b6f3a16 100644
+--- i/junk/file-2
++++ w/junk/file-2
+@@ -1,7 +1,7 @@
+ One
+ 2
+-3
++Three
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/am-test-5-2 b/t/t4252/am-test-5-2
new file mode 100644
index 0000000..373025b
--- /dev/null
+++ b/t/t4252/am-test-5-2
@@ -0,0 +1,15 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Six
+
+Applying this patch with --directory='frotz nitfol' should succeed
+
+diff --git i/file-5 w/file-5
+new file mode 100644
+index 000000..1d6ed9f
+--- /dev/null
++++ w/file-5
+@@ -0,0 +1,3 @@
++One
++two
++three
diff --git a/t/t4252/am-test-6-1 b/t/t4252/am-test-6-1
new file mode 100644
index 0000000..a8859e9
--- /dev/null
+++ b/t/t4252/am-test-6-1
@@ -0,0 +1,21 @@
+From: A U Thor <au.thor@example.com>
+Date: Thu Dec 4 16:00:00 2008 -0800
+Subject: Huh
+
+Should fail and leave rejects
+
+diff --git i/file-2 w/file-2
+index 06e567b..b6f3a16 100644
+--- i/file-2
++++ w/file-2
+@@ -1,3 +1,3 @@
+-0
++One
+ 2
+ 3
+@@ -4,4 +4,4 @@
+ 4
+ 5
+-6
++Six
+ 7
diff --git a/t/t4252/file-1-0 b/t/t4252/file-1-0
new file mode 100644
index 0000000..06e567b
--- /dev/null
+++ b/t/t4252/file-1-0
@@ -0,0 +1,7 @@
+1
+2
+3
+4
+5
+6
+7
diff --git a/t/t4252/file-2-0 b/t/t4252/file-2-0
new file mode 100644
index 0000000..06e567b
--- /dev/null
+++ b/t/t4252/file-2-0
@@ -0,0 +1,7 @@
+1
+2
+3
+4
+5
+6
+7
diff --git a/t/t4253-am-keep-cr-dos.sh b/t/t4253-am-keep-cr-dos.sh
new file mode 100755
index 0000000..735e55d
--- /dev/null
+++ b/t/t4253-am-keep-cr-dos.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Stefan-W. Hahn
+#
+
+test_description='git-am mbox with dos line ending.
+
+'
+. ./test-lib.sh
+
+# Three patches which will be added as files with dos line ending.
+
+cat >file1 <<\EOF
+line 1
+EOF
+
+cat >file1a <<\EOF
+line 1
+line 4
+EOF
+
+cat >file2 <<\EOF
+line 1
+line 2
+EOF
+
+cat >file3 <<\EOF
+line 1
+line 2
+line 3
+EOF
+
+test_expect_success 'setup repository with dos files' '
+ append_cr <file1 >file &&
+ git add file &&
+ git commit -m Initial &&
+ git tag initial &&
+ append_cr <file2 >file &&
+ git commit -a -m Second &&
+ append_cr <file3 >file &&
+ git commit -a -m Third
+'
+
+test_expect_success 'am with dos files without --keep-cr' '
+ git checkout -b dosfiles initial &&
+ git format-patch -k initial..master &&
+ test_must_fail git am -k -3 000*.patch &&
+ git am --abort &&
+ rm -rf .git/rebase-apply 000*.patch
+'
+
+test_expect_success 'am with dos files with --keep-cr' '
+ git checkout -b dosfiles-keep-cr initial &&
+ git format-patch -k --stdout initial..master | git am --keep-cr -k -3 &&
+ git diff --exit-code master
+'
+
+test_expect_success 'am with dos files config am.keepcr' '
+ git config am.keepcr 1 &&
+ git checkout -b dosfiles-conf-keepcr initial &&
+ git format-patch -k --stdout initial..master | git am -k -3 &&
+ git diff --exit-code master
+'
+
+test_expect_success 'am with dos files config am.keepcr overriden by --no-keep-cr' '
+ git config am.keepcr 1 &&
+ git checkout -b dosfiles-conf-keepcr-override initial &&
+ git format-patch -k initial..master &&
+ test_must_fail git am -k -3 --no-keep-cr 000*.patch &&
+ git am --abort &&
+ rm -rf .git/rebase-apply 000*.patch
+'
+
+test_expect_success 'am with dos files with --keep-cr continue' '
+ git checkout -b dosfiles-keep-cr-continue initial &&
+ git format-patch -k initial..master &&
+ append_cr <file1a >file &&
+ git commit -m "different patch" file &&
+ test_must_fail git am --keep-cr -k -3 000*.patch &&
+ append_cr <file2 >file &&
+ git add file &&
+ git am -3 --resolved &&
+ git diff --exit-code master
+'
+
+test_expect_success 'am with unix files config am.keepcr overriden by --no-keep-cr' '
+ git config am.keepcr 1 &&
+ git checkout -b unixfiles-conf-keepcr-override initial &&
+ cp -f file1 file &&
+ git commit -m "line ending to unix" file &&
+ git format-patch -k initial..master &&
+ git am -k -3 --no-keep-cr 000*.patch &&
+ git diff --exit-code -w master
+'
+
+test_done
diff --git a/t/t4300-merge-tree.sh b/t/t4300-merge-tree.sh
new file mode 100755
index 0000000..46c3fe7
--- /dev/null
+++ b/t/t4300-merge-tree.sh
@@ -0,0 +1,257 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Will Palmer
+#
+
+test_description='git merge-tree'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit "initial" "initial-file" "initial"
+'
+
+test_expect_success 'file add A, !B' '
+ cat >expected <<\EXPECTED &&
+added in remote
+ their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+@@ -0,0 +1 @@
++AAA
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "add-a-not-b" "ONE" "AAA" &&
+ git merge-tree initial initial add-a-not-b >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file add !A, B' '
+ cat >expected <<\EXPECTED &&
+added in local
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "add-not-a-b" "ONE" "AAA" &&
+ git merge-tree initial add-not-a-b initial >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file add A, B (same)' '
+ cat >expected <<\EXPECTED &&
+added in both
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "add-a-b-same-A" "ONE" "AAA" &&
+ git reset --hard initial &&
+ test_commit "add-a-b-same-B" "ONE" "AAA" &&
+ git merge-tree initial add-a-b-same-A add-a-b-same-B >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file add A, B (different)' '
+ cat >expected <<\EXPECTED &&
+added in both
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ their 100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+@@ -1 +1,5 @@
++<<<<<<< .our
+ AAA
++=======
++BBB
++>>>>>>> .their
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "add-a-b-diff-A" "ONE" "AAA" &&
+ git reset --hard initial &&
+ test_commit "add-a-b-diff-B" "ONE" "BBB" &&
+ git merge-tree initial add-a-b-diff-A add-a-b-diff-B >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, !B' '
+ cat >expected <<\EXPECTED &&
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-not-b" "initial-file" "BBB" &&
+ git merge-tree initial change-a-not-b initial >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change !A, B' '
+ cat >expected <<\EXPECTED &&
+merged
+ result 100644 ba629238ca89489f2b350e196ca445e09d8bb834 initial-file
+ our 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+@@ -1 +1 @@
+-initial
++BBB
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-not-a-b" "initial-file" "BBB" &&
+ git merge-tree initial initial change-not-a-b >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, B (same)' '
+ cat >expected <<\EXPECTED &&
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-b-same-A" "initial-file" "AAA" &&
+ git reset --hard initial &&
+ test_commit "change-a-b-same-B" "initial-file" "AAA" &&
+ git merge-tree initial change-a-b-same-A change-a-b-same-B >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, B (different)' '
+ cat >expected <<\EXPECTED &&
+changed in both
+ base 100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d initial-file
+ their 100644 ba629238ca89489f2b350e196ca445e09d8bb834 initial-file
+@@ -1 +1,5 @@
++<<<<<<< .our
+ AAA
++=======
++BBB
++>>>>>>> .their
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-b-diff-A" "initial-file" "AAA" &&
+ git reset --hard initial &&
+ test_commit "change-a-b-diff-B" "initial-file" "BBB" &&
+ git merge-tree initial change-a-b-diff-A change-a-b-diff-B >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, B (mixed)' '
+ cat >expected <<\EXPECTED &&
+changed in both
+ base 100644 f4f1f998c7776568c4ff38f516d77fef9399b5a7 ONE
+ our 100644 af14c2c3475337c73759d561ef70b59e5c731176 ONE
+ their 100644 372d761493f524d44d59bd24700c3bdf914c973c ONE
+@@ -7,7 +7,11 @@
+ AAA
+ AAA
+ AAA
++<<<<<<< .our
+ BBB
++=======
++CCC
++>>>>>>> .their
+ AAA
+ AAA
+ AAA
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-b-mix-base" "ONE" "
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA
+AAA" &&
+ test_commit "change-a-b-mix-A" "ONE" \
+ "$(sed -e "1{s/AAA/BBB/;}" -e "10{s/AAA/BBB/;}" <ONE)" &&
+ git reset --hard change-a-b-mix-base &&
+ test_commit "change-a-b-mix-B" "ONE" \
+ "$(sed -e "1{s/AAA/BBB/;}" -e "10{s/AAA/CCC/;}" <ONE)" &&
+ git merge-tree change-a-b-mix-base change-a-b-mix-A change-a-b-mix-B \
+ >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file remove A, !B' '
+ cat >expected <<\EXPECTED &&
+removed in local
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ their 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "rm-a-not-b-base" "ONE" "AAA" &&
+ git rm ONE &&
+ git commit -m "rm-a-not-b" &&
+ git tag "rm-a-not-b" &&
+ git merge-tree rm-a-not-b-base rm-a-not-b rm-a-not-b-base >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file remove !A, B' '
+ cat >expected <<\EXPECTED &&
+removed in remote
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ our 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+@@ -1 +0,0 @@
+-AAA
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "rm-not-a-b-base" "ONE" "AAA" &&
+ git rm ONE &&
+ git commit -m "rm-not-a-b" &&
+ git tag "rm-not-a-b" &&
+ git merge-tree rm-a-not-b-base rm-a-not-b-base rm-a-not-b >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file change A, remove B' '
+ cat >expected <<\EXPECTED &&
+removed in remote
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ our 100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+@@ -1 +0,0 @@
+-BBB
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "change-a-rm-b-base" "ONE" "AAA" &&
+ test_commit "change-a-rm-b-A" "ONE" "BBB" &&
+ git reset --hard change-a-rm-b-base &&
+ git rm ONE &&
+ git commit -m "change-a-rm-b-B" &&
+ git tag "change-a-rm-b-B" &&
+ git merge-tree change-a-rm-b-base change-a-rm-b-A change-a-rm-b-B \
+ >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'file remove A, change B' '
+ cat >expected <<\EXPECTED &&
+removed in local
+ base 100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+ their 100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+EXPECTED
+
+ git reset --hard initial &&
+ test_commit "rm-a-change-b-base" "ONE" "AAA" &&
+
+ git rm ONE &&
+ git commit -m "rm-a-change-b-A" &&
+ git tag "rm-a-change-b-A" &&
+ git reset --hard rm-a-change-b-base &&
+ test_commit "rm-a-change-b-B" "ONE" "BBB" &&
+ git merge-tree rm-a-change-b-base rm-a-change-b-A rm-a-change-b-B \
+ >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
new file mode 100755
index 0000000..27bfba5
--- /dev/null
+++ b/t/t5000-tar-tree.sh
@@ -0,0 +1,255 @@
+#!/bin/sh
+#
+# Copyright (C) 2005 Rene Scharfe
+#
+
+test_description='git tar-tree and git get-tar-commit-id test
+
+This test covers the topics of file contents, commit date handling and
+commit id embedding:
+
+ The contents of the repository is compared to the extracted tar
+ archive. The repository contains simple text files, symlinks and a
+ binary file (/bin/sh). Only paths shorter than 99 characters are
+ used.
+
+ git tar-tree applies the commit date to every file in the archive it
+ creates. The test sets the commit date to a specific value and checks
+ if the tar archive contains that value.
+
+ When giving git tar-tree a commit id (in contrast to a tree id) it
+ embeds this commit id into the tar archive as a comment. The test
+ checks the ability of git get-tar-commit-id to figure it out from the
+ tar file.
+
+'
+
+. ./test-lib.sh
+UNZIP=${UNZIP:-unzip}
+
+SUBSTFORMAT=%H%n
+
+test_expect_success \
+ 'populate workdir' \
+ 'mkdir a b c &&
+ echo simple textfile >a/a &&
+ mkdir a/bin &&
+ cp /bin/sh a/bin &&
+ printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile1 &&
+ printf "A not substituted O" >a/substfile2 &&
+ if test_have_prereq SYMLINKS; then
+ ln -s a a/l1
+ else
+ printf %s a > a/l1
+ fi &&
+ (p=long_path_to_a_file && cd a &&
+ for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
+ echo text >file_with_long_path) &&
+ (cd a && find .) | sort >a.lst'
+
+test_expect_success \
+ 'add ignored file' \
+ 'echo ignore me >a/ignored &&
+ echo ignored export-ignore >.git/info/attributes'
+
+test_expect_success \
+ 'add files to repository' \
+ 'find a -type f | xargs git update-index --add &&
+ find a -type l | xargs git update-index --add &&
+ treeid=`git write-tree` &&
+ echo $treeid >treeid &&
+ git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
+ git commit-tree $treeid </dev/null)'
+
+test_expect_success \
+ 'create bare clone' \
+ 'git clone --bare . bare.git &&
+ cp .git/info/attributes bare.git/info/attributes'
+
+test_expect_success \
+ 'remove ignored file' \
+ 'rm a/ignored'
+
+test_expect_success \
+ 'git archive' \
+ 'git archive HEAD >b.tar'
+
+test_expect_success \
+ 'git tar-tree' \
+ 'git tar-tree HEAD >b2.tar'
+
+test_expect_success \
+ 'git archive vs. git tar-tree' \
+ 'test_cmp b.tar b2.tar'
+
+test_expect_success \
+ 'git archive in a bare repo' \
+ '(cd bare.git && git archive HEAD) >b3.tar'
+
+test_expect_success \
+ 'git archive vs. the same in a bare repo' \
+ 'test_cmp b.tar b3.tar'
+
+test_expect_success 'git archive with --output' \
+ 'git archive --output=b4.tar HEAD &&
+ test_cmp b.tar b4.tar'
+
+test_expect_success 'git archive --remote' \
+ 'git archive --remote=. HEAD >b5.tar &&
+ test_cmp b.tar b5.tar'
+
+test_expect_success \
+ 'validate file modification time' \
+ 'mkdir extract &&
+ "$TAR" xf b.tar -C extract a/a &&
+ test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime &&
+ echo "1117231200" >expected.mtime &&
+ test_cmp expected.mtime b.mtime'
+
+test_expect_success \
+ 'git get-tar-commit-id' \
+ 'git get-tar-commit-id <b.tar >b.commitid &&
+ test_cmp .git/$(git symbolic-ref HEAD) b.commitid'
+
+test_expect_success \
+ 'extract tar archive' \
+ '(cd b && "$TAR" xf -) <b.tar'
+
+test_expect_success \
+ 'validate filenames' \
+ '(cd b/a && find .) | sort >b.lst &&
+ test_cmp a.lst b.lst'
+
+test_expect_success \
+ 'validate file contents' \
+ 'diff -r a b/a'
+
+test_expect_success \
+ 'git tar-tree with prefix' \
+ 'git tar-tree HEAD prefix >c.tar'
+
+test_expect_success \
+ 'extract tar archive with prefix' \
+ '(cd c && "$TAR" xf -) <c.tar'
+
+test_expect_success \
+ 'validate filenames with prefix' \
+ '(cd c/prefix/a && find .) | sort >c.lst &&
+ test_cmp a.lst c.lst'
+
+test_expect_success \
+ 'validate file contents with prefix' \
+ 'diff -r a c/prefix/a'
+
+test_expect_success \
+ 'create archives with substfiles' \
+ 'cp .git/info/attributes .git/info/attributes.before &&
+ echo "substfile?" export-subst >>.git/info/attributes &&
+ git archive HEAD >f.tar &&
+ git archive --prefix=prefix/ HEAD >g.tar &&
+ mv .git/info/attributes.before .git/info/attributes'
+
+test_expect_success \
+ 'extract substfiles' \
+ '(mkdir f && cd f && "$TAR" xf -) <f.tar'
+
+test_expect_success \
+ 'validate substfile contents' \
+ 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
+ >f/a/substfile1.expected &&
+ test_cmp f/a/substfile1.expected f/a/substfile1 &&
+ test_cmp a/substfile2 f/a/substfile2
+'
+
+test_expect_success \
+ 'extract substfiles from archive with prefix' \
+ '(mkdir g && cd g && "$TAR" xf -) <g.tar'
+
+test_expect_success \
+ 'validate substfile contents from archive with prefix' \
+ 'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
+ >g/prefix/a/substfile1.expected &&
+ test_cmp g/prefix/a/substfile1.expected g/prefix/a/substfile1 &&
+ test_cmp a/substfile2 g/prefix/a/substfile2
+'
+
+test_expect_success \
+ 'git archive --format=zip' \
+ 'git archive --format=zip HEAD >d.zip'
+
+test_expect_success \
+ 'git archive --format=zip in a bare repo' \
+ '(cd bare.git && git archive --format=zip HEAD) >d1.zip'
+
+test_expect_success \
+ 'git archive --format=zip vs. the same in a bare repo' \
+ 'test_cmp d.zip d1.zip'
+
+test_expect_success 'git archive --format=zip with --output' \
+ 'git archive --format=zip --output=d2.zip HEAD &&
+ test_cmp d.zip d2.zip'
+
+test_expect_success 'git archive with --output, inferring format' '
+ git archive --output=d3.zip HEAD &&
+ test_cmp d.zip d3.zip
+'
+
+test_expect_success 'git archive with --output, override inferred format' '
+ git archive --format=tar --output=d4.zip HEAD &&
+ test_cmp b.tar d4.zip
+'
+
+$UNZIP -v >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+ say "Skipping ZIP tests, because unzip was not found"
+else
+ test_set_prereq UNZIP
+fi
+
+test_expect_success UNZIP \
+ 'extract ZIP archive' \
+ '(mkdir d && cd d && $UNZIP ../d.zip)'
+
+test_expect_success UNZIP \
+ 'validate filenames' \
+ '(cd d/a && find .) | sort >d.lst &&
+ test_cmp a.lst d.lst'
+
+test_expect_success UNZIP \
+ 'validate file contents' \
+ 'diff -r a d/a'
+
+test_expect_success \
+ 'git archive --format=zip with prefix' \
+ 'git archive --format=zip --prefix=prefix/ HEAD >e.zip'
+
+test_expect_success UNZIP \
+ 'extract ZIP archive with prefix' \
+ '(mkdir e && cd e && $UNZIP ../e.zip)'
+
+test_expect_success UNZIP \
+ 'validate filenames with prefix' \
+ '(cd e/prefix/a && find .) | sort >e.lst &&
+ test_cmp a.lst e.lst'
+
+test_expect_success UNZIP \
+ 'validate file contents with prefix' \
+ 'diff -r a e/prefix/a'
+
+test_expect_success \
+ 'git archive --list outside of a git repo' \
+ 'GIT_DIR=some/non-existing/directory git archive --list'
+
+test_expect_success 'git-archive --prefix=olde-' '
+ git archive --prefix=olde- >h.tar HEAD &&
+ (
+ mkdir h &&
+ cd h &&
+ "$TAR" xf - <../h.tar
+ ) &&
+ test -d h/olde-a &&
+ test -d h/olde-a/bin &&
+ test -f h/olde-a/bin/sh
+'
+
+test_done
diff --git a/t/t5001-archive-attr.sh b/t/t5001-archive-attr.sh
new file mode 100755
index 0000000..426b319
--- /dev/null
+++ b/t/t5001-archive-attr.sh
@@ -0,0 +1,91 @@
+#!/bin/sh
+
+test_description='git archive attribute tests'
+
+. ./test-lib.sh
+
+SUBSTFORMAT=%H%n
+
+test_expect_exists() {
+ test_expect_success " $1 exists" "test -e $1"
+}
+
+test_expect_missing() {
+ test_expect_success " $1 does not exist" "test ! -e $1"
+}
+
+test_expect_success 'setup' '
+ echo ignored >ignored &&
+ echo ignored export-ignore >>.git/info/attributes &&
+ git add ignored &&
+
+ echo ignored by tree >ignored-by-tree &&
+ echo ignored-by-tree export-ignore >.gitattributes &&
+ git add ignored-by-tree .gitattributes &&
+
+ echo ignored by worktree >ignored-by-worktree &&
+ echo ignored-by-worktree export-ignore >.gitattributes &&
+ git add ignored-by-worktree &&
+
+ printf "A\$Format:%s\$O" "$SUBSTFORMAT" >nosubstfile &&
+ printf "A\$Format:%s\$O" "$SUBSTFORMAT" >substfile1 &&
+ printf "A not substituted O" >substfile2 &&
+ echo "substfile?" export-subst >>.git/info/attributes &&
+ git add nosubstfile substfile1 substfile2 &&
+
+ git commit -m. &&
+
+ git clone --bare . bare &&
+ cp .git/info/attributes bare/info/attributes
+'
+
+test_expect_success 'git archive' '
+ git archive HEAD >archive.tar &&
+ (mkdir archive && cd archive && "$TAR" xf -) <archive.tar
+'
+
+test_expect_missing archive/ignored
+test_expect_missing archive/ignored-by-tree
+test_expect_exists archive/ignored-by-worktree
+
+test_expect_success 'git archive with worktree attributes' '
+ git archive --worktree-attributes HEAD >worktree.tar &&
+ (mkdir worktree && cd worktree && "$TAR" xf -) <worktree.tar
+'
+
+test_expect_missing worktree/ignored
+test_expect_exists worktree/ignored-by-tree
+test_expect_missing worktree/ignored-by-worktree
+
+test_expect_success 'git archive vs. bare' '
+ (cd bare && git archive HEAD) >bare-archive.tar &&
+ test_cmp archive.tar bare-archive.tar
+'
+
+test_expect_success 'git archive with worktree attributes, bare' '
+ (cd bare && git archive --worktree-attributes HEAD) >bare-worktree.tar &&
+ (mkdir bare-worktree && cd bare-worktree && "$TAR" xf -) <bare-worktree.tar
+'
+
+test_expect_missing bare-worktree/ignored
+test_expect_exists bare-worktree/ignored-by-tree
+test_expect_exists bare-worktree/ignored-by-worktree
+
+test_expect_success 'export-subst' '
+ git log "--pretty=format:A${SUBSTFORMAT}O" HEAD >substfile1.expected &&
+ test_cmp nosubstfile archive/nosubstfile &&
+ test_cmp substfile1.expected archive/substfile1 &&
+ test_cmp substfile2 archive/substfile2
+'
+
+test_expect_success 'git tar-tree vs. git archive with worktree attributes' '
+ git tar-tree HEAD >tar-tree.tar &&
+ test_cmp worktree.tar tar-tree.tar
+'
+
+test_expect_success 'git tar-tree vs. git archive with worktree attrs, bare' '
+ (cd bare && git tar-tree HEAD) >bare-tar-tree.tar &&
+ test_cmp bare-worktree.tar bare-tar-tree.tar
+'
+
+test_done
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
new file mode 100755
index 0000000..ebc36c1
--- /dev/null
+++ b/t/t5100-mailinfo.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git mailinfo and git mailsplit test'
+
+. ./test-lib.sh
+
+test_expect_success 'split sample box' \
+ 'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
+ last=`cat last` &&
+ echo total is $last &&
+ test `cat last` = 16'
+
+check_mailinfo () {
+ mail=$1 opt=$2
+ mo="$mail$opt"
+ git mailinfo -u $opt msg$mo patch$mo <$mail >info$mo &&
+ test_cmp "$TEST_DIRECTORY"/t5100/msg$mo msg$mo &&
+ test_cmp "$TEST_DIRECTORY"/t5100/patch$mo patch$mo &&
+ test_cmp "$TEST_DIRECTORY"/t5100/info$mo info$mo
+}
+
+
+for mail in `echo 00*`
+do
+ test_expect_success "mailinfo $mail" '
+ check_mailinfo $mail "" &&
+ if test -f "$TEST_DIRECTORY"/t5100/msg$mail--scissors
+ then
+ check_mailinfo $mail --scissors
+ fi &&
+ if test -f "$TEST_DIRECTORY"/t5100/msg$mail--no-inbody-headers
+ then
+ check_mailinfo $mail --no-inbody-headers
+ fi
+ '
+done
+
+
+test_expect_success 'split box with rfc2047 samples' \
+ 'mkdir rfc2047 &&
+ git mailsplit -orfc2047 "$TEST_DIRECTORY"/t5100/rfc2047-samples.mbox \
+ >rfc2047/last &&
+ last=`cat rfc2047/last` &&
+ echo total is $last &&
+ test `cat rfc2047/last` = 11'
+
+for mail in `echo rfc2047/00*`
+do
+ test_expect_success "mailinfo $mail" '
+ git mailinfo -u $mail-msg $mail-patch <$mail >$mail-info &&
+ echo msg &&
+ test_cmp "$TEST_DIRECTORY"/t5100/empty $mail-msg &&
+ echo patch &&
+ test_cmp "$TEST_DIRECTORY"/t5100/empty $mail-patch &&
+ echo info &&
+ test_cmp "$TEST_DIRECTORY"/t5100/rfc2047-info-$(basename $mail) $mail-info
+ '
+done
+
+test_expect_success 'respect NULs' '
+
+ git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain &&
+ test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 &&
+ (cat 001 | git mailinfo msg patch) &&
+ test 4 = $(wc -l < patch)
+
+'
+
+test_expect_success 'Preserve NULs out of MIME encoded message' '
+
+ git mailsplit -d5 -o. "$TEST_DIRECTORY"/t5100/nul-b64.in &&
+ test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.in 00001 &&
+ git mailinfo msg patch <00001 &&
+ test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.expect patch
+
+'
+
+test_expect_success 'mailinfo on from header without name works' '
+
+ mkdir info-from &&
+ git mailsplit -oinfo-from "$TEST_DIRECTORY"/t5100/info-from.in &&
+ test_cmp "$TEST_DIRECTORY"/t5100/info-from.in info-from/0001 &&
+ git mailinfo info-from/msg info-from/patch \
+ <info-from/0001 >info-from/out &&
+ test_cmp "$TEST_DIRECTORY"/t5100/info-from.expect info-from/out
+
+'
+
+test_done
diff --git a/t/t5100/.gitattributes b/t/t5100/.gitattributes
new file mode 100644
index 0000000..c93f514
--- /dev/null
+++ b/t/t5100/.gitattributes
@@ -0,0 +1,4 @@
+msg* encoding=UTF-8
+info* encoding=UTF-8
+rfc2047-info-* encoding=UTF-8
+sample.mbox encoding=UTF-8
diff --git a/t/t5100/empty b/t/t5100/empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/t/t5100/empty
diff --git a/t/t5100/info-from.expect b/t/t5100/info-from.expect
new file mode 100644
index 0000000..c31d2eb
--- /dev/null
+++ b/t/t5100/info-from.expect
@@ -0,0 +1,5 @@
+Author: bare@example.com
+Email: bare@example.com
+Subject: testing bare address in from header
+Date: Sun, 25 May 2008 00:38:18 -0700
+
diff --git a/t/t5100/info-from.in b/t/t5100/info-from.in
new file mode 100644
index 0000000..4f08209
--- /dev/null
+++ b/t/t5100/info-from.in
@@ -0,0 +1,8 @@
+From 667d8940e719cddee1cfe237cbbe215e20270b09 Mon Sep 17 00:00:00 2001
+From: bare@example.com
+Date: Sun, 25 May 2008 00:38:18 -0700
+Subject: [PATCH] testing bare address in from header
+
+commit message
+---
+patch
diff --git a/t/t5100/info0001 b/t/t5100/info0001
new file mode 100644
index 0000000..f951538
--- /dev/null
+++ b/t/t5100/info0001
@@ -0,0 +1,5 @@
+Author: A (zzz) U Thor (Comment)
+Email: a.u.thor@example.com
+Subject: a commit.
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0002 b/t/t5100/info0002
new file mode 100644
index 0000000..49bb0fe
--- /dev/null
+++ b/t/t5100/info0002
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: another patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0003 b/t/t5100/info0003
new file mode 100644
index 0000000..bd0d122
--- /dev/null
+++ b/t/t5100/info0003
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: third patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0004 b/t/t5100/info0004
new file mode 100644
index 0000000..616c309
--- /dev/null
+++ b/t/t5100/info0004
@@ -0,0 +1,5 @@
+Author: YOSHIFUJI Hideaki / å‰è—¤è‹±æ˜Ž
+Email: yoshfuji@linux-ipv6.org
+Subject: GIT: Try all addresses for given remote name
+Date: Thu, 21 Jul 2005 09:10:36 -0400 (EDT)
+
diff --git a/t/t5100/info0005 b/t/t5100/info0005
new file mode 100644
index 0000000..46a46fc
--- /dev/null
+++ b/t/t5100/info0005
@@ -0,0 +1,5 @@
+Author: David KÃ¥gedal
+Email: davidk@lysator.liu.se
+Subject: Fixed two bugs in git-cvsimport-script.
+Date: Mon, 15 Aug 2005 20:18:25 +0200
+
diff --git a/t/t5100/info0006 b/t/t5100/info0006
new file mode 100644
index 0000000..8c05277
--- /dev/null
+++ b/t/t5100/info0006
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: a commit.
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0007 b/t/t5100/info0007
new file mode 100644
index 0000000..49bb0fe
--- /dev/null
+++ b/t/t5100/info0007
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: another patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0008 b/t/t5100/info0008
new file mode 100644
index 0000000..e8a2951
--- /dev/null
+++ b/t/t5100/info0008
@@ -0,0 +1,5 @@
+Author: Junio C Hamano
+Email: junio@kernel.org
+Subject: another patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0009 b/t/t5100/info0009
new file mode 100644
index 0000000..2a66321
--- /dev/null
+++ b/t/t5100/info0009
@@ -0,0 +1,5 @@
+Author: F U Bar
+Email: f.u.bar@example.com
+Subject: updates
+Date: Mon, 17 Sep 2001 00:00:00 +0900
+
diff --git a/t/t5100/info0010 b/t/t5100/info0010
new file mode 100644
index 0000000..1791241
--- /dev/null
+++ b/t/t5100/info0010
@@ -0,0 +1,5 @@
+Author: Lukas Sandström
+Email: lukass@etek.chalmers.se
+Subject: git-mailinfo: Fix getting the subject from the body
+Date: Thu, 10 Jul 2008 23:41:33 +0200
+
diff --git a/t/t5100/info0011 b/t/t5100/info0011
new file mode 100644
index 0000000..da5a605
--- /dev/null
+++ b/t/t5100/info0011
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: Xyzzy
+Date: Fri, 8 Aug 2008 13:08:37 +0200 (CEST)
+
diff --git a/t/t5100/info0012 b/t/t5100/info0012
new file mode 100644
index 0000000..ac1216f
--- /dev/null
+++ b/t/t5100/info0012
@@ -0,0 +1,5 @@
+Author: Dmitriy Blinov
+Email: bda@mnsspb.ru
+Subject: Изменён ÑпиÑок пакетов необходимых Ð´Ð»Ñ Ñборки
+Date: Wed, 12 Nov 2008 17:54:41 +0300
+
diff --git a/t/t5100/info0013 b/t/t5100/info0013
new file mode 100644
index 0000000..bbe049e
--- /dev/null
+++ b/t/t5100/info0013
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: a patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0014 b/t/t5100/info0014
new file mode 100644
index 0000000..08566b3
--- /dev/null
+++ b/t/t5100/info0014
@@ -0,0 +1,5 @@
+Author: Junio Hamano
+Email: junkio@cox.net
+Subject: BLAH ONE
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
diff --git a/t/t5100/info0014--scissors b/t/t5100/info0014--scissors
new file mode 100644
index 0000000..ab9c8d0
--- /dev/null
+++ b/t/t5100/info0014--scissors
@@ -0,0 +1,5 @@
+Author: Junio C Hamano
+Email: gitster@pobox.com
+Subject: Teach mailinfo to ignore everything before -- >8 -- mark
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
diff --git a/t/t5100/info0015 b/t/t5100/info0015
new file mode 100644
index 0000000..0114f10
--- /dev/null
+++ b/t/t5100/info0015
@@ -0,0 +1,5 @@
+Author:
+Email:
+Subject: check bogus body header (from)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0015--no-inbody-headers b/t/t5100/info0015--no-inbody-headers
new file mode 100644
index 0000000..c4d8d77
--- /dev/null
+++ b/t/t5100/info0015--no-inbody-headers
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check bogus body header (from)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/info0016 b/t/t5100/info0016
new file mode 100644
index 0000000..38ccd0d
--- /dev/null
+++ b/t/t5100/info0016
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check bogus body header (date)
+Date: bogus
+
diff --git a/t/t5100/info0016--no-inbody-headers b/t/t5100/info0016--no-inbody-headers
new file mode 100644
index 0000000..f4857d4
--- /dev/null
+++ b/t/t5100/info0016--no-inbody-headers
@@ -0,0 +1,5 @@
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: check bogus body header (date)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
diff --git a/t/t5100/msg0001 b/t/t5100/msg0001
new file mode 100644
index 0000000..b275a9a
--- /dev/null
+++ b/t/t5100/msg0001
@@ -0,0 +1,2 @@
+Here is a patch from A U Thor.
+
diff --git a/t/t5100/msg0002 b/t/t5100/msg0002
new file mode 100644
index 0000000..e2546ec
--- /dev/null
+++ b/t/t5100/msg0002
@@ -0,0 +1,21 @@
+Here is a patch from A U Thor. This addresses the issue raised in the
+message:
+
+From: Nit Picker <nit.picker@example.net>
+Subject: foo is too old
+Message-Id: <nitpicker.12121212@example.net>
+
+Hopefully this would fix the problem stated there.
+
+
+I have included an extra blank line above, but it does not have to be
+stripped away here, along with the
+whitespaces at the end of the above line. They are expected to be squashed
+when the message is made into a commit log by stripspace,
+Also, there are three blank lines after this paragraph,
+two truly blank and another full of spaces in between.
+
+
+
+Hope this helps.
+
diff --git a/t/t5100/msg0003 b/t/t5100/msg0003
new file mode 100644
index 0000000..1ac6810
--- /dev/null
+++ b/t/t5100/msg0003
@@ -0,0 +1,9 @@
+Here is a patch from A U Thor. This addresses the issue raised in the
+message:
+
+From: Nit Picker <nit.picker@example.net>
+Subject: foo is too old
+Message-Id: <nitpicker.12121212@example.net>
+
+Hopefully this would fix the problem stated there.
+
diff --git a/t/t5100/msg0004 b/t/t5100/msg0004
new file mode 100644
index 0000000..6f8ba3b
--- /dev/null
+++ b/t/t5100/msg0004
@@ -0,0 +1,7 @@
+Hello.
+
+Try all addresses for given remote name until it succeeds.
+Also supports IPv6.
+
+Signed-of-by: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
+
diff --git a/t/t5100/msg0005 b/t/t5100/msg0005
new file mode 100644
index 0000000..dd94cd7
--- /dev/null
+++ b/t/t5100/msg0005
@@ -0,0 +1,13 @@
+The git-cvsimport-script had a copule of small bugs that prevented me
+from importing a big CVS repository.
+
+The first was that it didn't handle removed files with a multi-digit
+primary revision number.
+
+The second was that it was asking the CVS server for "F" messages,
+although they were not handled.
+
+I also updated the documentation for that script to correspond to
+actual flags.
+
+Signed-off-by: David KÃ¥gedal <davidk@lysator.liu.se>
diff --git a/t/t5100/msg0006 b/t/t5100/msg0006
new file mode 100644
index 0000000..b275a9a
--- /dev/null
+++ b/t/t5100/msg0006
@@ -0,0 +1,2 @@
+Here is a patch from A U Thor.
+
diff --git a/t/t5100/msg0007 b/t/t5100/msg0007
new file mode 100644
index 0000000..71b23c0
--- /dev/null
+++ b/t/t5100/msg0007
@@ -0,0 +1,2 @@
+Here is an empty patch from A U Thor.
+
diff --git a/t/t5100/msg0008 b/t/t5100/msg0008
new file mode 100644
index 0000000..a80ecb9
--- /dev/null
+++ b/t/t5100/msg0008
@@ -0,0 +1,4 @@
+>Here is an empty patch from A U Thor.
+
+Hey you forgot the patch!
+
diff --git a/t/t5100/msg0009 b/t/t5100/msg0009
new file mode 100644
index 0000000..9ffe131
--- /dev/null
+++ b/t/t5100/msg0009
@@ -0,0 +1,2 @@
+This is to fix diff-format documentation.
+
diff --git a/t/t5100/msg0010 b/t/t5100/msg0010
new file mode 100644
index 0000000..a96c230
--- /dev/null
+++ b/t/t5100/msg0010
@@ -0,0 +1,5 @@
+"Subject: " isn't in the static array "header", and thus
+memcmp("Subject: ", header[i], 7) will never match.
+
+Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t5100/msg0011 b/t/t5100/msg0011
new file mode 100644
index 0000000..4667f21
--- /dev/null
+++ b/t/t5100/msg0011
@@ -0,0 +1,2 @@
+Here comes a commit log message, and
+its second line is here.
diff --git a/t/t5100/msg0012 b/t/t5100/msg0012
new file mode 100644
index 0000000..1dc2bf7
--- /dev/null
+++ b/t/t5100/msg0012
@@ -0,0 +1,7 @@
+textlive-* иÑправлены на texlive-*
+docutils заменён на python-docutils
+
+ДейÑтвительно, оказалоÑÑŒ, что rest2web вытÑгивает за Ñобой
+python-docutils. Ð’ то Ð²Ñ€ÐµÐ¼Ñ ÐºÐ°Ðº Ñам rest2web не нужен.
+
+Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
diff --git a/t/t5100/msg0013 b/t/t5100/msg0013
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/t/t5100/msg0013
diff --git a/t/t5100/msg0014 b/t/t5100/msg0014
new file mode 100644
index 0000000..62e5cd2
--- /dev/null
+++ b/t/t5100/msg0014
@@ -0,0 +1,18 @@
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t5100/msg0014--scissors b/t/t5100/msg0014--scissors
new file mode 100644
index 0000000..259c6a4
--- /dev/null
+++ b/t/t5100/msg0014--scissors
@@ -0,0 +1,4 @@
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t5100/msg0015 b/t/t5100/msg0015
new file mode 100644
index 0000000..4abb3d5
--- /dev/null
+++ b/t/t5100/msg0015
@@ -0,0 +1,2 @@
+ - a list
+ - of stuff
diff --git a/t/t5100/msg0015--no-inbody-headers b/t/t5100/msg0015--no-inbody-headers
new file mode 100644
index 0000000..be5115b
--- /dev/null
+++ b/t/t5100/msg0015--no-inbody-headers
@@ -0,0 +1,3 @@
+From: bogosity
+ - a list
+ - of stuff
diff --git a/t/t5100/msg0016 b/t/t5100/msg0016
new file mode 100644
index 0000000..0d9adad
--- /dev/null
+++ b/t/t5100/msg0016
@@ -0,0 +1,2 @@
+and some content
+
diff --git a/t/t5100/msg0016--no-inbody-headers b/t/t5100/msg0016--no-inbody-headers
new file mode 100644
index 0000000..1063f51
--- /dev/null
+++ b/t/t5100/msg0016--no-inbody-headers
@@ -0,0 +1,4 @@
+Date: bogus
+
+and some content
+
diff --git a/t/t5100/nul-b64.expect b/t/t5100/nul-b64.expect
new file mode 100644
index 0000000..d7d680f
--- /dev/null
+++ b/t/t5100/nul-b64.expect
Binary files differ
diff --git a/t/t5100/nul-b64.in b/t/t5100/nul-b64.in
new file mode 100644
index 0000000..16540d9
--- /dev/null
+++ b/t/t5100/nul-b64.in
@@ -0,0 +1,37 @@
+From 667d8940e719cddee1cfe237cbbe215e20270b09 Mon Sep 17 00:00:00 2001
+From: Junio C Hamano <gitster@pobox.com>
+Date: Sun, 25 May 2008 00:38:18 -0700
+Subject: [PATCH] second
+Content-Transfer-Encoding: base64
+
+LS0tCiBmaWxlIHwgIEJpbiAxMzU3IC0+IDEzNTcgYnl0ZXMKIDEgZmlsZXMgY2hhbmdlZCwg
+MCBpbnNlcnRpb25zKCspLCAwIGRlbGV0aW9ucygtKQoKZGlmZiAtLWdpdCBhL2ZpbGUgYi9m
+aWxlCmluZGV4IDc3MzYxZDguLjllMDJiZTYgMTAwNjQ0Ci0tLSBhL2ZpbGUKKysrIGIvZmls
+ZQpAQCAtMSwxMiArMSwxMiBAQAogTG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNl
+Y3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIFN1c3BlbmRpc3NlCiBzaXQgYW1ldCB0dXJwaXMg
+ZWdldCBlc3QgY3Vyc3VzIGxhb3JlZXQuIEFsaXF1YW0gbWF1cmlzLiBQcmFlc2VudAotdm9s
+dXRwYXQuIFByb2luIGluIHB1cnVzLiBOdWxsYSB1cm5hIHNhcGllbiwgZGFwaWJ1cyBzaXQg
+YW1ldCwKK3ZvbHV0cGF0LiBQcm9pbiBpbiBwdXJ1cy4gTnVsbGEgdXJuYSBzYXBpZW4sIGRh
+cGkAdXMgc2l0IGFtZXQsCiBoZW5kcmVyaXQgbmVjLCB0ZW1wdXMgZXUsIG1pLiBVdCBwb3J0
+YSwgbGVvIGlkIHRpbmNpZHVudCB1bGxhbWNvcnBlciwKLXZlbGl0IGZlbGlzIHRyaXN0aXF1
+ZSBhbnRlLCBhdCBsb2JvcnRpcyBkaWFtIHBlZGUgdXQgZHVpLiBQcm9pbiBhYwordmVsaXQg
+ZmVsaXMgdHJpc3RpcXVlIGFudGUsIGF0IGxvAG9ydGlzIGRpYW0gcGVkZSB1dCBkdWkuIFBy
+b2luIGFjCiBsZWN0dXMuIERvbmVjIGF0IG1hc3NhIGFjIGlwc3VtIGhlbmRyZXJpdCBzb2xs
+aWNpdHVkaW4uIE5hbSBkaWN0dW0KIG5pc2kgc2VkIG1pLiBEdWlzIHNlZCBhbnRlLiBVdCB2
+aXRhZSBlc3QgdXQgZHVpIHVsdHJpY2llcyBkaWduaXNzaW0uCiAKLUluIHZlbCBvZGlvIGVn
+ZXQgbmlzbCBjb252YWxsaXMgdm9sdXRwYXQuIE1vcmJpIHZpdGFlIG5pYmguIE51bGxhbQor
+SW4gdmVsIG9kaW8gZWdldCBuaXNsIGNvbnZhbGxpcyB2b2x1dHBhdC4gTW9yAGkgdml0YWUg
+bmkAaC4gTnVsbGFtCiBhY2N1bXNhbiwgZG9sb3IgcXVpcyBhbGlxdWFtIHNjZWxlcmlzcXVl
+LCBlbGl0IGVuaW0gY29uZGltZW50dW0KIG1hdXJpcywgbm9uIHRyaXN0aXF1ZSBtYXVyaXMg
+dHVycGlzIGV0IG1hdXJpcy4gVXQgbm9uIG5pc2wuIE5hbSBkaWFtCiBtaSwgc2VtcGVyIHBv
+c3VlcmUsIGVsZWlmZW5kIHV0LCBhdWN0b3IgdmVsLCBlcmF0LiBTZWQgcG9zdWVyZQpAQCAt
+MTYsNyArMTYsNyBAQCBzZWQgZXN0LiBFdGlhbSBkaWFtIGZlbGlzLCBmZXJtZW50dW0gZWdl
+dCwgYWRpcGlzY2luZyBhdCwgcG9zdWVyZSBpbiwKIGR1aS4gRXRpYW0gbHVjdHVzLgogCiBO
+dWxsYSBpZCBhdWd1ZS4gTmFtIGlhY3VsaXMgYWNjdW1zYW4gbmlzaS4gU3VzcGVuZGlzc2Ug
+cG90ZW50aS4gTnVuYwotdmFyaXVzIGF1Z3VlIG5lYyBvcmNpLiBVdCBjb25kaW1lbnR1bSBk
+b2xvciBzYWdpdHRpcyBuaWJoLiBTdXNwZW5kaXNzZQordmFyaXVzIGF1Z3VlIG5lYyBvcmNp
+LiBVdCBjb25kaW1lbnR1bSBkb2xvciBzYWdpdHRpcyBuaQBoLiBTdXNwZW5kaXNzZQogdGVt
+cG9yIGxlY3R1cyBzZWQgbWFnbmEuIFN1c3BlbmRpc3NlIHBvdGVudGkuIE51bGxhbSB0ZW1w
+b3IgaXBzdW0uIFNlZAogbW9sZXN0aWUgdGVsbHVzLiBQaGFzZWxsdXMgbGlndWxhLiBJbiB2
+ZWhpY3VsYSB1bHRyaWNlcwogbmlzaS4gU3VzcGVuZGlzc2UgZmVsaXMgYXVndWUsIHBlbGxl
+bnRlc3F1ZSBhdCwgZGljdHVtIHZpdmVycmEsCi0tIAoxLjUuNS4xLjU0MC5nNTc3ODAKCg==
diff --git a/t/t5100/nul-plain b/t/t5100/nul-plain
new file mode 100644
index 0000000..3d40691
--- /dev/null
+++ b/t/t5100/nul-plain
Binary files differ
diff --git a/t/t5100/patch0001 b/t/t5100/patch0001
new file mode 100644
index 0000000..8ce1551
--- /dev/null
+++ b/t/t5100/patch0001
@@ -0,0 +1,14 @@
+---
+ foo | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/foo b/foo
+index 9123cdc..918dcf8 100644
+--- a/foo
++++ b/foo
+@@ -1 +1 @@
+-Fri Jun 9 00:44:04 PDT 2006
++Fri Jun 9 00:44:13 PDT 2006
+--
+1.4.0.g6f2b
+
diff --git a/t/t5100/patch0002 b/t/t5100/patch0002
new file mode 100644
index 0000000..8ce1551
--- /dev/null
+++ b/t/t5100/patch0002
@@ -0,0 +1,14 @@
+---
+ foo | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/foo b/foo
+index 9123cdc..918dcf8 100644
+--- a/foo
++++ b/foo
+@@ -1 +1 @@
+-Fri Jun 9 00:44:04 PDT 2006
++Fri Jun 9 00:44:13 PDT 2006
+--
+1.4.0.g6f2b
+
diff --git a/t/t5100/patch0003 b/t/t5100/patch0003
new file mode 100644
index 0000000..8ce1551
--- /dev/null
+++ b/t/t5100/patch0003
@@ -0,0 +1,14 @@
+---
+ foo | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/foo b/foo
+index 9123cdc..918dcf8 100644
+--- a/foo
++++ b/foo
+@@ -1 +1 @@
+-Fri Jun 9 00:44:04 PDT 2006
++Fri Jun 9 00:44:13 PDT 2006
+--
+1.4.0.g6f2b
+
diff --git a/t/t5100/patch0004 b/t/t5100/patch0004
new file mode 100644
index 0000000..196458e
--- /dev/null
+++ b/t/t5100/patch0004
@@ -0,0 +1,93 @@
+diff --git a/connect.c b/connect.c
+--- a/connect.c
++++ b/connect.c
+@@ -96,42 +96,57 @@ static enum protocol get_protocol(const
+ die("I don't handle protocol '%s'", name);
+ }
+
+-static void lookup_host(const char *host, struct sockaddr *in)
+-{
+- struct addrinfo *res;
+- int ret;
+-
+- ret = getaddrinfo(host, NULL, NULL, &res);
+- if (ret)
+- die("Unable to look up %s (%s)", host, gai_strerror(ret));
+- *in = *res->ai_addr;
+- freeaddrinfo(res);
+-}
++#define STR_(s) # s
++#define STR(s) STR_(s)
+
+ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
+ {
+- struct sockaddr addr;
+- int port = DEFAULT_GIT_PORT, sockfd;
+- char *colon;
+-
+- colon = strchr(host, ':');
+- if (colon) {
+- char *end;
+- unsigned long n = strtoul(colon+1, &end, 0);
+- if (colon[1] && !*end) {
+- *colon = 0;
+- port = n;
++ int sockfd = -1;
++ char *colon, *end;
++ char *port = STR(DEFAULT_GIT_PORT);
++ struct addrinfo hints, *ai0, *ai;
++ int gai;
++
++ if (host[0] == '[') {
++ end = strchr(host + 1, ']');
++ if (end) {
++ *end = 0;
++ end++;
++ host++;
++ } else
++ end = host;
++ } else
++ end = host;
++ colon = strchr(end, ':');
++
++ if (colon)
++ port = colon + 1;
++
++ memset(&hints, 0, sizeof(hints));
++ hints.ai_socktype = SOCK_STREAM;
++ hints.ai_protocol = IPPROTO_TCP;
++
++ gai = getaddrinfo(host, port, &hints, &ai);
++ if (gai)
++ die("Unable to look up %s (%s)", host, gai_strerror(gai));
++
++ for (ai0 = ai; ai; ai = ai->ai_next) {
++ sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
++ if (sockfd < 0)
++ continue;
++ if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
++ close(sockfd);
++ sockfd = -1;
++ continue;
+ }
++ break;
+ }
+
+- lookup_host(host, &addr);
+- ((struct sockaddr_in *)&addr)->sin_port = htons(port);
++ freeaddrinfo(ai0);
+
+- sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
+ if (sockfd < 0)
+ die("unable to create socket (%s)", strerror(errno));
+- if (connect(sockfd, (void *)&addr, sizeof(addr)) < 0)
+- die("unable to connect (%s)", strerror(errno));
++
+ fd[0] = sockfd;
+ fd[1] = sockfd;
+ packet_write(sockfd, "%s %s\n", prog, path);
+
+--
+YOSHIFUJI Hideaki @ USAGI Project <yoshfuji@linux-ipv6.org>
+GPG-FP : 9022 65EB 1ECF 3AD1 0BDF 80D8 4807 F894 E062 0EEA
+
diff --git a/t/t5100/patch0005 b/t/t5100/patch0005
new file mode 100644
index 0000000..7d24b24
--- /dev/null
+++ b/t/t5100/patch0005
@@ -0,0 +1,69 @@
+---
+
+ Documentation/git-cvsimport-script.txt | 9 ++++++++-
+ git-cvsimport-script | 4 ++--
+ 2 files changed, 10 insertions(+), 3 deletions(-)
+
+50452f9c0c2df1f04d83a26266ba704b13861632
+diff --git a/Documentation/git-cvsimport-script.txt b/Documentation/git-cvsimport-script.txt
+--- a/Documentation/git-cvsimport-script.txt
++++ b/Documentation/git-cvsimport-script.txt
+@@ -29,6 +29,10 @@ OPTIONS
+ currently, only the :local:, :ext: and :pserver: access methods
+ are supported.
+
++-C <target-dir>::
++ The GIT repository to import to. If the directory doesn't
++ exist, it will be created. Default is the current directory.
++
+ -i::
+ Import-only: don't perform a checkout after importing. This option
+ ensures the working directory and cache remain untouched and will
+@@ -44,7 +48,7 @@ OPTIONS
+
+ -p <options-for-cvsps>::
+ Additional options for cvsps.
+- The options '-x' and '-A' are implicit and should not be used here.
++ The options '-u' and '-A' are implicit and should not be used here.
+
+ If you need to pass multiple options, separate them with a comma.
+
+@@ -57,6 +61,9 @@ OPTIONS
+ -h::
+ Print a short usage message and exit.
+
++-z <fuzz>::
++ Pass the timestamp fuzz factor to cvsps.
++
+ OUTPUT
+ ------
+ If '-v' is specified, the script reports what it is doing.
+diff --git a/git-cvsimport-script b/git-cvsimport-script
+--- a/git-cvsimport-script
++++ b/git-cvsimport-script
+@@ -190,7 +190,7 @@ sub conn {
+ $self->{'socketo'}->write("Root $repo\n");
+
+ # Trial and error says that this probably is the minimum set
+- $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E F Checked-in Created Updated Merged Removed\n");
++ $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mode M Mbinary E Checked-in Created Updated Merged Removed\n");
+
+ $self->{'socketo'}->write("valid-requests\n");
+ $self->{'socketo'}->flush();
+@@ -691,7 +691,7 @@ while(<CVS>) {
+ unlink($tmpname);
+ my $mode = pmode($cvs->{'mode'});
+ push(@new,[$mode, $sha, $fn]); # may be resurrected!
+- } elsif($state == 9 and /^\s+(\S+):\d(?:\.\d+)+->(\d(?:\.\d+)+)\(DEAD\)\s*$/) {
++ } elsif($state == 9 and /^\s+(\S+):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
+ my $fn = $1;
+ $fn =~ s#^/+##;
+ push(@old,$fn);
+
+--
+David Kågedal
+-
+To unsubscribe from this list: send the line "unsubscribe git" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+
diff --git a/t/t5100/patch0006 b/t/t5100/patch0006
new file mode 100644
index 0000000..8ce1551
--- /dev/null
+++ b/t/t5100/patch0006
@@ -0,0 +1,14 @@
+---
+ foo | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/foo b/foo
+index 9123cdc..918dcf8 100644
+--- a/foo
++++ b/foo
+@@ -1 +1 @@
+-Fri Jun 9 00:44:04 PDT 2006
++Fri Jun 9 00:44:13 PDT 2006
+--
+1.4.0.g6f2b
+
diff --git a/t/t5100/patch0007 b/t/t5100/patch0007
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/t/t5100/patch0007
diff --git a/t/t5100/patch0008 b/t/t5100/patch0008
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/t/t5100/patch0008
diff --git a/t/t5100/patch0009 b/t/t5100/patch0009
new file mode 100644
index 0000000..65615c3
--- /dev/null
+++ b/t/t5100/patch0009
@@ -0,0 +1,13 @@
+diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
+index b426a14..97756ec 100644
+--- a/Documentation/diff-format.txt
++++ b/Documentation/diff-format.txt
+@@ -81,7 +81,7 @@ The "diff" formatting options can be customized via the
+ environment variable 'GIT_DIFF_OPTS'. For example, if you
+ prefer context diff:
+
+- GIT_DIFF_OPTS=-c git-diff-index -p $(cat .git/HEAD)
++ GIT_DIFF_OPTS=-c git-diff-index -p HEAD
+
+
+ 2. When the environment variable 'GIT_EXTERNAL_DIFF' is set, the
diff --git a/t/t5100/patch0010 b/t/t5100/patch0010
new file mode 100644
index 0000000..f055481
--- /dev/null
+++ b/t/t5100/patch0010
@@ -0,0 +1,20 @@
+---
+ builtin-mailinfo.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index 962aa34..2d1520f 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -334,7 +334,7 @@ static int check_header(char *line, unsigned linesize, char **hdr_data, int over
+ return 1;
+ if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
+ for (i = 0; header[i]; i++) {
+- if (!memcmp("Subject: ", header[i], 9)) {
++ if (!memcmp("Subject", header[i], 7)) {
+ if (! handle_header(line, hdr_data[i], 0)) {
+ return 1;
+ }
+--
+1.5.6.2.455.g1efb2
+
diff --git a/t/t5100/patch0011 b/t/t5100/patch0011
new file mode 100644
index 0000000..8841d3c
--- /dev/null
+++ b/t/t5100/patch0011
@@ -0,0 +1,22 @@
+---
+ builtin-mailinfo.c | 4 ++--
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index 3e5fe51..aabfe5c 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -758,8 +758,8 @@ static void handle_body(void)
+ /* process any boundary lines */
+ if (*content_top && is_multipart_boundary(&line)) {
+ /* flush any leftover */
+- if (line.len)
+- handle_filter(&line);
++ if (prev.len)
++ handle_filter(&prev);
+
+ if (!handle_boundary())
+ goto handle_body_out;
+--
+1.6.0.rc2
+
+
diff --git a/t/t5100/patch0012 b/t/t5100/patch0012
new file mode 100644
index 0000000..36a0b68
--- /dev/null
+++ b/t/t5100/patch0012
@@ -0,0 +1,30 @@
+---
+ howto/build_navy.txt | 6 +++---
+ 1 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/howto/build_navy.txt b/howto/build_navy.txt
+index 3fd3afb..0ee807e 100644
+--- a/howto/build_navy.txt
++++ b/howto/build_navy.txt
+@@ -119,8 +119,8 @@
+ - libxv-dev
+ - libusplash-dev
+ - latex-make
+- - textlive-lang-cyrillic
+- - textlive-latex-extra
++ - texlive-lang-cyrillic
++ - texlive-latex-extra
+ - dia
+ - python-pyrex
+ - libtool
+@@ -128,7 +128,7 @@
+ - sox
+ - cython
+ - imagemagick
+- - docutils
++ - python-docutils
+
+ #. на машине dinar: добавить Ñвой открытый ssh-ключ в authorized_keys2 Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ddev
+ #. на Ñвоей машине: отредактировать /etc/sudoers (команда ``visudo``) примерно Ñледующим образом::
+--
+1.5.6.5
diff --git a/t/t5100/patch0013 b/t/t5100/patch0013
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/t/t5100/patch0013
diff --git a/t/t5100/patch0014 b/t/t5100/patch0014
new file mode 100644
index 0000000..124efd2
--- /dev/null
+++ b/t/t5100/patch0014
@@ -0,0 +1,64 @@
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ return 0;
+ }
+
++static int scissors(const struct strbuf *line)
++{
++ size_t i, len = line->len;
++ int scissors_dashes_seen = 0;
++ const char *buf = line->buf;
++
++ for (i = 0; i < len; i++) {
++ if (isspace(buf[i]))
++ continue;
++ if (buf[i] == '-') {
++ scissors_dashes_seen |= 02;
++ continue;
++ }
++ if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++ scissors_dashes_seen |= 01;
++ i++;
++ continue;
++ }
++ if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++ i += 7;
++ continue;
++ }
++ /* everything else --- not scissors */
++ break;
++ }
++ return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ strbuf_ltrim(line);
+ if (!line->len)
+ return 0;
+- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++ still_looking = check_header(line, s_hdr_data, 0);
++ if (still_looking)
+ return 0;
+ }
+
++ if (scissors(line)) {
++ fseek(cmitmsg, 0L, SEEK_SET);
++ still_looking = 1;
++ return 0;
++ }
++
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset.buf);
+--
+1.6.4.1
diff --git a/t/t5100/patch0014--scissors b/t/t5100/patch0014--scissors
new file mode 100644
index 0000000..124efd2
--- /dev/null
+++ b/t/t5100/patch0014--scissors
@@ -0,0 +1,64 @@
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ return 0;
+ }
+
++static int scissors(const struct strbuf *line)
++{
++ size_t i, len = line->len;
++ int scissors_dashes_seen = 0;
++ const char *buf = line->buf;
++
++ for (i = 0; i < len; i++) {
++ if (isspace(buf[i]))
++ continue;
++ if (buf[i] == '-') {
++ scissors_dashes_seen |= 02;
++ continue;
++ }
++ if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++ scissors_dashes_seen |= 01;
++ i++;
++ continue;
++ }
++ if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++ i += 7;
++ continue;
++ }
++ /* everything else --- not scissors */
++ break;
++ }
++ return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ strbuf_ltrim(line);
+ if (!line->len)
+ return 0;
+- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++ still_looking = check_header(line, s_hdr_data, 0);
++ if (still_looking)
+ return 0;
+ }
+
++ if (scissors(line)) {
++ fseek(cmitmsg, 0L, SEEK_SET);
++ still_looking = 1;
++ return 0;
++ }
++
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset.buf);
+--
+1.6.4.1
diff --git a/t/t5100/patch0015 b/t/t5100/patch0015
new file mode 100644
index 0000000..ad64848
--- /dev/null
+++ b/t/t5100/patch0015
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/patch0015--no-inbody-headers b/t/t5100/patch0015--no-inbody-headers
new file mode 100644
index 0000000..ad64848
--- /dev/null
+++ b/t/t5100/patch0015--no-inbody-headers
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/patch0016 b/t/t5100/patch0016
new file mode 100644
index 0000000..ad64848
--- /dev/null
+++ b/t/t5100/patch0016
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/patch0016--no-inbody-headers b/t/t5100/patch0016--no-inbody-headers
new file mode 100644
index 0000000..ad64848
--- /dev/null
+++ b/t/t5100/patch0016--no-inbody-headers
@@ -0,0 +1,8 @@
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5100/rfc2047-info-0001 b/t/t5100/rfc2047-info-0001
new file mode 100644
index 0000000..0a383b0
--- /dev/null
+++ b/t/t5100/rfc2047-info-0001
@@ -0,0 +1,4 @@
+Author: Keith Moore
+Email: moore@cs.utk.edu
+Subject: If you can read this you understand the example.
+
diff --git a/t/t5100/rfc2047-info-0002 b/t/t5100/rfc2047-info-0002
new file mode 100644
index 0000000..881be75
--- /dev/null
+++ b/t/t5100/rfc2047-info-0002
@@ -0,0 +1,4 @@
+Author: Olle Järnefors
+Email: ojarnef@admin.kth.se
+Subject: Time for ISO 10646?
+
diff --git a/t/t5100/rfc2047-info-0003 b/t/t5100/rfc2047-info-0003
new file mode 100644
index 0000000..d0f7891
--- /dev/null
+++ b/t/t5100/rfc2047-info-0003
@@ -0,0 +1,4 @@
+Author: Patrik Fältström
+Email: paf@nada.kth.se
+Subject: RFC-HDR care and feeding
+
diff --git a/t/t5100/rfc2047-info-0004 b/t/t5100/rfc2047-info-0004
new file mode 100644
index 0000000..f67a90a
--- /dev/null
+++ b/t/t5100/rfc2047-info-0004
@@ -0,0 +1,4 @@
+Author: Nathaniel Borenstein (×ולש ןב ילטפנ)
+Email: nsb@thumper.bellcore.com
+Subject: Test of new header generator
+
diff --git a/t/t5100/rfc2047-info-0005 b/t/t5100/rfc2047-info-0005
new file mode 100644
index 0000000..c27be3b
--- /dev/null
+++ b/t/t5100/rfc2047-info-0005
@@ -0,0 +1,2 @@
+Subject: (a)
+
diff --git a/t/t5100/rfc2047-info-0006 b/t/t5100/rfc2047-info-0006
new file mode 100644
index 0000000..9dad474
--- /dev/null
+++ b/t/t5100/rfc2047-info-0006
@@ -0,0 +1,2 @@
+Subject: (a b)
+
diff --git a/t/t5100/rfc2047-info-0007 b/t/t5100/rfc2047-info-0007
new file mode 100644
index 0000000..294f195
--- /dev/null
+++ b/t/t5100/rfc2047-info-0007
@@ -0,0 +1,2 @@
+Subject: (ab)
+
diff --git a/t/t5100/rfc2047-info-0008 b/t/t5100/rfc2047-info-0008
new file mode 100644
index 0000000..294f195
--- /dev/null
+++ b/t/t5100/rfc2047-info-0008
@@ -0,0 +1,2 @@
+Subject: (ab)
+
diff --git a/t/t5100/rfc2047-info-0009 b/t/t5100/rfc2047-info-0009
new file mode 100644
index 0000000..294f195
--- /dev/null
+++ b/t/t5100/rfc2047-info-0009
@@ -0,0 +1,2 @@
+Subject: (ab)
+
diff --git a/t/t5100/rfc2047-info-0010 b/t/t5100/rfc2047-info-0010
new file mode 100644
index 0000000..9dad474
--- /dev/null
+++ b/t/t5100/rfc2047-info-0010
@@ -0,0 +1,2 @@
+Subject: (a b)
+
diff --git a/t/t5100/rfc2047-info-0011 b/t/t5100/rfc2047-info-0011
new file mode 100644
index 0000000..9dad474
--- /dev/null
+++ b/t/t5100/rfc2047-info-0011
@@ -0,0 +1,2 @@
+Subject: (a b)
+
diff --git a/t/t5100/rfc2047-samples.mbox b/t/t5100/rfc2047-samples.mbox
new file mode 100644
index 0000000..1fc2248
--- /dev/null
+++ b/t/t5100/rfc2047-samples.mbox
@@ -0,0 +1,48 @@
+From nobody Mon Sep 17 00:00:00 2001
+From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>
+To: =?ISO8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>
+CC: =?ISO8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>
+Subject: =?ISO8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
+ =?ISO8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=
+
+From nobody Mon Sep 17 00:00:00 2001
+From: =?ISO8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>
+To: ietf-822@dimacs.rutgers.edu, ojarnef@admin.kth.se
+Subject: Time for ISO 10646?
+
+From nobody Mon Sep 17 00:00:00 2001
+To: Dave Crocker <dcrocker@mordor.stanford.edu>
+Cc: ietf-822@dimacs.rutgers.edu, paf@comsol.se
+From: =?ISO8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se>
+Subject: Re: RFC-HDR care and feeding
+
+From nobody Mon Sep 17 00:00:00 2001
+From: Nathaniel Borenstein <nsb@thumper.bellcore.com>
+ (=?ISO8859-8?b?7eXs+SDv4SDp7Oj08A==?=)
+To: Greg Vaudreuil <gvaudre@NRI.Reston.VA.US>, Ned Freed
+ <ned@innosoft.com>, Keith Moore <moore@cs.utk.edu>
+Subject: Test of new header generator
+MIME-Version: 1.0
+Content-type: text/plain; charset=ISO8859-1
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO8859-1?Q?a?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO8859-1?Q?a?= b)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO8859-1?Q?a?= =?ISO8859-1?Q?b?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO8859-1?Q?a?= =?ISO8859-1?Q?b?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO8859-1?Q?a?=
+ =?ISO8859-1?Q?b?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO8859-1?Q?a_b?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO8859-1?Q?a?= =?ISO8859-2?Q?_b?=)
diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox
new file mode 100644
index 0000000..de10312
--- /dev/null
+++ b/t/t5100/sample.mbox
@@ -0,0 +1,685 @@
+
+
+
+From nobody Mon Sep 17 00:00:00 2001
+From: A (zzz)
+ U
+ Thor
+ <a.u.thor@example.com> (Comment)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: [PATCH] a commit.
+
+Here is a patch from A U Thor.
+
+---
+ foo | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/foo b/foo
+index 9123cdc..918dcf8 100644
+--- a/foo
++++ b/foo
+@@ -1 +1 @@
+-Fri Jun 9 00:44:04 PDT 2006
++Fri Jun 9 00:44:13 PDT 2006
+--
+1.4.0.g6f2b
+
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: [PATCH] another patch
+
+Here is a patch from A U Thor. This addresses the issue raised in the
+message:
+
+From: Nit Picker <nit.picker@example.net>
+Subject: foo is too old
+Message-Id: <nitpicker.12121212@example.net>
+
+Hopefully this would fix the problem stated there.
+
+
+I have included an extra blank line above, but it does not have to be
+stripped away here, along with the
+whitespaces at the end of the above line. They are expected to be squashed
+when the message is made into a commit log by stripspace,
+Also, there are three blank lines after this paragraph,
+two truly blank and another full of spaces in between.
+
+
+
+Hope this helps.
+
+---
+ foo | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/foo b/foo
+index 9123cdc..918dcf8 100644
+--- a/foo
++++ b/foo
+@@ -1 +1 @@
+-Fri Jun 9 00:44:04 PDT 2006
++Fri Jun 9 00:44:13 PDT 2006
+--
+1.4.0.g6f2b
+
+From nobody Mon Sep 17 00:00:00 2001
+From: Junio C Hamano <junio@kernel.org>
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: re: [PATCH] another patch
+
+From: A U Thor <a.u.thor@example.com>
+Subject: [PATCH] third patch
+
+Here is a patch from A U Thor. This addresses the issue raised in the
+message:
+
+From: Nit Picker <nit.picker@example.net>
+Subject: foo is too old
+Message-Id: <nitpicker.12121212@example.net>
+
+Hopefully this would fix the problem stated there.
+
+---
+ foo | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/foo b/foo
+index 9123cdc..918dcf8 100644
+--- a/foo
++++ b/foo
+@@ -1 +1 @@
+-Fri Jun 9 00:44:04 PDT 2006
++Fri Jun 9 00:44:13 PDT 2006
+--
+1.4.0.g6f2b
+
+From nobody Sat Aug 27 23:07:49 2005
+Path: news.gmane.org!not-for-mail
+Message-ID: <20050721.091036.01119516.yoshfuji@linux-ipv6.org>
+From: YOSHIFUJI Hideaki / =?ISO-2022-JP?B?GyRCNUhGIzFRTEAbKEI=?=
+ <yoshfuji@linux-ipv6.org>
+Newsgroups: gmane.comp.version-control.git
+Subject: [PATCH 1/2] GIT: Try all addresses for given remote name
+Date: Thu, 21 Jul 2005 09:10:36 -0400 (EDT)
+Lines: 99
+Organization: USAGI/WIDE Project
+Approved: news@gmane.org
+NNTP-Posting-Host: main.gmane.org
+Mime-Version: 1.0
+Content-Type: Text/Plain; charset=us-ascii
+Content-Transfer-Encoding: 7bit
+X-Trace: sea.gmane.org 1121951434 29350 80.91.229.2 (21 Jul 2005 13:10:34 GMT)
+X-Complaints-To: usenet@sea.gmane.org
+NNTP-Posting-Date: Thu, 21 Jul 2005 13:10:34 +0000 (UTC)
+
+Hello.
+
+Try all addresses for given remote name until it succeeds.
+Also supports IPv6.
+
+Signed-of-by: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
+
+diff --git a/connect.c b/connect.c
+--- a/connect.c
++++ b/connect.c
+@@ -96,42 +96,57 @@ static enum protocol get_protocol(const
+ die("I don't handle protocol '%s'", name);
+ }
+
+-static void lookup_host(const char *host, struct sockaddr *in)
+-{
+- struct addrinfo *res;
+- int ret;
+-
+- ret = getaddrinfo(host, NULL, NULL, &res);
+- if (ret)
+- die("Unable to look up %s (%s)", host, gai_strerror(ret));
+- *in = *res->ai_addr;
+- freeaddrinfo(res);
+-}
++#define STR_(s) # s
++#define STR(s) STR_(s)
+
+ static int git_tcp_connect(int fd[2], const char *prog, char *host, char *path)
+ {
+- struct sockaddr addr;
+- int port = DEFAULT_GIT_PORT, sockfd;
+- char *colon;
+-
+- colon = strchr(host, ':');
+- if (colon) {
+- char *end;
+- unsigned long n = strtoul(colon+1, &end, 0);
+- if (colon[1] && !*end) {
+- *colon = 0;
+- port = n;
++ int sockfd = -1;
++ char *colon, *end;
++ char *port = STR(DEFAULT_GIT_PORT);
++ struct addrinfo hints, *ai0, *ai;
++ int gai;
++
++ if (host[0] == '[') {
++ end = strchr(host + 1, ']');
++ if (end) {
++ *end = 0;
++ end++;
++ host++;
++ } else
++ end = host;
++ } else
++ end = host;
++ colon = strchr(end, ':');
++
++ if (colon)
++ port = colon + 1;
++
++ memset(&hints, 0, sizeof(hints));
++ hints.ai_socktype = SOCK_STREAM;
++ hints.ai_protocol = IPPROTO_TCP;
++
++ gai = getaddrinfo(host, port, &hints, &ai);
++ if (gai)
++ die("Unable to look up %s (%s)", host, gai_strerror(gai));
++
++ for (ai0 = ai; ai; ai = ai->ai_next) {
++ sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
++ if (sockfd < 0)
++ continue;
++ if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
++ close(sockfd);
++ sockfd = -1;
++ continue;
+ }
++ break;
+ }
+
+- lookup_host(host, &addr);
+- ((struct sockaddr_in *)&addr)->sin_port = htons(port);
++ freeaddrinfo(ai0);
+
+- sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
+ if (sockfd < 0)
+ die("unable to create socket (%s)", strerror(errno));
+- if (connect(sockfd, (void *)&addr, sizeof(addr)) < 0)
+- die("unable to connect (%s)", strerror(errno));
++
+ fd[0] = sockfd;
+ fd[1] = sockfd;
+ packet_write(sockfd, "%s %s\n", prog, path);
+
+--
+YOSHIFUJI Hideaki @ USAGI Project <yoshfuji@linux-ipv6.org>
+GPG-FP : 9022 65EB 1ECF 3AD1 0BDF 80D8 4807 F894 E062 0EEA
+
+From nobody Sat Aug 27 23:07:49 2005
+Path: news.gmane.org!not-for-mail
+Message-ID: <u5tacjjdpxq.fsf@lysator.liu.se>
+From: =?ISO8859-1?Q?David_K=E5gedal?= <davidk@lysator.liu.se>
+Newsgroups: gmane.comp.version-control.git
+Subject: [PATCH] Fixed two bugs in git-cvsimport-script.
+Date: Mon, 15 Aug 2005 20:18:25 +0200
+Lines: 83
+Approved: news@gmane.org
+NNTP-Posting-Host: main.gmane.org
+Mime-Version: 1.0
+Content-Type: text/plain; charset=ISO8859-1
+Content-Transfer-Encoding: QUOTED-PRINTABLE
+X-Trace: sea.gmane.org 1124130247 31839 80.91.229.2 (15 Aug 2005 18:24:07 GMT)
+X-Complaints-To: usenet@sea.gmane.org
+NNTP-Posting-Date: Mon, 15 Aug 2005 18:24:07 +0000 (UTC)
+Cc: "Junio C. Hamano" <junkio@cox.net>
+Original-X-From: git-owner@vger.kernel.org Mon Aug 15 20:24:05 2005
+
+The git-cvsimport-script had a copule of small bugs that prevented me
+from importing a big CVS repository.
+
+The first was that it didn't handle removed files with a multi-digit
+primary revision number.
+
+The second was that it was asking the CVS server for "F" messages,
+although they were not handled.
+
+I also updated the documentation for that script to correspond to
+actual flags.
+
+Signed-off-by: David K=E5gedal <davidk@lysator.liu.se>
+---
+
+ Documentation/git-cvsimport-script.txt | 9 ++++++++-
+ git-cvsimport-script | 4 ++--
+ 2 files changed, 10 insertions(+), 3 deletions(-)
+
+50452f9c0c2df1f04d83a26266ba704b13861632
+diff --git a/Documentation/git-cvsimport-script.txt b/Documentation/git=
+-cvsimport-script.txt
+--- a/Documentation/git-cvsimport-script.txt
++++ b/Documentation/git-cvsimport-script.txt
+@@ -29,6 +29,10 @@ OPTIONS
+ currently, only the :local:, :ext: and :pserver: access methods=20
+ are supported.
+=20
++-C <target-dir>::
++ The GIT repository to import to. If the directory doesn't
++ exist, it will be created. Default is the current directory.
++
+ -i::
+ Import-only: don't perform a checkout after importing. This option
+ ensures the working directory and cache remain untouched and will
+@@ -44,7 +48,7 @@ OPTIONS
+=20
+ -p <options-for-cvsps>::
+ Additional options for cvsps.
+- The options '-x' and '-A' are implicit and should not be used here.
++ The options '-u' and '-A' are implicit and should not be used here.
+=20
+ If you need to pass multiple options, separate them with a comma.
+=20
+@@ -57,6 +61,9 @@ OPTIONS
+ -h::
+ Print a short usage message and exit.
+=20
++-z <fuzz>::
++ Pass the timestamp fuzz factor to cvsps.
++
+ OUTPUT
+ ------
+ If '-v' is specified, the script reports what it is doing.
+diff --git a/git-cvsimport-script b/git-cvsimport-script
+--- a/git-cvsimport-script
++++ b/git-cvsimport-script
+@@ -190,7 +190,7 @@ sub conn {
+ $self->{'socketo'}->write("Root $repo\n");
+=20
+ # Trial and error says that this probably is the minimum set
+- $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mo=
+de M Mbinary E F Checked-in Created Updated Merged Removed\n");
++ $self->{'socketo'}->write("Valid-responses ok error Valid-requests Mo=
+de M Mbinary E Checked-in Created Updated Merged Removed\n");
+=20
+ $self->{'socketo'}->write("valid-requests\n");
+ $self->{'socketo'}->flush();
+@@ -691,7 +691,7 @@ while(<CVS>) {
+ unlink($tmpname);
+ my $mode =3D pmode($cvs->{'mode'});
+ push(@new,[$mode, $sha, $fn]); # may be resurrected!
+- } elsif($state =3D=3D 9 and /^\s+(\S+):\d(?:\.\d+)+->(\d(?:\.\d+)+)\(=
+DEAD\)\s*$/) {
++ } elsif($state =3D=3D 9 and /^\s+(\S+):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)=
+\(DEAD\)\s*$/) {
+ my $fn =3D $1;
+ $fn =3D~ s#^/+##;
+ push(@old,$fn);
+
+--=20
+David K=E5gedal
+-
+To unsubscribe from this list: send the line "unsubscribe git" in
+the body of a message to majordomo@vger.kernel.org
+More majordomo info at http://vger.kernel.org/majordomo-info.html
+
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+References: <Pine.LNX.4.640.0001@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0002@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0003@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0004@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0005@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0006@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0007@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0008@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0009@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0010@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0011@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0012@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0013@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0014@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0015@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0016@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0017@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0018@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0019@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0020@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0021@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0022@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0023@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0024@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0025@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0026@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0027@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0028@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0029@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0030@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0031@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0032@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0033@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0034@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0035@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0036@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0037@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0038@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0039@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0040@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0041@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0042@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0043@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0044@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0045@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0046@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0047@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0048@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0049@woody.linux-foundation.org>
+ <Pine.LNX.4.640.0050@woody.linux-foundation.org>
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: [PATCH] a commit.
+
+Here is a patch from A U Thor.
+
+---
+ foo | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/foo b/foo
+index 9123cdc..918dcf8 100644
+--- a/foo
++++ b/foo
+@@ -1 +1 @@
+-Fri Jun 9 00:44:04 PDT 2006
++Fri Jun 9 00:44:13 PDT 2006
+--
+1.4.0.g6f2b
+
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: [PATCH] another patch
+
+Here is an empty patch from A U Thor.
+
+From nobody Mon Sep 17 00:00:00 2001
+From: Junio C Hamano <junio@kernel.org>
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: re: [PATCH] another patch
+
+From: A U Thor <a.u.thor@example.com>
+Subject: [PATCH] another patch
+>Here is an empty patch from A U Thor.
+
+Hey you forgot the patch!
+
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Date: Mon, 17 Sep 2001 00:00:00 +0900
+Mime-Version: 1.0
+Content-Type: Text/Plain; charset=us-ascii
+Content-Transfer-Encoding: Quoted-Printable
+
+=0A=0AFrom: F U Bar <f.u.bar@example.com>
+Subject: [PATCH] updates=0A=0AThis is to fix diff-format documentation.
+
+diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
+index b426a14..97756ec 100644
+--- a/Documentation/diff-format.txt
++++ b/Documentation/diff-format.txt
+@@ -81,7 +81,7 @@ The "diff" formatting options can be customized via the
+ environment variable 'GIT_DIFF_OPTS'. For example, if you
+ prefer context diff:
+=20
+- GIT_DIFF_OPTS=3D-c git-diff-index -p $(cat .git/HEAD)
++ GIT_DIFF_OPTS=3D-c git-diff-index -p HEAD
+=20
+=20
+ 2. When the environment variable 'GIT_EXTERNAL_DIFF' is set, the
+From b9704a518e21158433baa2cc2d591fea687967f6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Lukas=20Sandstr=C3=B6m?= <lukass@etek.chalmers.se>
+Date: Thu, 10 Jul 2008 23:41:33 +0200
+Subject: Re: discussion that lead to this patch
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+[PATCH] git-mailinfo: Fix getting the subject from the body
+
+"Subject: " isn't in the static array "header", and thus
+memcmp("Subject: ", header[i], 7) will never match.
+
+Signed-off-by: Lukas Sandström <lukass@etek.chalmers.se>
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+---
+ builtin-mailinfo.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index 962aa34..2d1520f 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -334,7 +334,7 @@ static int check_header(char *line, unsigned linesize, char **hdr_data, int over
+ return 1;
+ if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
+ for (i = 0; header[i]; i++) {
+- if (!memcmp("Subject: ", header[i], 9)) {
++ if (!memcmp("Subject", header[i], 7)) {
+ if (! handle_header(line, hdr_data[i], 0)) {
+ return 1;
+ }
+--
+1.5.6.2.455.g1efb2
+
+From nobody Fri Aug 8 22:24:03 2008
+Date: Fri, 8 Aug 2008 13:08:37 +0200 (CEST)
+From: A U Thor <a.u.thor@example.com>
+Subject: [PATCH 3/3 v2] Xyzzy
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="=-=-="
+
+--=-=-=
+Content-Type: text/plain; charset=ISO8859-15
+Content-Transfer-Encoding: quoted-printable
+
+Here comes a commit log message, and
+its second line is here.
+---
+ builtin-mailinfo.c | 4 ++--
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index 3e5fe51..aabfe5c 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -758,8 +758,8 @@ static void handle_body(void)
+ /* process any boundary lines */
+ if (*content_top && is_multipart_boundary(&line)) {
+ /* flush any leftover */
+- if (line.len)
+- handle_filter(&line);
++ if (prev.len)
++ handle_filter(&prev);
+=20
+ if (!handle_boundary())
+ goto handle_body_out;
+--=20
+1.6.0.rc2
+
+--=-=-=--
+
+From bda@mnsspb.ru Wed Nov 12 17:54:41 2008
+From: Dmitriy Blinov <bda@mnsspb.ru>
+To: navy-patches@dinar.mns.mnsspb.ru
+Date: Wed, 12 Nov 2008 17:54:41 +0300
+Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+X-Mailer: git-send-email 1.5.6.5
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=utf-8
+Content-Transfer-Encoding: 8bit
+Subject: [Navy-patches] [PATCH]
+ =?utf-8?b?0JjQt9C80LXQvdGR0L0g0YHQv9C40YHQvtC6INC/0LA=?=
+ =?utf-8?b?0LrQtdGC0L7QsiDQvdC10L7QsdGF0L7QtNC40LzRi9GFINC00LvRjyA=?=
+ =?utf-8?b?0YHQsdC+0YDQutC4?=
+
+textlive-* иÑправлены на texlive-*
+docutils заменён на python-docutils
+
+ДейÑтвительно, оказалоÑÑŒ, что rest2web вытÑгивает за Ñобой
+python-docutils. Ð’ то Ð²Ñ€ÐµÐ¼Ñ ÐºÐ°Ðº Ñам rest2web не нужен.
+
+Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
+---
+ howto/build_navy.txt | 6 +++---
+ 1 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/howto/build_navy.txt b/howto/build_navy.txt
+index 3fd3afb..0ee807e 100644
+--- a/howto/build_navy.txt
++++ b/howto/build_navy.txt
+@@ -119,8 +119,8 @@
+ - libxv-dev
+ - libusplash-dev
+ - latex-make
+- - textlive-lang-cyrillic
+- - textlive-latex-extra
++ - texlive-lang-cyrillic
++ - texlive-latex-extra
+ - dia
+ - python-pyrex
+ - libtool
+@@ -128,7 +128,7 @@
+ - sox
+ - cython
+ - imagemagick
+- - docutils
++ - python-docutils
+
+ #. на машине dinar: добавить Ñвой открытый ssh-ключ в authorized_keys2 Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ddev
+ #. на Ñвоей машине: отредактировать /etc/sudoers (команда ``visudo``) примерно Ñледующим образом::
+--
+1.5.6.5
+From nobody Mon Sep 17 00:00:00 2001
+From: <a.u.thor@example.com> (A U Thor)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: [PATCH] a patch
+
+From nobody Mon Sep 17 00:00:00 2001
+From: Junio Hamano <junkio@cox.net>
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+Subject: Why doesn't git-am does not like >8 scissors mark?
+
+Subject: [PATCH] BLAH ONE
+
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ return 0;
+ }
+
++static int scissors(const struct strbuf *line)
++{
++ size_t i, len = line->len;
++ int scissors_dashes_seen = 0;
++ const char *buf = line->buf;
++
++ for (i = 0; i < len; i++) {
++ if (isspace(buf[i]))
++ continue;
++ if (buf[i] == '-') {
++ scissors_dashes_seen |= 02;
++ continue;
++ }
++ if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++ scissors_dashes_seen |= 01;
++ i++;
++ continue;
++ }
++ if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++ i += 7;
++ continue;
++ }
++ /* everything else --- not scissors */
++ break;
++ }
++ return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ strbuf_ltrim(line);
+ if (!line->len)
+ return 0;
+- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++ still_looking = check_header(line, s_hdr_data, 0);
++ if (still_looking)
+ return 0;
+ }
+
++ if (scissors(line)) {
++ fseek(cmitmsg, 0L, SEEK_SET);
++ still_looking = 1;
++ return 0;
++ }
++
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset.buf);
+--
+1.6.4.1
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Subject: check bogus body header (from)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
+From: bogosity
+ - a list
+ - of stuff
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
+From nobody Mon Sep 17 00:00:00 2001
+From: A U Thor <a.u.thor@example.com>
+Subject: check bogus body header (date)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
+Date: bogus
+
+and some content
+
+---
+diff --git a/foo b/foo
+index e69de29..d95f3ad 100644
+--- a/foo
++++ b/foo
+@@ -0,0 +1 @@
++content
+
diff --git a/t/t5150-request-pull.sh b/t/t5150-request-pull.sh
new file mode 100755
index 0000000..9cc0a42
--- /dev/null
+++ b/t/t5150-request-pull.sh
@@ -0,0 +1,228 @@
+#!/bin/sh
+
+test_description='Test workflows involving pull request.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ git init --bare upstream.git &&
+ git init --bare downstream.git &&
+ git clone upstream.git upstream-private &&
+ git clone downstream.git local &&
+
+ trash_url="file://$TRASH_DIRECTORY" &&
+ downstream_url="$trash_url/downstream.git/" &&
+ upstream_url="$trash_url/upstream.git/" &&
+
+ (
+ cd upstream-private &&
+ cat <<-\EOT >mnemonic.txt &&
+ Thirtey days hath November,
+ Aprile, June, and September:
+ EOT
+ git add mnemonic.txt &&
+ test_tick &&
+ git commit -m "\"Thirty days\", a reminder of month lengths" &&
+ git tag -m "version 1" -a initial &&
+ git push --tags origin master
+ ) &&
+ (
+ cd local &&
+ git remote add upstream "$trash_url/upstream.git" &&
+ git fetch upstream &&
+ git pull upstream master &&
+ cat <<-\EOT >>mnemonic.txt &&
+ Of twyecescore-eightt is but eine,
+ And all the remnante be thrycescore-eine.
+ O’course Leap yare comes an’pynes,
+ Ev’rie foure yares, gote it ryghth.
+ An’twyecescore-eight is but twyecescore-nyne.
+ EOT
+ git add mnemonic.txt &&
+ test_tick &&
+ git commit -m "More detail" &&
+ git tag -m "version 2" -a full &&
+ git checkout -b simplify HEAD^ &&
+ mv mnemonic.txt mnemonic.standard &&
+ cat <<-\EOT >mnemonic.clarified &&
+ Thirty days has September,
+ All the rest I can’t remember.
+ EOT
+ git add -N mnemonic.standard mnemonic.clarified &&
+ git commit -a -m "Adapt to use modern, simpler English
+
+But keep the old version, too, in case some people prefer it." &&
+ git checkout master
+ )
+
+'
+
+test_expect_success 'setup: two scripts for reading pull requests' '
+
+ downstream_url_for_sed=$(
+ printf "%s\n" "$downstream_url" |
+ sed -e '\''s/\\/\\\\/g'\'' -e '\''s/[[/.*^$]/\\&/g'\''
+ ) &&
+
+ cat <<-\EOT >read-request.sed &&
+ #!/bin/sed -nf
+ / in the git repository at:$/!d
+ n
+ /^$/ n
+ s/^[ ]*\(.*\) \([^ ]*\)/please pull\
+ \1\
+ \2/p
+ q
+ EOT
+
+ cat <<-EOT >fuzz.sed
+ #!/bin/sed -nf
+ s/$_x40/OBJECT_NAME/g
+ s/A U Thor/AUTHOR/g
+ s/[-0-9]\{10\} [:0-9]\{8\} [-+][0-9]\{4\}/DATE/g
+ s/ [^ ].*/ SUBJECT/g
+ s/ [^ ].* (DATE)/ SUBJECT (DATE)/g
+ s/$downstream_url_for_sed/URL/g
+ s/for-upstream/BRANCH/g
+ s/mnemonic.txt/FILENAME/g
+ /^ FILENAME | *[0-9]* [-+]*\$/ b diffstat
+ /^AUTHOR ([0-9]*):\$/ b shortlog
+ p
+ b
+ : diffstat
+ n
+ / [0-9]* files changed/ {
+ a\\
+ DIFFSTAT
+ b
+ }
+ b diffstat
+ : shortlog
+ /^ [a-zA-Z]/ n
+ /^[a-zA-Z]* ([0-9]*):\$/ n
+ /^\$/ N
+ /^\n[a-zA-Z]* ([0-9]*):\$/!{
+ a\\
+ SHORTLOG
+ D
+ }
+ n
+ b shortlog
+ EOT
+
+'
+
+test_expect_success 'pull request when forgot to push' '
+
+ rm -fr downstream.git &&
+ git init --bare downstream.git &&
+ (
+ cd local &&
+ git checkout initial &&
+ git merge --ff-only master &&
+ test_must_fail git request-pull initial "$downstream_url" \
+ 2>../err
+ ) &&
+ grep "No branch of.*is at:\$" err &&
+ grep "Are you sure you pushed" err
+
+'
+
+test_expect_success 'pull request after push' '
+
+ rm -fr downstream.git &&
+ git init --bare downstream.git &&
+ (
+ cd local &&
+ git checkout initial &&
+ git merge --ff-only master &&
+ git push origin master:for-upstream &&
+ git request-pull initial origin >../request
+ ) &&
+ sed -nf read-request.sed <request >digest &&
+ cat digest &&
+ {
+ read task &&
+ read repository &&
+ read branch
+ } <digest &&
+ (
+ cd upstream-private &&
+ git checkout initial &&
+ git pull --ff-only "$repository" "$branch"
+ ) &&
+ test "$branch" = for-upstream &&
+ test_cmp local/mnemonic.txt upstream-private/mnemonic.txt
+
+'
+
+test_expect_success 'request names an appropriate branch' '
+
+ rm -fr downstream.git &&
+ git init --bare downstream.git &&
+ (
+ cd local &&
+ git checkout initial &&
+ git merge --ff-only master &&
+ git push --tags origin master simplify &&
+ git push origin master:for-upstream &&
+ git request-pull initial "$downstream_url" >../request
+ ) &&
+ sed -nf read-request.sed <request >digest &&
+ cat digest &&
+ {
+ read task &&
+ read repository &&
+ read branch
+ } <digest &&
+ {
+ test "$branch" = master ||
+ test "$branch" = for-upstream
+ }
+
+'
+
+test_expect_success 'pull request format' '
+
+ rm -fr downstream.git &&
+ git init --bare downstream.git &&
+ cat <<-\EOT >expect &&
+ The following changes since commit OBJECT_NAME:
+
+ SUBJECT (DATE)
+
+ are available in the git repository at:
+ URL BRANCH
+
+ SHORTLOG
+
+ DIFFSTAT
+ EOT
+ (
+ cd local &&
+ git checkout initial &&
+ git merge --ff-only master &&
+ git push origin master:for-upstream &&
+ git request-pull initial "$downstream_url" >../request
+ ) &&
+ <request sed -nf fuzz.sed >request.fuzzy &&
+ test_cmp expect request.fuzzy
+
+'
+
+test_expect_success 'request-pull ignores OPTIONS_KEEPDASHDASH poison' '
+
+ (
+ cd local &&
+ OPTIONS_KEEPDASHDASH=Yes &&
+ export OPTIONS_KEEPDASHDASH &&
+ git checkout initial &&
+ git merge --ff-only master &&
+ git push origin master:for-upstream &&
+ git request-pull -- initial "$downstream_url" >../request
+ )
+
+'
+
+test_done
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
new file mode 100755
index 0000000..bbb9c12
--- /dev/null
+++ b/t/t5300-pack-object.sh
@@ -0,0 +1,417 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git pack-object
+
+'
+. ./test-lib.sh
+
+TRASH=`pwd`
+
+test_expect_success \
+ 'setup' \
+ 'rm -f .git/index*
+ perl -e "print \"a\" x 4096;" > a &&
+ perl -e "print \"b\" x 4096;" > b &&
+ perl -e "print \"c\" x 4096;" > c &&
+ test-genrandom "seed a" 2097152 > a_big &&
+ test-genrandom "seed b" 2097152 > b_big &&
+ git update-index --add a a_big b b_big c &&
+ cat c >d && echo foo >>d && git update-index --add d &&
+ tree=`git write-tree` &&
+ commit=`git commit-tree $tree </dev/null` && {
+ echo $tree &&
+ echo $commit &&
+ git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ } >obj-list && {
+ git diff-tree --root -p $commit &&
+ while read object
+ do
+ t=`git cat-file -t $object` &&
+ git cat-file $t $object || return 1
+ done <obj-list
+ } >expect'
+
+test_expect_success \
+ 'pack without delta' \
+ 'packname_1=$(git pack-objects --window=0 test-1 <obj-list)'
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+ 'unpack without delta' \
+ "GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ git init &&
+ git unpack-objects -n <test-1-${packname_1}.pack &&
+ git unpack-objects <test-1-${packname_1}.pack"
+
+unset GIT_OBJECT_DIRECTORY
+cd "$TRASH/.git2"
+
+test_expect_success \
+ 'check unpack without delta' \
+ '(cd ../.git && find objects -type f -print) |
+ while read path
+ do
+ cmp $path ../.git/$path || {
+ echo $path differs.
+ return 1
+ }
+ done'
+cd "$TRASH"
+
+test_expect_success \
+ 'pack with REF_DELTA' \
+ 'pwd &&
+ packname_2=$(git pack-objects test-2 <obj-list)'
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+ 'unpack with REF_DELTA' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ git init &&
+ git unpack-objects -n <test-2-${packname_2}.pack &&
+ git unpack-objects <test-2-${packname_2}.pack'
+
+unset GIT_OBJECT_DIRECTORY
+cd "$TRASH/.git2"
+test_expect_success \
+ 'check unpack with REF_DELTA' \
+ '(cd ../.git && find objects -type f -print) |
+ while read path
+ do
+ cmp $path ../.git/$path || {
+ echo $path differs.
+ return 1
+ }
+ done'
+cd "$TRASH"
+
+test_expect_success \
+ 'pack with OFS_DELTA' \
+ 'pwd &&
+ packname_3=$(git pack-objects --delta-base-offset test-3 <obj-list)'
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+ 'unpack with OFS_DELTA' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ git init &&
+ git unpack-objects -n <test-3-${packname_3}.pack &&
+ git unpack-objects <test-3-${packname_3}.pack'
+
+unset GIT_OBJECT_DIRECTORY
+cd "$TRASH/.git2"
+test_expect_success \
+ 'check unpack with OFS_DELTA' \
+ '(cd ../.git && find objects -type f -print) |
+ while read path
+ do
+ cmp $path ../.git/$path || {
+ echo $path differs.
+ return 1
+ }
+ done'
+cd "$TRASH"
+
+test_expect_success 'compare delta flavors' '
+ perl -e '\''
+ defined($_ = -s $_) or die for @ARGV;
+ exit 1 if $ARGV[0] <= $ARGV[1];
+ '\'' test-2-$packname_2.pack test-3-$packname_3.pack
+'
+
+rm -fr .git2
+mkdir .git2
+
+test_expect_success \
+ 'use packed objects' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ git init &&
+ cp test-1-${packname_1}.pack test-1-${packname_1}.idx .git2/objects/pack && {
+ git diff-tree --root -p $commit &&
+ while read object
+ do
+ t=`git cat-file -t $object` &&
+ git cat-file $t $object || return 1
+ done <obj-list
+ } >current &&
+ test_cmp expect current'
+
+test_expect_success \
+ 'use packed deltified (REF_DELTA) objects' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ rm -f .git2/objects/pack/test-* &&
+ cp test-2-${packname_2}.pack test-2-${packname_2}.idx .git2/objects/pack && {
+ git diff-tree --root -p $commit &&
+ while read object
+ do
+ t=`git cat-file -t $object` &&
+ git cat-file $t $object || return 1
+ done <obj-list
+ } >current &&
+ test_cmp expect current'
+
+test_expect_success \
+ 'use packed deltified (OFS_DELTA) objects' \
+ 'GIT_OBJECT_DIRECTORY=.git2/objects &&
+ export GIT_OBJECT_DIRECTORY &&
+ rm -f .git2/objects/pack/test-* &&
+ cp test-3-${packname_3}.pack test-3-${packname_3}.idx .git2/objects/pack && {
+ git diff-tree --root -p $commit &&
+ while read object
+ do
+ t=`git cat-file -t $object` &&
+ git cat-file $t $object || return 1
+ done <obj-list
+ } >current &&
+ test_cmp expect current'
+
+unset GIT_OBJECT_DIRECTORY
+
+test_expect_success 'survive missing objects/pack directory' '
+ (
+ rm -fr missing-pack &&
+ mkdir missing-pack &&
+ cd missing-pack &&
+ git init &&
+ GOP=.git/objects/pack
+ rm -fr $GOP &&
+ git index-pack --stdin --keep=test <../test-3-${packname_3}.pack &&
+ test -f $GOP/pack-${packname_3}.pack &&
+ test_cmp $GOP/pack-${packname_3}.pack ../test-3-${packname_3}.pack &&
+ test -f $GOP/pack-${packname_3}.idx &&
+ test_cmp $GOP/pack-${packname_3}.idx ../test-3-${packname_3}.idx &&
+ test -f $GOP/pack-${packname_3}.keep
+ )
+'
+
+test_expect_success \
+ 'verify pack' \
+ 'git verify-pack test-1-${packname_1}.idx \
+ test-2-${packname_2}.idx \
+ test-3-${packname_3}.idx'
+
+test_expect_success \
+ 'verify pack -v' \
+ 'git verify-pack -v test-1-${packname_1}.idx \
+ test-2-${packname_2}.idx \
+ test-3-${packname_3}.idx'
+
+test_expect_success \
+ 'verify-pack catches mismatched .idx and .pack files' \
+ 'cat test-1-${packname_1}.idx >test-3.idx &&
+ cat test-2-${packname_2}.pack >test-3.pack &&
+ if git verify-pack test-3.idx
+ then false
+ else :;
+ fi'
+
+test_expect_success \
+ 'verify-pack catches a corrupted pack signature' \
+ 'cat test-1-${packname_1}.pack >test-3.pack &&
+ echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=2 &&
+ if git verify-pack test-3.idx
+ then false
+ else :;
+ fi'
+
+test_expect_success \
+ 'verify-pack catches a corrupted pack version' \
+ 'cat test-1-${packname_1}.pack >test-3.pack &&
+ echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=7 &&
+ if git verify-pack test-3.idx
+ then false
+ else :;
+ fi'
+
+test_expect_success \
+ 'verify-pack catches a corrupted type/size of the 1st packed object data' \
+ 'cat test-1-${packname_1}.pack >test-3.pack &&
+ echo | dd of=test-3.pack count=1 bs=1 conv=notrunc seek=12 &&
+ if git verify-pack test-3.idx
+ then false
+ else :;
+ fi'
+
+test_expect_success \
+ 'verify-pack catches a corrupted sum of the index file itself' \
+ 'l=`wc -c <test-3.idx` &&
+ l=`expr $l - 20` &&
+ cat test-1-${packname_1}.pack >test-3.pack &&
+ printf "%20s" "" | dd of=test-3.idx count=20 bs=1 conv=notrunc seek=$l &&
+ if git verify-pack test-3.pack
+ then false
+ else :;
+ fi'
+
+test_expect_success \
+ 'build pack index for an existing pack' \
+ 'cat test-1-${packname_1}.pack >test-3.pack &&
+ git index-pack -o tmp.idx test-3.pack &&
+ cmp tmp.idx test-1-${packname_1}.idx &&
+
+ git index-pack test-3.pack &&
+ cmp test-3.idx test-1-${packname_1}.idx &&
+
+ cat test-2-${packname_2}.pack >test-3.pack &&
+ git index-pack -o tmp.idx test-2-${packname_2}.pack &&
+ cmp tmp.idx test-2-${packname_2}.idx &&
+
+ git index-pack test-3.pack &&
+ cmp test-3.idx test-2-${packname_2}.idx &&
+
+ cat test-3-${packname_3}.pack >test-3.pack &&
+ git index-pack -o tmp.idx test-3-${packname_3}.pack &&
+ cmp tmp.idx test-3-${packname_3}.idx &&
+
+ git index-pack test-3.pack &&
+ cmp test-3.idx test-3-${packname_3}.idx &&
+
+ :'
+
+test_expect_success 'unpacking with --strict' '
+
+ for j in a b c d e f g
+ do
+ for i in 0 1 2 3 4 5 6 7 8 9
+ do
+ o=$(echo $j$i | git hash-object -w --stdin) &&
+ echo "100644 $o 0 $j$i"
+ done
+ done >LIST &&
+ rm -f .git/index &&
+ git update-index --index-info <LIST &&
+ LIST=$(git write-tree) &&
+ rm -f .git/index &&
+ head -n 10 LIST | git update-index --index-info &&
+ LI=$(git write-tree) &&
+ rm -f .git/index &&
+ tail -n 10 LIST | git update-index --index-info &&
+ ST=$(git write-tree) &&
+ PACK5=$( git rev-list --objects "$LIST" "$LI" "$ST" | \
+ git pack-objects test-5 ) &&
+ PACK6=$( (
+ echo "$LIST"
+ echo "$LI"
+ echo "$ST"
+ ) | git pack-objects test-6 ) &&
+ test_create_repo test-5 &&
+ (
+ cd test-5 &&
+ git unpack-objects --strict <../test-5-$PACK5.pack &&
+ git ls-tree -r $LIST &&
+ git ls-tree -r $LI &&
+ git ls-tree -r $ST
+ ) &&
+ test_create_repo test-6 &&
+ (
+ # tree-only into empty repo -- many unreachables
+ cd test-6 &&
+ test_must_fail git unpack-objects --strict <../test-6-$PACK6.pack
+ ) &&
+ (
+ # already populated -- no unreachables
+ cd test-5 &&
+ git unpack-objects --strict <../test-6-$PACK6.pack
+ )
+'
+
+test_expect_success 'index-pack with --strict' '
+
+ for j in a b c d e f g
+ do
+ for i in 0 1 2 3 4 5 6 7 8 9
+ do
+ o=$(echo $j$i | git hash-object -w --stdin) &&
+ echo "100644 $o 0 $j$i"
+ done
+ done >LIST &&
+ rm -f .git/index &&
+ git update-index --index-info <LIST &&
+ LIST=$(git write-tree) &&
+ rm -f .git/index &&
+ head -n 10 LIST | git update-index --index-info &&
+ LI=$(git write-tree) &&
+ rm -f .git/index &&
+ tail -n 10 LIST | git update-index --index-info &&
+ ST=$(git write-tree) &&
+ PACK5=$( git rev-list --objects "$LIST" "$LI" "$ST" | \
+ git pack-objects test-5 ) &&
+ PACK6=$( (
+ echo "$LIST"
+ echo "$LI"
+ echo "$ST"
+ ) | git pack-objects test-6 ) &&
+ test_create_repo test-7 &&
+ (
+ cd test-7 &&
+ git index-pack --strict --stdin <../test-5-$PACK5.pack &&
+ git ls-tree -r $LIST &&
+ git ls-tree -r $LI &&
+ git ls-tree -r $ST
+ ) &&
+ test_create_repo test-8 &&
+ (
+ # tree-only into empty repo -- many unreachables
+ cd test-8 &&
+ test_must_fail git index-pack --strict --stdin <../test-6-$PACK6.pack
+ ) &&
+ (
+ # already populated -- no unreachables
+ cd test-7 &&
+ git index-pack --strict --stdin <../test-6-$PACK6.pack
+ )
+'
+
+test_expect_success 'honor pack.packSizeLimit' '
+ git config pack.packSizeLimit 3m &&
+ packname_10=$(git pack-objects test-10 <obj-list) &&
+ test 2 = $(ls test-10-*.pack | wc -l)
+'
+
+test_expect_success 'verify resulting packs' '
+ git verify-pack test-10-*.pack
+'
+
+test_expect_success 'tolerate packsizelimit smaller than biggest object' '
+ git config pack.packSizeLimit 1 &&
+ packname_11=$(git pack-objects test-11 <obj-list) &&
+ test 5 = $(ls test-11-*.pack | wc -l)
+'
+
+test_expect_success 'verify resulting packs' '
+ git verify-pack test-11-*.pack
+'
+
+#
+# WARNING!
+#
+# The following test is destructive. Please keep the next
+# two tests at the end of this file.
+#
+
+test_expect_success \
+ 'fake a SHA1 hash collision' \
+ 'test -f .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67 &&
+ cp -f .git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \
+ .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67'
+
+test_expect_success \
+ 'make sure index-pack detects the SHA1 collision' \
+ 'test_must_fail git index-pack -o bad.idx test-3.pack 2>msg &&
+ grep "SHA1 COLLISION FOUND" msg'
+
+test_done
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
new file mode 100755
index 0000000..0a24e61
--- /dev/null
+++ b/t/t5301-sliding-window.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='mmap sliding window tests'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'rm -f .git/index*
+ for i in a b c
+ do
+ echo $i >$i &&
+ test-genrandom "$i" 32768 >>$i &&
+ git update-index --add $i || return 1
+ done &&
+ echo d >d && cat c >>d && git update-index --add d &&
+ tree=`git write-tree` &&
+ commit1=`git commit-tree $tree </dev/null` &&
+ git update-ref HEAD $commit1 &&
+ git repack -a -d &&
+ test "`git count-objects`" = "0 objects, 0 kilobytes" &&
+ pack1=`ls .git/objects/pack/*.pack` &&
+ test -f "$pack1"'
+
+test_expect_success \
+ 'verify-pack -v, defaults' \
+ 'git verify-pack -v "$pack1"'
+
+test_expect_success \
+ 'verify-pack -v, packedGitWindowSize == 1 page' \
+ 'git config core.packedGitWindowSize 512 &&
+ git verify-pack -v "$pack1"'
+
+test_expect_success \
+ 'verify-pack -v, packedGit{WindowSize,Limit} == 1 page' \
+ 'git config core.packedGitWindowSize 512 &&
+ git config core.packedGitLimit 512 &&
+ git verify-pack -v "$pack1"'
+
+test_expect_success \
+ 'repack -a -d, packedGit{WindowSize,Limit} == 1 page' \
+ 'git config core.packedGitWindowSize 512 &&
+ git config core.packedGitLimit 512 &&
+ commit2=`git commit-tree $tree -p $commit1 </dev/null` &&
+ git update-ref HEAD $commit2 &&
+ git repack -a -d &&
+ test "`git count-objects`" = "0 objects, 0 kilobytes" &&
+ pack2=`ls .git/objects/pack/*.pack` &&
+ test -f "$pack2"
+ test "$pack1" \!= "$pack2"'
+
+test_expect_success \
+ 'verify-pack -v, defaults' \
+ 'git config --unset core.packedGitWindowSize &&
+ git config --unset core.packedGitLimit &&
+ git verify-pack -v "$pack2"'
+
+test_done
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
new file mode 100755
index 0000000..fb3a270
--- /dev/null
+++ b/t/t5302-pack-index.sh
@@ -0,0 +1,225 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Nicolas Pitre
+#
+
+test_description='pack index with 64-bit offsets and object CRC'
+. ./test-lib.sh
+
+test_expect_success \
+ 'setup' \
+ 'rm -rf .git
+ git init &&
+ git config pack.threads 1 &&
+ i=1 &&
+ while test $i -le 100
+ do
+ iii=`printf '%03i' $i`
+ test-genrandom "bar" 200 > wide_delta_$iii &&
+ test-genrandom "baz $iii" 50 >> wide_delta_$iii &&
+ test-genrandom "foo"$i 100 > deep_delta_$iii &&
+ test-genrandom "foo"`expr $i + 1` 100 >> deep_delta_$iii &&
+ test-genrandom "foo"`expr $i + 2` 100 >> deep_delta_$iii &&
+ echo $iii >file_$iii &&
+ test-genrandom "$iii" 8192 >>file_$iii &&
+ git update-index --add file_$iii deep_delta_$iii wide_delta_$iii &&
+ i=`expr $i + 1` || return 1
+ done &&
+ { echo 101 && test-genrandom 100 8192; } >file_101 &&
+ git update-index --add file_101 &&
+ tree=`git write-tree` &&
+ commit=`git commit-tree $tree </dev/null` && {
+ echo $tree &&
+ git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ } >obj-list &&
+ git update-ref HEAD $commit'
+
+test_expect_success \
+ 'pack-objects with index version 1' \
+ 'pack1=$(git pack-objects --index-version=1 test-1 <obj-list) &&
+ git verify-pack -v "test-1-${pack1}.pack"'
+
+test_expect_success \
+ 'pack-objects with index version 2' \
+ 'pack2=$(git pack-objects --index-version=2 test-2 <obj-list) &&
+ git verify-pack -v "test-2-${pack2}.pack"'
+
+test_expect_success \
+ 'both packs should be identical' \
+ 'cmp "test-1-${pack1}.pack" "test-2-${pack2}.pack"'
+
+test_expect_success \
+ 'index v1 and index v2 should be different' \
+ '! cmp "test-1-${pack1}.idx" "test-2-${pack2}.idx"'
+
+test_expect_success \
+ 'index-pack with index version 1' \
+ 'git index-pack --index-version=1 -o 1.idx "test-1-${pack1}.pack"'
+
+test_expect_success \
+ 'index-pack with index version 2' \
+ 'git index-pack --index-version=2 -o 2.idx "test-1-${pack1}.pack"'
+
+test_expect_success \
+ 'index-pack results should match pack-objects ones' \
+ 'cmp "test-1-${pack1}.idx" "1.idx" &&
+ cmp "test-2-${pack2}.idx" "2.idx"'
+
+test_expect_success \
+ 'index v2: force some 64-bit offsets with pack-objects' \
+ 'pack3=$(git pack-objects --index-version=2,0x40000 test-3 <obj-list)'
+
+if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) ||
+ ! (echo "$msg" | grep "pack too large .* off_t")
+then
+ test_set_prereq OFF64_T
+else
+ say "# skipping tests concerning 64-bit offsets"
+fi
+
+test_expect_success OFF64_T \
+ 'index v2: verify a pack with some 64-bit offsets' \
+ 'git verify-pack -v "test-3-${pack3}.pack"'
+
+test_expect_success OFF64_T \
+ '64-bit offsets: should be different from previous index v2 results' \
+ '! cmp "test-2-${pack2}.idx" "test-3-${pack3}.idx"'
+
+test_expect_success OFF64_T \
+ 'index v2: force some 64-bit offsets with index-pack' \
+ 'git index-pack --index-version=2,0x40000 -o 3.idx "test-1-${pack1}.pack"'
+
+test_expect_success OFF64_T \
+ '64-bit offsets: index-pack result should match pack-objects one' \
+ 'cmp "test-3-${pack3}.idx" "3.idx"'
+
+# returns the object number for given object in given pack index
+index_obj_nr()
+{
+ idx_file=$1
+ object_sha1=$2
+ nr=0
+ git show-index < $idx_file |
+ while read offs sha1 extra
+ do
+ nr=$(($nr + 1))
+ test "$sha1" = "$object_sha1" || continue
+ echo "$(($nr - 1))"
+ break
+ done
+}
+
+# returns the pack offset for given object as found in given pack index
+index_obj_offset()
+{
+ idx_file=$1
+ object_sha1=$2
+ git show-index < $idx_file | grep $object_sha1 |
+ ( read offs extra && echo "$offs" )
+}
+
+test_expect_success \
+ '[index v1] 1) stream pack to repository' \
+ 'git index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
+ git prune-packed &&
+ git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
+ cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
+ cmp "test-1-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"'
+
+test_expect_success \
+ '[index v1] 2) create a stealth corruption in a delta base reference' \
+ '# This test assumes file_101 is a delta smaller than 16 bytes.
+ # It should be against file_100 but we substitute its base for file_099
+ sha1_101=`git hash-object file_101` &&
+ sha1_099=`git hash-object file_099` &&
+ offs_101=`index_obj_offset 1.idx $sha1_101` &&
+ nr_099=`index_obj_nr 1.idx $sha1_099` &&
+ chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
+ dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
+ if=".git/objects/pack/pack-${pack1}.idx" \
+ skip=$((4 + 256 * 4 + $nr_099 * 24)) \
+ bs=1 count=20 conv=notrunc &&
+ git cat-file blob $sha1_101 > file_101_foo1'
+
+test_expect_success \
+ '[index v1] 3) corrupted delta happily returned wrong data' \
+ 'test -f file_101_foo1 && ! cmp file_101 file_101_foo1'
+
+test_expect_success \
+ '[index v1] 4) confirm that the pack is actually corrupted' \
+ 'test_must_fail git fsck --full $commit'
+
+test_expect_success \
+ '[index v1] 5) pack-objects happily reuses corrupted data' \
+ 'pack4=$(git pack-objects test-4 <obj-list) &&
+ test -f "test-4-${pack1}.pack"'
+
+test_expect_success \
+ '[index v1] 6) newly created pack is BAD !' \
+ 'test_must_fail git verify-pack -v "test-4-${pack1}.pack"'
+
+test_expect_success \
+ '[index v2] 1) stream pack to repository' \
+ 'rm -f .git/objects/pack/* &&
+ git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
+ git prune-packed &&
+ git count-objects | ( read nr rest && test "$nr" -eq 1 ) &&
+ cmp "test-1-${pack1}.pack" ".git/objects/pack/pack-${pack1}.pack" &&
+ cmp "test-2-${pack1}.idx" ".git/objects/pack/pack-${pack1}.idx"'
+
+test_expect_success \
+ '[index v2] 2) create a stealth corruption in a delta base reference' \
+ '# This test assumes file_101 is a delta smaller than 16 bytes.
+ # It should be against file_100 but we substitute its base for file_099
+ sha1_101=`git hash-object file_101` &&
+ sha1_099=`git hash-object file_099` &&
+ offs_101=`index_obj_offset 1.idx $sha1_101` &&
+ nr_099=`index_obj_nr 1.idx $sha1_099` &&
+ chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
+ dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
+ if=".git/objects/pack/pack-${pack1}.idx" \
+ skip=$((8 + 256 * 4 + $nr_099 * 20)) \
+ bs=1 count=20 conv=notrunc &&
+ git cat-file blob $sha1_101 > file_101_foo2'
+
+test_expect_success \
+ '[index v2] 3) corrupted delta happily returned wrong data' \
+ 'test -f file_101_foo2 && ! cmp file_101 file_101_foo2'
+
+test_expect_success \
+ '[index v2] 4) confirm that the pack is actually corrupted' \
+ 'test_must_fail git fsck --full $commit'
+
+test_expect_success \
+ '[index v2] 5) pack-objects refuses to reuse corrupted data' \
+ 'test_must_fail git pack-objects test-5 <obj-list &&
+ test_must_fail git pack-objects --no-reuse-object test-6 <obj-list'
+
+test_expect_success \
+ '[index v2] 6) verify-pack detects CRC mismatch' \
+ 'rm -f .git/objects/pack/* &&
+ git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
+ git verify-pack ".git/objects/pack/pack-${pack1}.pack" &&
+ obj=`git hash-object file_001` &&
+ nr=`index_obj_nr ".git/objects/pack/pack-${pack1}.idx" $obj` &&
+ chmod +w ".git/objects/pack/pack-${pack1}.idx" &&
+ printf xxxx | dd of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
+ bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + $nr * 4)) &&
+ ( while read obj
+ do git cat-file -p $obj >/dev/null || exit 1
+ done <obj-list ) &&
+ err=$(test_must_fail git verify-pack \
+ ".git/objects/pack/pack-${pack1}.pack" 2>&1) &&
+ echo "$err" | grep "CRC mismatch"'
+
+test_expect_success 'running index-pack in the object store' '
+ rm -f .git/objects/pack/* &&
+ cp test-1-${pack1}.pack .git/objects/pack/pack-${pack1}.pack &&
+ (
+ cd .git/objects/pack
+ git index-pack pack-${pack1}.pack
+ ) &&
+ test -f .git/objects/pack/pack-${pack1}.idx
+'
+
+test_done
diff --git a/t/t5303-pack-corruption-resilience.sh b/t/t5303-pack-corruption-resilience.sh
new file mode 100755
index 0000000..5f6cd4f
--- /dev/null
+++ b/t/t5303-pack-corruption-resilience.sh
@@ -0,0 +1,287 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nicolas Pitre
+#
+
+test_description='resilience to pack corruptions with redundant objects'
+. ./test-lib.sh
+
+# Note: the test objects are created with knowledge of their pack encoding
+# to ensure good code path coverage, and to facilitate direct alteration
+# later on. The assumed characteristics are:
+#
+# 1) blob_2 is a delta with blob_1 for base and blob_3 is a delta with blob2
+# for base, such that blob_3 delta depth is 2;
+#
+# 2) the bulk of object data is uncompressible so the text part remains
+# visible;
+#
+# 3) object header is always 2 bytes.
+
+create_test_files() {
+ test-genrandom "foo" 2000 > file_1 &&
+ test-genrandom "foo" 1800 > file_2 &&
+ test-genrandom "foo" 1800 > file_3 &&
+ echo " base " >> file_1 &&
+ echo " delta1 " >> file_2 &&
+ echo " delta delta2 " >> file_3 &&
+ test-genrandom "bar" 150 >> file_2 &&
+ test-genrandom "baz" 100 >> file_3
+}
+
+create_new_pack() {
+ rm -rf .git &&
+ git init &&
+ blob_1=`git hash-object -t blob -w file_1` &&
+ blob_2=`git hash-object -t blob -w file_2` &&
+ blob_3=`git hash-object -t blob -w file_3` &&
+ pack=`printf "$blob_1\n$blob_2\n$blob_3\n" |
+ git pack-objects $@ .git/objects/pack/pack` &&
+ pack=".git/objects/pack/pack-${pack}" &&
+ git verify-pack -v ${pack}.pack
+}
+
+do_repack() {
+ pack=`printf "$blob_1\n$blob_2\n$blob_3\n" |
+ git pack-objects $@ .git/objects/pack/pack` &&
+ pack=".git/objects/pack/pack-${pack}"
+}
+
+do_corrupt_object() {
+ ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` &&
+ ofs=$(($ofs + $2)) &&
+ chmod +w ${pack}.pack &&
+ dd of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
+ test_must_fail git verify-pack ${pack}.pack
+}
+
+printf '\0' > zero
+
+test_expect_success \
+ 'initial setup validation' \
+ 'create_test_files &&
+ create_new_pack &&
+ git prune-packed &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ 'create corruption in header of first object' \
+ 'do_corrupt_object $blob_1 0 < zero &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... but having a loose copy allows for full recovery' \
+ 'mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_1 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... and loose copy of first delta allows for partial recovery' \
+ 'git prune-packed &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ 'create corruption in data of first object' \
+ 'create_new_pack &&
+ git prune-packed &&
+ chmod +w ${pack}.pack &&
+ perl -i.bak -pe "s/ base /abcdef/" ${pack}.pack &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... but having a loose copy allows for full recovery' \
+ 'mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_1 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... and loose copy of second object allows for partial recovery' \
+ 'git prune-packed &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ 'create corruption in header of first delta' \
+ 'create_new_pack &&
+ git prune-packed &&
+ do_corrupt_object $blob_2 0 < zero &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... but having a loose copy allows for full recovery' \
+ 'mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... and then a repack "clears" the corruption' \
+ 'do_repack &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ 'create corruption in data of first delta' \
+ 'create_new_pack &&
+ git prune-packed &&
+ chmod +w ${pack}.pack &&
+ perl -i.bak -pe "s/ delta1 /abcdefgh/" ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... but having a loose copy allows for full recovery' \
+ 'mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... and then a repack "clears" the corruption' \
+ 'do_repack &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ 'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \
+ 'create_new_pack &&
+ git prune-packed &&
+ do_corrupt_object $blob_2 2 < zero &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... but having a loose copy allows for full recovery' \
+ 'mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... and then a repack "clears" the corruption' \
+ 'do_repack &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ 'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \
+ 'create_new_pack --delta-base-offset &&
+ git prune-packed &&
+ do_corrupt_object $blob_2 2 < zero &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... but having a loose copy allows for full recovery' \
+ 'mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... and then a repack "clears" the corruption' \
+ 'do_repack --delta-base-offset &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ 'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' \
+ 'create_new_pack --delta-base-offset &&
+ git prune-packed &&
+ printf "\001" | do_corrupt_object $blob_2 2 &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... but having a loose copy allows for full recovery' \
+ 'mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_2 &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... and then a repack "clears" the corruption' \
+ 'do_repack --delta-base-offset &&
+ git prune-packed &&
+ git verify-pack ${pack}.pack &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ '... and a redundant pack allows for full recovery too' \
+ 'do_corrupt_object $blob_2 2 < zero &&
+ git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null &&
+ mv ${pack}.idx tmp &&
+ git hash-object -t blob -w file_1 &&
+ git hash-object -t blob -w file_2 &&
+ printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
+ git prune-packed &&
+ mv tmp ${pack}.idx &&
+ git cat-file blob $blob_1 > /dev/null &&
+ git cat-file blob $blob_2 > /dev/null &&
+ git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+ 'corrupting header to have too small output buffer fails unpack' \
+ 'create_new_pack &&
+ git prune-packed &&
+ printf "\262\001" | do_corrupt_object $blob_1 0 &&
+ test_must_fail git cat-file blob $blob_1 > /dev/null &&
+ test_must_fail git cat-file blob $blob_2 > /dev/null &&
+ test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_done
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
new file mode 100755
index 0000000..e2ed13d
--- /dev/null
+++ b/t/t5304-prune.sh
@@ -0,0 +1,197 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Johannes E. Schindelin
+#
+
+test_description='prune'
+. ./test-lib.sh
+
+day=$((60*60*24))
+week=$(($day*7))
+
+add_blob() {
+ before=$(git count-objects | sed "s/ .*//") &&
+ BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+ BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE
+}
+
+test_expect_success setup '
+
+ : > file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git gc
+
+'
+
+test_expect_success 'prune stale packs' '
+
+ orig_pack=$(echo .git/objects/pack/*.pack) &&
+ : > .git/objects/tmp_1.pack &&
+ : > .git/objects/tmp_2.pack &&
+ test-chmtime =-86501 .git/objects/tmp_1.pack &&
+ git prune --expire 1.day &&
+ test -f $orig_pack &&
+ test -f .git/objects/tmp_2.pack &&
+ ! test -f .git/objects/tmp_1.pack
+
+'
+
+test_expect_success 'prune --expire' '
+
+ add_blob &&
+ git prune --expire=1.hour.ago &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE &&
+ test-chmtime =-86500 $BLOB_FILE &&
+ git prune --expire 1.day &&
+ test $before = $(git count-objects | sed "s/ .*//") &&
+ ! test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: implicit prune --expire' '
+
+ add_blob &&
+ test-chmtime =-$((2*$week-30)) $BLOB_FILE &&
+ git gc &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE &&
+ test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
+ git gc &&
+ test $before = $(git count-objects | sed "s/ .*//") &&
+ ! test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: refuse to start with invalid gc.pruneExpire' '
+
+ git config gc.pruneExpire invalid &&
+ test_must_fail git gc
+
+'
+
+test_expect_success 'gc: start with ok gc.pruneExpire' '
+
+ git config gc.pruneExpire 2.days.ago &&
+ git gc
+
+'
+
+test_expect_success 'prune: prune nonsense parameters' '
+
+ test_must_fail git prune garbage &&
+ test_must_fail git prune --- &&
+ test_must_fail git prune --no-such-option
+
+'
+
+test_expect_success 'prune: prune unreachable heads' '
+
+ git config core.logAllRefUpdates false &&
+ mv .git/logs .git/logs.old &&
+ : > file2 &&
+ git add file2 &&
+ git commit -m temporary &&
+ tmp_head=$(git rev-list -1 HEAD) &&
+ git reset HEAD^ &&
+ git prune &&
+ test_must_fail git reset $tmp_head --
+
+'
+
+test_expect_success 'prune: do not prune heads listed as an argument' '
+
+ : > file2 &&
+ git add file2 &&
+ git commit -m temporary &&
+ tmp_head=$(git rev-list -1 HEAD) &&
+ git reset HEAD^ &&
+ git prune -- $tmp_head &&
+ git reset $tmp_head --
+
+'
+
+test_expect_success 'gc --no-prune' '
+
+ add_blob &&
+ test-chmtime =-$((5001*$day)) $BLOB_FILE &&
+ git config gc.pruneExpire 2.days.ago &&
+ git gc --no-prune &&
+ test 1 = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc respects gc.pruneExpire' '
+
+ git config gc.pruneExpire 5002.days.ago &&
+ git gc &&
+ test -f $BLOB_FILE &&
+ git config gc.pruneExpire 5000.days.ago &&
+ git gc &&
+ test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc --prune=<date>' '
+
+ add_blob &&
+ test-chmtime =-$((5001*$day)) $BLOB_FILE &&
+ git gc --prune=5002.days.ago &&
+ test -f $BLOB_FILE &&
+ git gc --prune=5000.days.ago &&
+ test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc --prune=never' '
+
+ add_blob &&
+ git gc --prune=never &&
+ test -f $BLOB_FILE &&
+ git gc --prune=now &&
+ test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc respects gc.pruneExpire=never' '
+
+ git config gc.pruneExpire never &&
+ add_blob &&
+ git gc &&
+ test -f $BLOB_FILE &&
+ git config gc.pruneExpire now &&
+ git gc &&
+ test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'prune --expire=never' '
+
+ add_blob &&
+ git prune --expire=never &&
+ test -f $BLOB_FILE &&
+ git prune &&
+ test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc: prune old objects after local clone' '
+ add_blob &&
+ test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
+ git clone --no-hardlinks . aclone &&
+ (
+ cd aclone &&
+ test 1 = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE &&
+ git gc --prune &&
+ test 0 = $(git count-objects | sed "s/ .*//") &&
+ ! test -f $BLOB_FILE
+ )
+'
+
+test_done
diff --git a/t/t5305-include-tag.sh b/t/t5305-include-tag.sh
new file mode 100755
index 0000000..b061864
--- /dev/null
+++ b/t/t5305-include-tag.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='git pack-object --include-tag'
+. ./test-lib.sh
+
+TRASH=`pwd`
+
+test_expect_success setup '
+ echo c >d &&
+ git update-index --add d &&
+ tree=`git write-tree` &&
+ commit=`git commit-tree $tree </dev/null` &&
+ echo "object $commit" >sig &&
+ echo "type commit" >>sig &&
+ echo "tag mytag" >>sig &&
+ echo "tagger $(git var GIT_COMMITTER_IDENT)" >>sig &&
+ echo >>sig &&
+ echo "our test tag" >>sig &&
+ tag=`git mktag <sig` &&
+ rm d sig &&
+ git update-ref refs/tags/mytag $tag && {
+ echo $tree &&
+ echo $commit &&
+ git ls-tree $tree | sed -e "s/.* \\([0-9a-f]*\\) .*/\\1/"
+ } >obj-list
+'
+
+rm -rf clone.git
+test_expect_success 'pack without --include-tag' '
+ packname_1=$(git pack-objects \
+ --window=0 \
+ test-1 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ git init &&
+ git unpack-objects -n <test-1-${packname_1}.pack &&
+ git unpack-objects <test-1-${packname_1}.pack
+ )
+'
+
+test_expect_success 'check unpacked result (have commit, no tag)' '
+ git rev-list --objects $commit >list.expect &&
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ test_must_fail git cat-file -e $tag &&
+ git rev-list --objects $commit
+ ) >list.actual &&
+ test_cmp list.expect list.actual
+'
+
+rm -rf clone.git
+test_expect_success 'pack with --include-tag' '
+ packname_1=$(git pack-objects \
+ --window=0 \
+ --include-tag \
+ test-2 <obj-list)
+'
+
+test_expect_success 'unpack objects' '
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ git init &&
+ git unpack-objects -n <test-2-${packname_1}.pack &&
+ git unpack-objects <test-2-${packname_1}.pack
+ )
+'
+
+test_expect_success 'check unpacked result (have commit, have tag)' '
+ git rev-list --objects mytag >list.expect &&
+ (
+ GIT_DIR=clone.git &&
+ export GIT_DIR &&
+ git rev-list --objects $tag
+ ) >list.actual &&
+ test_cmp list.expect list.actual
+'
+
+test_done
diff --git a/t/t5306-pack-nobase.sh b/t/t5306-pack-nobase.sh
new file mode 100755
index 0000000..f4931c0
--- /dev/null
+++ b/t/t5306-pack-nobase.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Google Inc.
+#
+
+test_description='git-pack-object with missing base
+
+'
+. ./test-lib.sh
+
+# Create A-B chain
+#
+test_expect_success \
+ 'setup base' \
+ 'for a in a b c d e f g h i; do echo $a >>text; done &&
+ echo side >side &&
+ git update-index --add text side &&
+ A=$(echo A | git commit-tree $(git write-tree)) &&
+
+ echo m >>text &&
+ git update-index text &&
+ B=$(echo B | git commit-tree $(git write-tree) -p $A) &&
+ git update-ref HEAD $B
+ '
+
+# Create repository with C whose parent is B.
+# Repository contains C, C^{tree}, C:text, B, B^{tree}.
+# Repository is missing B:text (best delta base for C:text).
+# Repository is missing A (parent of B).
+# Repository is missing A:side.
+#
+test_expect_success \
+ 'setup patch_clone' \
+ 'base_objects=$(pwd)/.git/objects &&
+ (mkdir patch_clone &&
+ cd patch_clone &&
+ git init &&
+ echo "$base_objects" >.git/objects/info/alternates &&
+ echo q >>text &&
+ git read-tree $B &&
+ git update-index text &&
+ git update-ref HEAD $(echo C | git commit-tree $(git write-tree) -p $B) &&
+ rm .git/objects/info/alternates &&
+
+ git --git-dir=../.git cat-file commit $B |
+ git hash-object -t commit -w --stdin &&
+
+ git --git-dir=../.git cat-file tree "$B^{tree}" |
+ git hash-object -t tree -w --stdin
+ ) &&
+ C=$(git --git-dir=patch_clone/.git rev-parse HEAD)
+ '
+
+# Clone patch_clone indirectly by cloning base and fetching.
+#
+test_expect_success \
+ 'indirectly clone patch_clone' \
+ '(mkdir user_clone &&
+ cd user_clone &&
+ git init &&
+ git pull ../.git &&
+ test $(git rev-parse HEAD) = $B &&
+
+ git pull ../patch_clone/.git &&
+ test $(git rev-parse HEAD) = $C
+ )
+ '
+
+# Cloning the patch_clone directly should fail.
+#
+test_expect_success \
+ 'clone of patch_clone is incomplete' \
+ '(mkdir user_direct &&
+ cd user_direct &&
+ git init &&
+ test_must_fail git fetch ../patch_clone/.git
+ )
+ '
+
+test_done
diff --git a/t/t5307-pack-missing-commit.sh b/t/t5307-pack-missing-commit.sh
new file mode 100755
index 0000000..ae52a18
--- /dev/null
+++ b/t/t5307-pack-missing-commit.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='pack should notice missing commit objects'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in 1 2 3 4 5
+ do
+ echo "$i" >"file$i" &&
+ git add "file$i" &&
+ test_tick &&
+ git commit -m "$i" &&
+ git tag "tag$i"
+ done &&
+ obj=$(git rev-parse --verify tag3) &&
+ fanout=$(expr "$obj" : "\(..\)") &&
+ remainder=$(expr "$obj" : "..\(.*\)") &&
+ rm -f ".git/objects/$fanout/$remainder"
+'
+
+test_expect_success 'check corruption' '
+ test_must_fail git fsck
+'
+
+test_expect_success 'rev-list notices corruption (1)' '
+ test_must_fail git rev-list HEAD
+'
+
+test_expect_success 'rev-list notices corruption (2)' '
+ test_must_fail git rev-list --objects HEAD
+'
+
+test_expect_success 'pack-objects notices corruption' '
+ echo HEAD |
+ test_must_fail git pack-objects --revs pack
+'
+
+test_done
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
new file mode 100755
index 0000000..c718253
--- /dev/null
+++ b/t/t5400-send-pack.sh
@@ -0,0 +1,202 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='See why rewinding head breaks send-pack
+
+'
+. ./test-lib.sh
+
+cnt=64
+test_expect_success setup '
+ test_tick &&
+ mkdir mozart mozart/is &&
+ echo "Commit #0" >mozart/is/pink &&
+ git update-index --add mozart/is/pink &&
+ tree=$(git write-tree) &&
+ commit=$(echo "Commit #0" | git commit-tree $tree) &&
+ zero=$commit &&
+ parent=$zero &&
+ i=0 &&
+ while test $i -le $cnt
+ do
+ i=$(($i+1)) &&
+ test_tick &&
+ echo "Commit #$i" >mozart/is/pink &&
+ git update-index --add mozart/is/pink &&
+ tree=$(git write-tree) &&
+ commit=$(echo "Commit #$i" | git commit-tree $tree -p $parent) &&
+ git update-ref refs/tags/commit$i $commit &&
+ parent=$commit || return 1
+ done &&
+ git update-ref HEAD "$commit" &&
+ git clone ./. victim &&
+ ( cd victim && git config receive.denyCurrentBranch warn && git log ) &&
+ git update-ref HEAD "$zero" &&
+ parent=$zero &&
+ i=0 &&
+ while test $i -le $cnt
+ do
+ i=$(($i+1)) &&
+ test_tick &&
+ echo "Rebase #$i" >mozart/is/pink &&
+ git update-index --add mozart/is/pink &&
+ tree=$(git write-tree) &&
+ commit=$(echo "Rebase #$i" | git commit-tree $tree -p $parent) &&
+ git update-ref refs/tags/rebase$i $commit &&
+ parent=$commit || return 1
+ done &&
+ git update-ref HEAD "$commit" &&
+ echo Rebase &&
+ git log'
+
+test_expect_success 'pack the source repository' '
+ git repack -a -d &&
+ git prune
+'
+
+test_expect_success 'pack the destination repository' '
+ (
+ cd victim &&
+ git repack -a -d &&
+ git prune
+ )
+'
+
+test_expect_success 'refuse pushing rewound head without --force' '
+ pushed_head=$(git rev-parse --verify master) &&
+ victim_orig=$(cd victim && git rev-parse --verify master) &&
+ test_must_fail git send-pack ./victim master &&
+ victim_head=$(cd victim && git rev-parse --verify master) &&
+ test "$victim_head" = "$victim_orig" &&
+ # this should update
+ git send-pack --force ./victim master &&
+ victim_head=$(cd victim && git rev-parse --verify master) &&
+ test "$victim_head" = "$pushed_head"
+'
+
+test_expect_success \
+ 'push can be used to delete a ref' '
+ ( cd victim && git branch extra master ) &&
+ git send-pack ./victim :extra master &&
+ ( cd victim &&
+ test_must_fail git rev-parse --verify extra )
+'
+
+test_expect_success 'refuse deleting push with denyDeletes' '
+ (
+ cd victim &&
+ ( git branch -D extra || : ) &&
+ git config receive.denyDeletes true &&
+ git branch extra master
+ ) &&
+ test_must_fail git send-pack ./victim :extra master
+'
+
+test_expect_success 'denyNonFastforwards trumps --force' '
+ (
+ cd victim &&
+ ( git branch -D extra || : ) &&
+ git config receive.denyNonFastforwards true
+ ) &&
+ victim_orig=$(cd victim && git rev-parse --verify master) &&
+ test_must_fail git send-pack --force ./victim master^:master &&
+ victim_head=$(cd victim && git rev-parse --verify master) &&
+ test "$victim_orig" = "$victim_head"
+'
+
+test_expect_success 'push --all excludes remote tracking hierarchy' '
+ mkdir parent &&
+ (
+ cd parent &&
+ git init && : >file && git add file && git commit -m add
+ ) &&
+ git clone parent child &&
+ (
+ cd child && git push --all
+ ) &&
+ (
+ cd parent &&
+ test -z "$(git for-each-ref refs/remotes/origin)"
+ )
+'
+
+rewound_push_setup() {
+ rm -rf parent child &&
+ mkdir parent &&
+ (
+ cd parent &&
+ git init &&
+ echo one >file && git add file && git commit -m one &&
+ git config receive.denyCurrentBranch warn &&
+ echo two >file && git commit -a -m two
+ ) &&
+ git clone parent child &&
+ (
+ cd child && git reset --hard HEAD^
+ )
+}
+
+rewound_push_succeeded() {
+ cmp ../parent/.git/refs/heads/master .git/refs/heads/master
+}
+
+rewound_push_failed() {
+ if rewound_push_succeeded
+ then
+ false
+ else
+ true
+ fi
+}
+
+test_expect_success 'pushing explicit refspecs respects forcing' '
+ rewound_push_setup &&
+ parent_orig=$(cd parent && git rev-parse --verify master) &&
+ (
+ cd child &&
+ test_must_fail git send-pack ../parent \
+ refs/heads/master:refs/heads/master
+ ) &&
+ parent_head=$(cd parent && git rev-parse --verify master) &&
+ test "$parent_orig" = "$parent_head" &&
+ (
+ cd child &&
+ git send-pack ../parent \
+ +refs/heads/master:refs/heads/master
+ ) &&
+ parent_head=$(cd parent && git rev-parse --verify master) &&
+ child_head=$(cd parent && git rev-parse --verify master) &&
+ test "$parent_head" = "$child_head"
+'
+
+test_expect_success 'pushing wildcard refspecs respects forcing' '
+ rewound_push_setup &&
+ parent_orig=$(cd parent && git rev-parse --verify master) &&
+ (
+ cd child &&
+ test_must_fail git send-pack ../parent \
+ "refs/heads/*:refs/heads/*"
+ ) &&
+ parent_head=$(cd parent && git rev-parse --verify master) &&
+ test "$parent_orig" = "$parent_head" &&
+ (
+ cd child &&
+ git send-pack ../parent \
+ "+refs/heads/*:refs/heads/*"
+ ) &&
+ parent_head=$(cd parent && git rev-parse --verify master) &&
+ child_head=$(cd parent && git rev-parse --verify master) &&
+ test "$parent_head" = "$child_head"
+'
+
+test_expect_success 'deny pushing to delete current branch' '
+ rewound_push_setup &&
+ (
+ cd child &&
+ test_must_fail git send-pack ../parent :refs/heads/master 2>errs
+ )
+'
+
+test_done
diff --git a/t/t5401-update-hooks.sh b/t/t5401-update-hooks.sh
new file mode 100755
index 0000000..17bcb0b
--- /dev/null
+++ b/t/t5401-update-hooks.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn O. Pearce
+#
+
+test_description='Test the update hook infrastructure.'
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo This is a test. >a &&
+ git update-index --add a &&
+ tree0=$(git write-tree) &&
+ commit0=$(echo setup | git commit-tree $tree0) &&
+ echo We hope it works. >a &&
+ git update-index a &&
+ tree1=$(git write-tree) &&
+ commit1=$(echo modify | git commit-tree $tree1 -p $commit0) &&
+ git update-ref refs/heads/master $commit0 &&
+ git update-ref refs/heads/tofail $commit1 &&
+ git clone --bare ./. victim.git &&
+ GIT_DIR=victim.git git update-ref refs/heads/tofail $commit1 &&
+ git update-ref refs/heads/master $commit1 &&
+ git update-ref refs/heads/tofail $commit0
+'
+
+cat >victim.git/hooks/pre-receive <<'EOF'
+#!/bin/sh
+printf %s "$@" >>$GIT_DIR/pre-receive.args
+cat - >$GIT_DIR/pre-receive.stdin
+echo STDOUT pre-receive
+echo STDERR pre-receive >&2
+EOF
+chmod u+x victim.git/hooks/pre-receive
+
+cat >victim.git/hooks/update <<'EOF'
+#!/bin/sh
+echo "$@" >>$GIT_DIR/update.args
+read x; printf %s "$x" >$GIT_DIR/update.stdin
+echo STDOUT update $1
+echo STDERR update $1 >&2
+test "$1" = refs/heads/master || exit
+EOF
+chmod u+x victim.git/hooks/update
+
+cat >victim.git/hooks/post-receive <<'EOF'
+#!/bin/sh
+printf %s "$@" >>$GIT_DIR/post-receive.args
+cat - >$GIT_DIR/post-receive.stdin
+echo STDOUT post-receive
+echo STDERR post-receive >&2
+EOF
+chmod u+x victim.git/hooks/post-receive
+
+cat >victim.git/hooks/post-update <<'EOF'
+#!/bin/sh
+echo "$@" >>$GIT_DIR/post-update.args
+read x; printf %s "$x" >$GIT_DIR/post-update.stdin
+echo STDOUT post-update
+echo STDERR post-update >&2
+EOF
+chmod u+x victim.git/hooks/post-update
+
+test_expect_success push '
+ test_must_fail git send-pack --force ./victim.git \
+ master tofail >send.out 2>send.err
+'
+
+test_expect_success 'updated as expected' '
+ test $(GIT_DIR=victim.git git rev-parse master) = $commit1 &&
+ test $(GIT_DIR=victim.git git rev-parse tofail) = $commit1
+'
+
+test_expect_success 'hooks ran' '
+ test -f victim.git/pre-receive.args &&
+ test -f victim.git/pre-receive.stdin &&
+ test -f victim.git/update.args &&
+ test -f victim.git/update.stdin &&
+ test -f victim.git/post-receive.args &&
+ test -f victim.git/post-receive.stdin &&
+ test -f victim.git/post-update.args &&
+ test -f victim.git/post-update.stdin
+'
+
+test_expect_success 'pre-receive hook input' '
+ (echo $commit0 $commit1 refs/heads/master;
+ echo $commit1 $commit0 refs/heads/tofail
+ ) | test_cmp - victim.git/pre-receive.stdin
+'
+
+test_expect_success 'update hook arguments' '
+ (echo refs/heads/master $commit0 $commit1;
+ echo refs/heads/tofail $commit1 $commit0
+ ) | test_cmp - victim.git/update.args
+'
+
+test_expect_success 'post-receive hook input' '
+ echo $commit0 $commit1 refs/heads/master |
+ test_cmp - victim.git/post-receive.stdin
+'
+
+test_expect_success 'post-update hook arguments' '
+ echo refs/heads/master |
+ test_cmp - victim.git/post-update.args
+'
+
+test_expect_success 'all hook stdin is /dev/null' '
+ ! test -s victim.git/update.stdin &&
+ ! test -s victim.git/post-update.stdin
+'
+
+test_expect_success 'all *-receive hook args are empty' '
+ ! test -s victim.git/pre-receive.args &&
+ ! test -s victim.git/post-receive.args
+'
+
+test_expect_success 'send-pack produced no output' '
+ ! test -s send.out
+'
+
+cat <<EOF >expect
+remote: STDOUT pre-receive
+remote: STDERR pre-receive
+remote: STDOUT update refs/heads/master
+remote: STDERR update refs/heads/master
+remote: STDOUT update refs/heads/tofail
+remote: STDERR update refs/heads/tofail
+remote: error: hook declined to update refs/heads/tofail
+remote: STDOUT post-receive
+remote: STDERR post-receive
+remote: STDOUT post-update
+remote: STDERR post-update
+EOF
+test_expect_success 'send-pack stderr contains hook messages' '
+ grep ^remote: send.err | sed "s/ *\$//" >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5402-post-merge-hook.sh b/t/t5402-post-merge-hook.sh
new file mode 100755
index 0000000..6eb2ffd
--- /dev/null
+++ b/t/t5402-post-merge-hook.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Josh England
+#
+
+test_description='Test the post-merge hook.'
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo Data for commit0. >a &&
+ git update-index --add a &&
+ tree0=$(git write-tree) &&
+ commit0=$(echo setup | git commit-tree $tree0) &&
+ echo Changed data for commit1. >a &&
+ git update-index a &&
+ tree1=$(git write-tree) &&
+ commit1=$(echo modify | git commit-tree $tree1 -p $commit0) &&
+ git update-ref refs/heads/master $commit0 &&
+ git clone ./. clone1 &&
+ GIT_DIR=clone1/.git git update-index --add a &&
+ git clone ./. clone2 &&
+ GIT_DIR=clone2/.git git update-index --add a
+'
+
+for clone in 1 2; do
+ cat >clone${clone}/.git/hooks/post-merge <<'EOF'
+#!/bin/sh
+echo $@ >> $GIT_DIR/post-merge.args
+EOF
+ chmod u+x clone${clone}/.git/hooks/post-merge
+done
+
+test_expect_success 'post-merge does not run for up-to-date ' '
+ GIT_DIR=clone1/.git git merge $commit0 &&
+ ! test -f clone1/.git/post-merge.args
+'
+
+test_expect_success 'post-merge runs as expected ' '
+ GIT_DIR=clone1/.git git merge $commit1 &&
+ test -e clone1/.git/post-merge.args
+'
+
+test_expect_success 'post-merge from normal merge receives the right argument ' '
+ grep 0 clone1/.git/post-merge.args
+'
+
+test_expect_success 'post-merge from squash merge runs as expected ' '
+ GIT_DIR=clone2/.git git merge --squash $commit1 &&
+ test -e clone2/.git/post-merge.args
+'
+
+test_expect_success 'post-merge from squash merge receives the right argument ' '
+ grep 1 clone2/.git/post-merge.args
+'
+
+test_done
diff --git a/t/t5403-post-checkout-hook.sh b/t/t5403-post-checkout-hook.sh
new file mode 100755
index 0000000..d05a913
--- /dev/null
+++ b/t/t5403-post-checkout-hook.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Josh England
+#
+
+test_description='Test the post-checkout hook.'
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo Data for commit0. >a &&
+ echo Data for commit0. >b &&
+ git update-index --add a &&
+ git update-index --add b &&
+ tree0=$(git write-tree) &&
+ commit0=$(echo setup | git commit-tree $tree0) &&
+ git update-ref refs/heads/master $commit0 &&
+ git clone ./. clone1 &&
+ git clone ./. clone2 &&
+ GIT_DIR=clone2/.git git branch new2 &&
+ echo Data for commit1. >clone2/b &&
+ GIT_DIR=clone2/.git git add clone2/b &&
+ GIT_DIR=clone2/.git git commit -m new2
+'
+
+for clone in 1 2; do
+ cat >clone${clone}/.git/hooks/post-checkout <<'EOF'
+#!/bin/sh
+echo $@ > $GIT_DIR/post-checkout.args
+EOF
+ chmod u+x clone${clone}/.git/hooks/post-checkout
+done
+
+test_expect_success 'post-checkout runs as expected ' '
+ GIT_DIR=clone1/.git git checkout master &&
+ test -e clone1/.git/post-checkout.args
+'
+
+test_expect_success 'post-checkout receives the right arguments with HEAD unchanged ' '
+ old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
+ new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
+ flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
+ test $old = $new -a $flag = 1
+'
+
+test_expect_success 'post-checkout runs as expected ' '
+ GIT_DIR=clone1/.git git checkout master &&
+ test -e clone1/.git/post-checkout.args
+'
+
+test_expect_success 'post-checkout args are correct with git checkout -b ' '
+ GIT_DIR=clone1/.git git checkout -b new1 &&
+ old=$(awk "{print \$1}" clone1/.git/post-checkout.args) &&
+ new=$(awk "{print \$2}" clone1/.git/post-checkout.args) &&
+ flag=$(awk "{print \$3}" clone1/.git/post-checkout.args) &&
+ test $old = $new -a $flag = 1
+'
+
+test_expect_success 'post-checkout receives the right args with HEAD changed ' '
+ GIT_DIR=clone2/.git git checkout new2 &&
+ old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
+ new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
+ flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
+ test $old != $new -a $flag = 1
+'
+
+test_expect_success 'post-checkout receives the right args when not switching branches ' '
+ GIT_DIR=clone2/.git git checkout master b &&
+ old=$(awk "{print \$1}" clone2/.git/post-checkout.args) &&
+ new=$(awk "{print \$2}" clone2/.git/post-checkout.args) &&
+ flag=$(awk "{print \$3}" clone2/.git/post-checkout.args) &&
+ test $old = $new -a $flag = 0
+'
+
+if test "$(git config --bool core.filemode)" = true; then
+mkdir -p templates/hooks
+cat >templates/hooks/post-checkout <<'EOF'
+#!/bin/sh
+echo $@ > $GIT_DIR/post-checkout.args
+EOF
+chmod +x templates/hooks/post-checkout
+
+test_expect_success 'post-checkout hook is triggered by clone' '
+ git clone --template=templates . clone3 &&
+ test -f clone3/.git/post-checkout.args
+'
+fi
+
+test_done
diff --git a/t/t5404-tracking-branches.sh b/t/t5404-tracking-branches.sh
new file mode 100755
index 0000000..c240035
--- /dev/null
+++ b/t/t5404-tracking-branches.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='tracking branch update checks for git push'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo 1 >file &&
+ git add file &&
+ git commit -m 1 &&
+ git branch b1 &&
+ git branch b2 &&
+ git branch b3 &&
+ git clone . aa &&
+ git checkout b1 &&
+ echo b1 >>file &&
+ git commit -a -m b1 &&
+ git checkout b2 &&
+ echo b2 >>file &&
+ git commit -a -m b2
+'
+
+test_expect_success 'prepare pushable branches' '
+ cd aa &&
+ b1=$(git rev-parse origin/b1) &&
+ b2=$(git rev-parse origin/b2) &&
+ git checkout -b b1 origin/b1 &&
+ echo aa-b1 >>file &&
+ git commit -a -m aa-b1 &&
+ git checkout -b b2 origin/b2 &&
+ echo aa-b2 >>file &&
+ git commit -a -m aa-b2 &&
+ git checkout master &&
+ echo aa-master >>file &&
+ git commit -a -m aa-master
+'
+
+test_expect_success 'mixed-success push returns error' '
+ test_must_fail git push
+'
+
+test_expect_success 'check tracking branches updated correctly after push' '
+ test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
+'
+
+test_expect_success 'check tracking branches not updated for failed refs' '
+ test "$(git rev-parse origin/b1)" = "$b1" &&
+ test "$(git rev-parse origin/b2)" = "$b2"
+'
+
+test_expect_success 'deleted branches have their tracking branches removed' '
+ git push origin :b1 &&
+ test "$(git rev-parse origin/b1)" = "origin/b1"
+'
+
+test_expect_success 'already deleted tracking branches ignored' '
+ git branch -d -r origin/b3 &&
+ git push origin :b3 >output 2>&1 &&
+ ! grep error output
+'
+
+test_done
diff --git a/t/t5405-send-pack-rewind.sh b/t/t5405-send-pack-rewind.sh
new file mode 100755
index 0000000..4bda18a
--- /dev/null
+++ b/t/t5405-send-pack-rewind.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='forced push to replace commit we do not have'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >file1 && git add file1 && test_tick &&
+ git commit -m Initial &&
+ git config receive.denyCurrentBranch warn &&
+
+ mkdir another && (
+ cd another &&
+ git init &&
+ git fetch --update-head-ok .. master:master
+ ) &&
+
+ >file2 && git add file2 && test_tick &&
+ git commit -m Second
+
+'
+
+test_expect_success 'non forced push should die not segfault' '
+
+ (
+ cd another &&
+ git push .. master:master
+ test $? = 1
+ )
+
+'
+
+test_expect_success 'forced push should succeed' '
+
+ (
+ cd another &&
+ git push .. +master:master
+ )
+
+'
+
+test_done
diff --git a/t/t5406-remote-rejects.sh b/t/t5406-remote-rejects.sh
new file mode 100755
index 0000000..59e80a5
--- /dev/null
+++ b/t/t5406-remote-rejects.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+test_description='remote push rejects are reported by client'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir .git/hooks &&
+ (echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update &&
+ chmod +x .git/hooks/update &&
+ echo 1 >file &&
+ git add file &&
+ git commit -m 1 &&
+ git clone . child &&
+ cd child &&
+ echo 2 >file &&
+ git commit -a -m 2
+'
+
+test_expect_success 'push reports error' 'test_must_fail git push 2>stderr'
+
+test_expect_success 'individual ref reports error' 'grep rejected stderr'
+
+test_done
diff --git a/t/t5407-post-rewrite-hook.sh b/t/t5407-post-rewrite-hook.sh
new file mode 100755
index 0000000..552da65
--- /dev/null
+++ b/t/t5407-post-rewrite-hook.sh
@@ -0,0 +1,199 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Thomas Rast
+#
+
+test_description='Test the post-rewrite hook.'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit A foo A &&
+ test_commit B foo B &&
+ test_commit C foo C &&
+ test_commit D foo D
+'
+
+mkdir .git/hooks
+
+cat >.git/hooks/post-rewrite <<EOF
+#!/bin/sh
+echo \$@ > "$TRASH_DIRECTORY"/post-rewrite.args
+cat > "$TRASH_DIRECTORY"/post-rewrite.data
+EOF
+chmod u+x .git/hooks/post-rewrite
+
+clear_hook_input () {
+ rm -f post-rewrite.args post-rewrite.data
+}
+
+verify_hook_input () {
+ test_cmp "$TRASH_DIRECTORY"/post-rewrite.args expected.args &&
+ test_cmp "$TRASH_DIRECTORY"/post-rewrite.data expected.data
+}
+
+test_expect_success 'git commit --amend' '
+ clear_hook_input &&
+ echo "D new message" > newmsg &&
+ oldsha=$(git rev-parse HEAD^0) &&
+ git commit -Fnewmsg --amend &&
+ echo amend > expected.args &&
+ echo $oldsha $(git rev-parse HEAD^0) > expected.data &&
+ verify_hook_input
+'
+
+test_expect_success 'git commit --amend --no-post-rewrite' '
+ clear_hook_input &&
+ echo "D new message again" > newmsg &&
+ git commit --no-post-rewrite -Fnewmsg --amend &&
+ test ! -f post-rewrite.args &&
+ test ! -f post-rewrite.data
+'
+
+test_expect_success 'git rebase' '
+ git reset --hard D &&
+ clear_hook_input &&
+ test_must_fail git rebase --onto A B &&
+ echo C > foo &&
+ git add foo &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+ verify_hook_input
+'
+
+test_expect_success 'git rebase --skip' '
+ git reset --hard D &&
+ clear_hook_input &&
+ test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --skip &&
+ echo D > foo &&
+ git add foo &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<EOF &&
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+ verify_hook_input
+'
+
+test_expect_success 'git rebase -m' '
+ git reset --hard D &&
+ clear_hook_input &&
+ test_must_fail git rebase -m --onto A B &&
+ echo C > foo &&
+ git add foo &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+ verify_hook_input
+'
+
+test_expect_success 'git rebase -m --skip' '
+ git reset --hard D &&
+ clear_hook_input &&
+ test_must_fail git rebase --onto A B &&
+ test_must_fail git rebase --skip &&
+ echo D > foo &&
+ git add foo &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<EOF &&
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+ verify_hook_input
+'
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+# Helper to work around the lack of one-shot exporting for
+# test_must_fail (as it is a shell function)
+test_fail_interactive_rebase () {
+ (
+ FAKE_LINES="$1" &&
+ shift &&
+ export FAKE_LINES &&
+ test_must_fail git rebase -i "$@"
+ )
+}
+
+test_expect_success 'git rebase -i (unchanged)' '
+ git reset --hard D &&
+ clear_hook_input &&
+ test_fail_interactive_rebase "1 2" --onto A B &&
+ echo C > foo &&
+ git add foo &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+ verify_hook_input
+'
+
+test_expect_success 'git rebase -i (skip)' '
+ git reset --hard D &&
+ clear_hook_input &&
+ test_fail_interactive_rebase "2" --onto A B &&
+ echo D > foo &&
+ git add foo &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<EOF &&
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+ verify_hook_input
+'
+
+test_expect_success 'git rebase -i (squash)' '
+ git reset --hard D &&
+ clear_hook_input &&
+ test_fail_interactive_rebase "1 squash 2" --onto A B &&
+ echo C > foo &&
+ git add foo &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+ verify_hook_input
+'
+
+test_expect_success 'git rebase -i (fixup without conflict)' '
+ git reset --hard D &&
+ clear_hook_input &&
+ FAKE_LINES="1 fixup 2" git rebase -i B &&
+ echo rebase >expected.args &&
+ cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+ verify_hook_input
+'
+
+test_expect_success 'git rebase -i (double edit)' '
+ git reset --hard D &&
+ clear_hook_input &&
+ FAKE_LINES="edit 1 edit 2" git rebase -i B &&
+ git rebase --continue &&
+ echo something > foo &&
+ git add foo &&
+ git rebase --continue &&
+ echo rebase >expected.args &&
+ cat >expected.data <<EOF &&
+$(git rev-parse C) $(git rev-parse HEAD^)
+$(git rev-parse D) $(git rev-parse HEAD)
+EOF
+ verify_hook_input
+'
+
+test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
new file mode 100755
index 0000000..18376d6
--- /dev/null
+++ b/t/t5500-fetch-pack.sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Testing multi_ack pack fetching'
+
+. ./test-lib.sh
+
+# Test fetch-pack/upload-pack pair.
+
+# Some convenience functions
+
+add () {
+ name=$1 &&
+ text="$@" &&
+ branch=`echo $name | sed -e 's/^\(.\).*$/\1/'` &&
+ parents="" &&
+
+ shift &&
+ while test $1; do
+ parents="$parents -p $1" &&
+ shift
+ done &&
+
+ echo "$text" > test.txt &&
+ git update-index --add test.txt &&
+ tree=$(git write-tree) &&
+ # make sure timestamps are in correct order
+ test_tick &&
+ commit=$(echo "$text" | git commit-tree $tree $parents) &&
+ eval "$name=$commit; export $name" &&
+ echo $commit > .git/refs/heads/$branch &&
+ eval ${branch}TIP=$commit
+}
+
+pull_to_client () {
+ number=$1 &&
+ heads=$2 &&
+ count=$3 &&
+ test_expect_success "$number pull" '
+ (
+ cd client &&
+ git fetch-pack -k -v .. $heads &&
+
+ case "$heads" in
+ *A*)
+ echo $ATIP > .git/refs/heads/A;;
+ esac &&
+ case "$heads" in *B*)
+ echo $BTIP > .git/refs/heads/B;;
+ esac &&
+ git symbolic-ref HEAD refs/heads/`echo $heads \
+ | sed -e "s/^\(.\).*$/\1/"` &&
+
+ git fsck --full &&
+
+ mv .git/objects/pack/pack-* . &&
+ p=`ls -1 pack-*.pack` &&
+ git unpack-objects <$p &&
+ git fsck --full &&
+
+ idx=`echo pack-*.idx` &&
+ pack_count=`git show-index <$idx | wc -l` &&
+ test $pack_count = $count &&
+ rm -f pack-*
+ )
+ '
+}
+
+# Here begins the actual testing
+
+# A1 - ... - A20 - A21
+# \
+# B1 - B2 - .. - B70
+
+# client pulls A20, B1. Then tracks only B. Then pulls A.
+
+test_expect_success 'setup' '
+ mkdir client &&
+ (
+ cd client &&
+ git init &&
+ git config transfer.unpacklimit 0
+ ) &&
+ add A1 &&
+ prev=1 &&
+ cur=2 &&
+ while [ $cur -le 10 ]; do
+ add A$cur $(eval echo \$A$prev) &&
+ prev=$cur &&
+ cur=$(($cur+1))
+ done &&
+ add B1 $A1
+ echo $ATIP > .git/refs/heads/A &&
+ echo $BTIP > .git/refs/heads/B &&
+ git symbolic-ref HEAD refs/heads/B
+'
+
+pull_to_client 1st "B A" $((11*3))
+
+test_expect_success 'post 1st pull setup' '
+ add A11 $A10 &&
+ prev=1 &&
+ cur=2 &&
+ while [ $cur -le 65 ]; do
+ add B$cur $(eval echo \$B$prev) &&
+ prev=$cur &&
+ cur=$(($cur+1))
+ done
+'
+
+pull_to_client 2nd "B" $((64*3))
+
+pull_to_client 3rd "A" $((1*3))
+
+test_expect_success 'clone shallow' '
+ git clone --depth 2 "file://$(pwd)/." shallow
+'
+
+test_expect_success 'clone shallow object count' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow &&
+ grep "^in-pack: 18" count.shallow
+'
+
+test_expect_success 'clone shallow object count (part 2)' '
+ sed -e "/^in-pack:/d" -e "/^packs:/d" -e "/^size-pack:/d" \
+ -e "/: 0$/d" count.shallow > count_output &&
+ ! test -s count_output
+'
+
+test_expect_success 'fsck in shallow repo' '
+ (
+ cd shallow &&
+ git fsck --full
+ )
+'
+
+test_expect_success 'simple fetch in shallow repo' '
+ (
+ cd shallow &&
+ git fetch
+ )
+'
+
+test_expect_success 'no changes expected' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow.2 &&
+ cmp count.shallow count.shallow.2
+'
+
+test_expect_success 'fetch same depth in shallow repo' '
+ (
+ cd shallow &&
+ git fetch --depth=2
+ )
+'
+
+test_expect_success 'no changes expected' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow.3 &&
+ cmp count.shallow count.shallow.3
+'
+
+test_expect_success 'add two more' '
+ add B66 $B65 &&
+ add B67 $B66
+'
+
+test_expect_success 'pull in shallow repo' '
+ (
+ cd shallow &&
+ git pull .. B
+ )
+'
+
+test_expect_success 'clone shallow object count' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow &&
+ grep "^count: 6" count.shallow
+'
+
+test_expect_success 'add two more (part 2)' '
+ add B68 $B67 &&
+ add B69 $B68
+'
+
+test_expect_success 'deepening pull in shallow repo' '
+ (
+ cd shallow &&
+ git pull --depth 4 .. B
+ )
+'
+
+test_expect_success 'clone shallow object count' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow &&
+ grep "^count: 12" count.shallow
+'
+
+test_expect_success 'deepening fetch in shallow repo' '
+ (
+ cd shallow &&
+ git fetch --depth 4 .. A:A
+ )
+'
+
+test_expect_success 'clone shallow object count' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow &&
+ grep "^count: 18" count.shallow
+'
+
+test_expect_success 'pull in shallow repo with missing merge base' '
+ (
+ cd shallow &&
+ test_must_fail git pull --depth 4 .. A
+ )
+'
+
+test_expect_success 'additional simple shallow deepenings' '
+ (
+ cd shallow &&
+ git fetch --depth=8 &&
+ git fetch --depth=10 &&
+ git fetch --depth=11
+ )
+'
+
+test_expect_success 'clone shallow object count' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow &&
+ grep "^count: 52" count.shallow
+'
+
+test_done
diff --git a/t/t5502-quickfetch.sh b/t/t5502-quickfetch.sh
new file mode 100755
index 0000000..1037a72
--- /dev/null
+++ b/t/t5502-quickfetch.sh
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+test_description='test quickfetch from local'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ test_tick &&
+ echo ichi >file &&
+ git add file &&
+ git commit -m initial &&
+
+ cnt=$( (
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 3
+'
+
+test_expect_success 'clone without alternate' '
+
+ (
+ mkdir cloned &&
+ cd cloned &&
+ git init-db &&
+ git remote add -f origin ..
+ ) &&
+ cnt=$( (
+ cd cloned &&
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 3
+'
+
+test_expect_success 'further commits in the original' '
+
+ test_tick &&
+ echo ni >file &&
+ git commit -a -m second &&
+
+ cnt=$( (
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 6
+'
+
+test_expect_success 'copy commit and tree but not blob by hand' '
+
+ git rev-list --objects HEAD |
+ git pack-objects --stdout |
+ (
+ cd cloned &&
+ git unpack-objects
+ ) &&
+
+ cnt=$( (
+ cd cloned &&
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 6
+
+ blob=$(git rev-parse HEAD:file | sed -e "s|..|&/|") &&
+ test -f "cloned/.git/objects/$blob" &&
+ rm -f "cloned/.git/objects/$blob" &&
+
+ cnt=$( (
+ cd cloned &&
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 5
+
+'
+
+test_expect_success 'quickfetch should not leave a corrupted repository' '
+
+ (
+ cd cloned &&
+ git fetch
+ ) &&
+
+ cnt=$( (
+ cd cloned &&
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ test $cnt -eq 6
+
+'
+
+test_expect_success 'quickfetch should not copy from alternate' '
+
+ (
+ mkdir quickclone &&
+ cd quickclone &&
+ git init-db &&
+ (cd ../.git/objects && pwd) >.git/objects/info/alternates &&
+ git remote add origin .. &&
+ git fetch -k -k
+ ) &&
+ obj_cnt=$( (
+ cd quickclone &&
+ git count-objects | sed -e "s/ *objects,.*//"
+ ) ) &&
+ pck_cnt=$( (
+ cd quickclone &&
+ git count-objects -v | sed -n -e "/packs:/{
+ s/packs://
+ p
+ q
+ }"
+ ) ) &&
+ origin_master=$( (
+ cd quickclone &&
+ git rev-parse origin/master
+ ) ) &&
+ echo "loose objects: $obj_cnt, packfiles: $pck_cnt" &&
+ test $obj_cnt -eq 0 &&
+ test $pck_cnt -eq 0 &&
+ test z$origin_master = z$(git rev-parse master)
+
+'
+
+test_expect_success 'quickfetch should handle ~1000 refs (on Windows)' '
+
+ git gc &&
+ head=$(git rev-parse HEAD) &&
+ branchprefix="$head refs/heads/branch" &&
+ for i in 0 1 2 3 4 5 6 7 8 9; do
+ for j in 0 1 2 3 4 5 6 7 8 9; do
+ for k in 0 1 2 3 4 5 6 7 8 9; do
+ echo "$branchprefix$i$j$k" >> .git/packed-refs
+ done
+ done
+ done &&
+ (
+ cd cloned &&
+ git fetch &&
+ git fetch
+ )
+
+'
+
+test_done
diff --git a/t/t5503-tagfollow.sh b/t/t5503-tagfollow.sh
new file mode 100755
index 0000000..bab1a53
--- /dev/null
+++ b/t/t5503-tagfollow.sh
@@ -0,0 +1,156 @@
+#!/bin/sh
+
+test_description='test automatic tag following'
+
+. ./test-lib.sh
+
+case $(uname -s) in
+*MINGW*)
+ skip_all="GIT_DEBUG_SEND_PACK not supported - skipping tests"
+ test_done
+esac
+
+# End state of the repository:
+#
+# T - tag1 S - tag2
+# / /
+# L - A ------ O ------ B
+# \ \ \
+# \ C - origin/cat \
+# origin/master master
+
+test_expect_success setup '
+ test_tick &&
+ echo ichi >file &&
+ git add file &&
+ git commit -m L &&
+ L=$(git rev-parse --verify HEAD) &&
+
+ (
+ mkdir cloned &&
+ cd cloned &&
+ git init-db &&
+ git remote add -f origin ..
+ ) &&
+
+ test_tick &&
+ echo A >file &&
+ git add file &&
+ git commit -m A &&
+ A=$(git rev-parse --verify HEAD)
+'
+
+U=UPLOAD_LOG
+
+cat - <<EOF >expect
+#S
+want $A
+#E
+EOF
+test_expect_success 'fetch A (new commit : 1 connection)' '
+ rm -f $U
+ (
+ cd cloned &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $A = $(git rev-parse --verify origin/master)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "create tag T on A, create C on branch cat" '
+ git tag -a -m tag1 tag1 $A &&
+ T=$(git rev-parse --verify tag1) &&
+
+ git checkout -b cat &&
+ echo C >file &&
+ git add file &&
+ git commit -m C &&
+ C=$(git rev-parse --verify HEAD) &&
+ git checkout master
+'
+
+cat - <<EOF >expect
+#S
+want $C
+want $T
+#E
+EOF
+test_expect_success 'fetch C, T (new branch, tag : 1 connection)' '
+ rm -f $U
+ (
+ cd cloned &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $C = $(git rev-parse --verify origin/cat) &&
+ test $T = $(git rev-parse --verify tag1) &&
+ test $A = $(git rev-parse --verify tag1^0)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success "create commits O, B, tag S on B" '
+ test_tick &&
+ echo O >file &&
+ git add file &&
+ git commit -m O &&
+
+ test_tick &&
+ echo B >file &&
+ git add file &&
+ git commit -m B &&
+ B=$(git rev-parse --verify HEAD) &&
+
+ git tag -a -m tag2 tag2 $B &&
+ S=$(git rev-parse --verify tag2)
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'fetch B, S (commit and tag : 1 connection)' '
+ rm -f $U
+ (
+ cd cloned &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $B = $(git rev-parse --verify origin/master) &&
+ test $B = $(git rev-parse --verify tag2^0) &&
+ test $S = $(git rev-parse --verify tag2)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ test_cmp expect actual
+'
+
+cat - <<EOF >expect
+#S
+want $B
+want $S
+#E
+EOF
+test_expect_success 'new clone fetch master and tags' '
+ git branch -D cat
+ rm -f $U
+ (
+ mkdir clone2 &&
+ cd clone2 &&
+ git init &&
+ git remote add origin .. &&
+ GIT_DEBUG_SEND_PACK=3 git fetch 3>../$U &&
+ test $B = $(git rev-parse --verify origin/master) &&
+ test $S = $(git rev-parse --verify tag2) &&
+ test $B = $(git rev-parse --verify tag2^0) &&
+ test $T = $(git rev-parse --verify tag1) &&
+ test $A = $(git rev-parse --verify tag1^0)
+ ) &&
+ test -s $U &&
+ cut -d" " -f1,2 $U >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
new file mode 100755
index 0000000..4c498b1
--- /dev/null
+++ b/t/t5505-remote.sh
@@ -0,0 +1,867 @@
+#!/bin/sh
+
+test_description='git remote porcelain-ish'
+
+. ./test-lib.sh
+
+setup_repository () {
+ mkdir "$1" && (
+ cd "$1" &&
+ git init &&
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m "Initial" &&
+ git checkout -b side &&
+ >elif &&
+ git add elif &&
+ test_tick &&
+ git commit -m "Second" &&
+ git checkout master
+ )
+}
+
+tokens_match () {
+ echo "$1" | tr ' ' '\012' | sort | sed -e '/^$/d' >expect &&
+ echo "$2" | tr ' ' '\012' | sort | sed -e '/^$/d' >actual &&
+ test_cmp expect actual
+}
+
+check_remote_track () {
+ actual=$(git remote show "$1" | sed -ne 's|^ \(.*\) tracked$|\1|p')
+ shift &&
+ tokens_match "$*" "$actual"
+}
+
+check_tracking_branch () {
+ f="" &&
+ r=$(git for-each-ref "--format=%(refname)" |
+ sed -ne "s|^refs/remotes/$1/||p") &&
+ shift &&
+ tokens_match "$*" "$r"
+}
+
+test_expect_success setup '
+
+ setup_repository one &&
+ setup_repository two &&
+ (
+ cd two && git branch another
+ ) &&
+ git clone one test
+
+'
+
+test_expect_success 'remote information for the origin' '
+(
+ cd test &&
+ tokens_match origin "$(git remote)" &&
+ check_remote_track origin master side &&
+ check_tracking_branch origin HEAD master side
+)
+'
+
+test_expect_success 'add another remote' '
+(
+ cd test &&
+ git remote add -f second ../two &&
+ tokens_match "origin second" "$(git remote)" &&
+ check_remote_track origin master side &&
+ check_remote_track second master side another &&
+ check_tracking_branch second master side another &&
+ git for-each-ref "--format=%(refname)" refs/remotes |
+ sed -e "/^refs\/remotes\/origin\//d" \
+ -e "/^refs\/remotes\/second\//d" >actual &&
+ >expect &&
+ test_cmp expect actual
+)
+'
+
+test_expect_success 'remote forces tracking branches' '
+(
+ cd test &&
+ case `git config remote.second.fetch` in
+ +*) true ;;
+ *) false ;;
+ esac
+)
+'
+
+test_expect_success 'remove remote' '
+(
+ cd test &&
+ git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
+ git remote rm second
+)
+'
+
+test_expect_success 'remove remote' '
+(
+ cd test &&
+ tokens_match origin "$(git remote)" &&
+ check_remote_track origin master side &&
+ git for-each-ref "--format=%(refname)" refs/remotes |
+ sed -e "/^refs\/remotes\/origin\//d" >actual &&
+ >expect &&
+ test_cmp expect actual
+)
+'
+
+test_expect_success 'remove remote protects non-remote branches' '
+(
+ cd test &&
+ { cat >expect1 <<EOF
+Note: A non-remote branch was not removed; to delete it, use:
+ git branch -d master
+EOF
+ } &&
+ { cat >expect2 <<EOF
+Note: Non-remote branches were not removed; to delete them, use:
+ git branch -d foobranch
+ git branch -d master
+EOF
+ } &&
+ git tag footag &&
+ git config --add remote.oops.fetch "+refs/*:refs/*" &&
+ git remote rm oops 2>actual1 &&
+ git branch foobranch &&
+ git config --add remote.oops.fetch "+refs/*:refs/*" &&
+ git remote rm oops 2>actual2 &&
+ git branch -d foobranch &&
+ git tag -d footag &&
+ test_cmp expect1 actual1 &&
+ test_cmp expect2 actual2
+)
+'
+
+cat > test/expect << EOF
+* remote origin
+ Fetch URL: $(pwd)/one
+ Push URL: $(pwd)/one
+ HEAD branch: master
+ Remote branches:
+ master new (next fetch will store in remotes/origin)
+ side tracked
+ Local branches configured for 'git pull':
+ ahead merges with remote master
+ master merges with remote master
+ octopus merges with remote topic-a
+ and with remote topic-b
+ and with remote topic-c
+ rebase rebases onto remote master
+ Local refs configured for 'git push':
+ master pushes to master (local out of date)
+ master pushes to upstream (create)
+* remote two
+ Fetch URL: ../two
+ Push URL: ../three
+ HEAD branch (remote HEAD is ambiguous, may be one of the following):
+ another
+ master
+ Local refs configured for 'git push':
+ ahead forces to master (fast-forwardable)
+ master pushes to another (up to date)
+EOF
+
+test_expect_success 'show' '
+ (cd test &&
+ git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
+ git fetch &&
+ git checkout -b ahead origin/master &&
+ echo 1 >> file &&
+ test_tick &&
+ git commit -m update file &&
+ git checkout master &&
+ git branch --track octopus origin/master &&
+ git branch --track rebase origin/master &&
+ git branch -d -r origin/master &&
+ git config --add remote.two.url ../two &&
+ git config --add remote.two.pushurl ../three &&
+ git config branch.rebase.rebase true &&
+ git config branch.octopus.merge "topic-a topic-b topic-c" &&
+ (cd ../one &&
+ echo 1 > file &&
+ test_tick &&
+ git commit -m update file) &&
+ git config --add remote.origin.push : &&
+ git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
+ git config --add remote.origin.push +refs/tags/lastbackup &&
+ git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
+ git config --add remote.two.push refs/heads/master:refs/heads/another &&
+ git remote show origin two > output &&
+ git branch -d rebase octopus &&
+ test_cmp expect output)
+'
+
+cat > test/expect << EOF
+* remote origin
+ Fetch URL: $(pwd)/one
+ Push URL: $(pwd)/one
+ HEAD branch: (not queried)
+ Remote branches: (status not queried)
+ master
+ side
+ Local branches configured for 'git pull':
+ ahead merges with remote master
+ master merges with remote master
+ Local refs configured for 'git push' (status not queried):
+ (matching) pushes to (matching)
+ refs/heads/master pushes to refs/heads/upstream
+ refs/tags/lastbackup forces to refs/tags/lastbackup
+EOF
+
+test_expect_success 'show -n' '
+ (mv one one.unreachable &&
+ cd test &&
+ git remote show -n origin > output &&
+ mv ../one.unreachable ../one &&
+ test_cmp expect output)
+'
+
+test_expect_success 'prune' '
+ (cd one &&
+ git branch -m side side2) &&
+ (cd test &&
+ git fetch origin &&
+ git remote prune origin &&
+ git rev-parse refs/remotes/origin/side2 &&
+ test_must_fail git rev-parse refs/remotes/origin/side)
+'
+
+test_expect_success 'set-head --delete' '
+ (cd test &&
+ git symbolic-ref refs/remotes/origin/HEAD &&
+ git remote set-head --delete origin &&
+ test_must_fail git symbolic-ref refs/remotes/origin/HEAD)
+'
+
+test_expect_success 'set-head --auto' '
+ (cd test &&
+ git remote set-head --auto origin &&
+ echo refs/remotes/origin/master >expect &&
+ git symbolic-ref refs/remotes/origin/HEAD >output &&
+ test_cmp expect output
+ )
+'
+
+cat >test/expect <<EOF
+error: Multiple remote HEAD branches. Please choose one explicitly with:
+ git remote set-head two another
+ git remote set-head two master
+EOF
+
+test_expect_success 'set-head --auto fails w/multiple HEADs' '
+ (cd test &&
+ test_must_fail git remote set-head --auto two >output 2>&1 &&
+ test_cmp expect output)
+'
+
+cat >test/expect <<EOF
+refs/remotes/origin/side2
+EOF
+
+test_expect_success 'set-head explicit' '
+ (cd test &&
+ git remote set-head origin side2 &&
+ git symbolic-ref refs/remotes/origin/HEAD >output &&
+ git remote set-head origin master &&
+ test_cmp expect output)
+'
+
+cat > test/expect << EOF
+Pruning origin
+URL: $(pwd)/one
+ * [would prune] origin/side2
+EOF
+
+test_expect_success 'prune --dry-run' '
+ (cd one &&
+ git branch -m side2 side) &&
+ (cd test &&
+ git remote prune --dry-run origin > output &&
+ git rev-parse refs/remotes/origin/side2 &&
+ test_must_fail git rev-parse refs/remotes/origin/side &&
+ (cd ../one &&
+ git branch -m side side2) &&
+ test_cmp expect output)
+'
+
+test_expect_success 'add --mirror && prune' '
+ (mkdir mirror &&
+ cd mirror &&
+ git init --bare &&
+ git remote add --mirror -f origin ../one) &&
+ (cd one &&
+ git branch -m side2 side) &&
+ (cd mirror &&
+ git rev-parse --verify refs/heads/side2 &&
+ test_must_fail git rev-parse --verify refs/heads/side &&
+ git fetch origin &&
+ git remote prune origin &&
+ test_must_fail git rev-parse --verify refs/heads/side2 &&
+ git rev-parse --verify refs/heads/side)
+'
+
+test_expect_success 'add alt && prune' '
+ (mkdir alttst &&
+ cd alttst &&
+ git init &&
+ git remote add -f origin ../one &&
+ git config remote.alt.url ../one &&
+ git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*") &&
+ (cd one &&
+ git branch -m side side2) &&
+ (cd alttst &&
+ git rev-parse --verify refs/remotes/origin/side &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
+ git fetch alt &&
+ git remote prune alt &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/side &&
+ git rev-parse --verify refs/remotes/origin/side2)
+'
+
+cat >test/expect <<\EOF
+some-tag
+EOF
+
+test_expect_success 'add with reachable tags (default)' '
+ (cd one &&
+ >foobar &&
+ git add foobar &&
+ git commit -m "Foobar" &&
+ git tag -a -m "Foobar tag" foobar-tag &&
+ git reset --hard HEAD~1 &&
+ git tag -a -m "Some tag" some-tag) &&
+ (mkdir add-tags &&
+ cd add-tags &&
+ git init &&
+ git remote add -f origin ../one &&
+ git tag -l some-tag >../test/output &&
+ git tag -l foobar-tag >>../test/output &&
+ test_must_fail git config remote.origin.tagopt) &&
+ test_cmp test/expect test/output
+'
+
+cat >test/expect <<\EOF
+some-tag
+foobar-tag
+--tags
+EOF
+
+test_expect_success 'add --tags' '
+ (rm -rf add-tags &&
+ mkdir add-tags &&
+ cd add-tags &&
+ git init &&
+ git remote add -f --tags origin ../one &&
+ git tag -l some-tag >../test/output &&
+ git tag -l foobar-tag >>../test/output &&
+ git config remote.origin.tagopt >>../test/output) &&
+ test_cmp test/expect test/output
+'
+
+cat >test/expect <<\EOF
+--no-tags
+EOF
+
+test_expect_success 'add --no-tags' '
+ (rm -rf add-tags &&
+ mkdir add-no-tags &&
+ cd add-no-tags &&
+ git init &&
+ git remote add -f --no-tags origin ../one &&
+ git tag -l some-tag >../test/output &&
+ git tag -l foobar-tag >../test/output &&
+ git config remote.origin.tagopt >>../test/output) &&
+ (cd one &&
+ git tag -d some-tag foobar-tag) &&
+ test_cmp test/expect test/output
+'
+
+test_expect_success 'reject --no-no-tags' '
+ (cd add-no-tags &&
+ test_must_fail git remote add -f --no-no-tags neworigin ../one)
+'
+
+cat > one/expect << EOF
+ apis/master
+ apis/side
+ drosophila/another
+ drosophila/master
+ drosophila/side
+EOF
+
+test_expect_success 'update' '
+
+ (cd one &&
+ git remote add drosophila ../two &&
+ git remote add apis ../mirror &&
+ git remote update &&
+ git branch -r > output &&
+ test_cmp expect output)
+
+'
+
+cat > one/expect << EOF
+ drosophila/another
+ drosophila/master
+ drosophila/side
+ manduca/master
+ manduca/side
+ megaloprepus/master
+ megaloprepus/side
+EOF
+
+test_expect_success 'update with arguments' '
+
+ (cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || break
+ done &&
+ git remote add manduca ../mirror &&
+ git remote add megaloprepus ../mirror &&
+ git config remotes.phobaeticus "drosophila megaloprepus" &&
+ git config remotes.titanus manduca &&
+ git remote update phobaeticus titanus &&
+ git branch -r > output &&
+ test_cmp expect output)
+
+'
+
+test_expect_success 'update --prune' '
+
+ (cd one &&
+ git branch -m side2 side3) &&
+ (cd test &&
+ git remote update --prune &&
+ (cd ../one && git branch -m side3 side2)
+ git rev-parse refs/remotes/origin/side3 &&
+ test_must_fail git rev-parse refs/remotes/origin/side2)
+'
+
+cat > one/expect << EOF
+ apis/master
+ apis/side
+ manduca/master
+ manduca/side
+ megaloprepus/master
+ megaloprepus/side
+EOF
+
+test_expect_success 'update default' '
+
+ (cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || break
+ done &&
+ git config remote.drosophila.skipDefaultUpdate true &&
+ git remote update default &&
+ git branch -r > output &&
+ test_cmp expect output)
+
+'
+
+cat > one/expect << EOF
+ drosophila/another
+ drosophila/master
+ drosophila/side
+EOF
+
+test_expect_success 'update default (overridden, with funny whitespace)' '
+
+ (cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || break
+ done &&
+ git config remotes.default "$(printf "\t drosophila \n")" &&
+ git remote update default &&
+ git branch -r > output &&
+ test_cmp expect output)
+
+'
+
+test_expect_success 'update (with remotes.default defined)' '
+
+ (cd one &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || break
+ done &&
+ git config remotes.default "drosophila" &&
+ git remote update &&
+ git branch -r > output &&
+ test_cmp expect output)
+
+'
+
+test_expect_success '"remote show" does not show symbolic refs' '
+
+ git clone one three &&
+ (cd three &&
+ git remote show origin > output &&
+ ! grep "^ *HEAD$" < output &&
+ ! grep -i stale < output)
+
+'
+
+test_expect_success 'reject adding remote with an invalid name' '
+
+ test_must_fail git remote add some:url desired-name
+
+'
+
+# The first three test if the tracking branches are properly renamed,
+# the last two ones check if the config is updated.
+
+test_expect_success 'rename a remote' '
+
+ git clone one four &&
+ (cd four &&
+ git remote rename origin upstream &&
+ rmdir .git/refs/remotes/origin &&
+ test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
+ test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
+ test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
+ test "$(git config branch.master.remote)" = "upstream")
+
+'
+
+cat > remotes_origin << EOF
+URL: $(pwd)/one
+Push: refs/heads/master:refs/heads/upstream
+Pull: refs/heads/master:refs/heads/origin
+EOF
+
+test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
+ git clone one five &&
+ origin_url=$(pwd)/one &&
+ (cd five &&
+ git remote rm origin &&
+ mkdir -p .git/remotes &&
+ cat ../remotes_origin > .git/remotes/origin &&
+ git remote rename origin origin &&
+ ! test -f .git/remotes/origin &&
+ test "$(git config remote.origin.url)" = "$origin_url" &&
+ test "$(git config remote.origin.push)" = "refs/heads/master:refs/heads/upstream" &&
+ test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+'
+
+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
+ git clone one six &&
+ origin_url=$(pwd)/one &&
+ (cd six &&
+ git remote rm origin &&
+ echo "$origin_url" > .git/branches/origin &&
+ git remote rename origin origin &&
+ ! test -f .git/branches/origin &&
+ test "$(git config remote.origin.url)" = "$origin_url" &&
+ test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+'
+
+test_expect_success 'remote prune to cause a dangling symref' '
+ git clone one seven &&
+ (
+ cd one &&
+ git checkout side2 &&
+ git branch -D master
+ ) &&
+ (
+ cd seven &&
+ git remote prune origin
+ ) >err 2>&1 &&
+ grep "has become dangling" err &&
+
+ : And the dangling symref will not cause other annoying errors &&
+ (
+ cd seven &&
+ git branch -a
+ ) 2>err &&
+ ! grep "points nowhere" err &&
+ (
+ cd seven &&
+ test_must_fail git branch nomore origin
+ ) 2>err &&
+ grep "dangling symref" err
+'
+
+test_expect_success 'show empty remote' '
+
+ test_create_repo empty &&
+ git clone empty empty-clone &&
+ (
+ cd empty-clone &&
+ git remote show origin
+ )
+'
+
+test_expect_success 'remote set-branches requires a remote' '
+ test_must_fail git remote set-branches &&
+ test_must_fail git remote set-branches --add
+'
+
+test_expect_success 'remote set-branches' '
+ echo "+refs/heads/*:refs/remotes/scratch/*" >expect.initial &&
+ sort <<-\EOF >expect.add &&
+ +refs/heads/*:refs/remotes/scratch/*
+ +refs/heads/other:refs/remotes/scratch/other
+ EOF
+ sort <<-\EOF >expect.replace &&
+ +refs/heads/maint:refs/remotes/scratch/maint
+ +refs/heads/master:refs/remotes/scratch/master
+ +refs/heads/next:refs/remotes/scratch/next
+ EOF
+ sort <<-\EOF >expect.add-two &&
+ +refs/heads/maint:refs/remotes/scratch/maint
+ +refs/heads/master:refs/remotes/scratch/master
+ +refs/heads/next:refs/remotes/scratch/next
+ +refs/heads/pu:refs/remotes/scratch/pu
+ +refs/heads/t/topic:refs/remotes/scratch/t/topic
+ EOF
+ sort <<-\EOF >expect.setup-ffonly &&
+ refs/heads/master:refs/remotes/scratch/master
+ +refs/heads/next:refs/remotes/scratch/next
+ EOF
+ sort <<-\EOF >expect.respect-ffonly &&
+ refs/heads/master:refs/remotes/scratch/master
+ +refs/heads/next:refs/remotes/scratch/next
+ +refs/heads/pu:refs/remotes/scratch/pu
+ EOF
+
+ git clone .git/ setbranches &&
+ (
+ cd setbranches &&
+ git remote rename origin scratch &&
+ git config --get-all remote.scratch.fetch >config-result &&
+ sort <config-result >../actual.initial &&
+
+ git remote set-branches scratch --add other &&
+ git config --get-all remote.scratch.fetch >config-result &&
+ sort <config-result >../actual.add &&
+
+ git remote set-branches scratch maint master next &&
+ git config --get-all remote.scratch.fetch >config-result &&
+ sort <config-result >../actual.replace &&
+
+ git remote set-branches --add scratch pu t/topic &&
+ git config --get-all remote.scratch.fetch >config-result &&
+ sort <config-result >../actual.add-two &&
+
+ git config --unset-all remote.scratch.fetch &&
+ git config remote.scratch.fetch \
+ refs/heads/master:refs/remotes/scratch/master &&
+ git config --add remote.scratch.fetch \
+ +refs/heads/next:refs/remotes/scratch/next &&
+ git config --get-all remote.scratch.fetch >config-result &&
+ sort <config-result >../actual.setup-ffonly &&
+
+ git remote set-branches --add scratch pu &&
+ git config --get-all remote.scratch.fetch >config-result &&
+ sort <config-result >../actual.respect-ffonly
+ ) &&
+ test_cmp expect.initial actual.initial &&
+ test_cmp expect.add actual.add &&
+ test_cmp expect.replace actual.replace &&
+ test_cmp expect.add-two actual.add-two &&
+ test_cmp expect.setup-ffonly actual.setup-ffonly &&
+ test_cmp expect.respect-ffonly actual.respect-ffonly
+'
+
+test_expect_success 'remote set-branches with --mirror' '
+ echo "+refs/*:refs/*" >expect.initial &&
+ echo "+refs/heads/master:refs/heads/master" >expect.replace &&
+ git clone --mirror .git/ setbranches-mirror &&
+ (
+ cd setbranches-mirror &&
+ git remote rename origin scratch &&
+ git config --get-all remote.scratch.fetch >../actual.initial &&
+
+ git remote set-branches scratch heads/master &&
+ git config --get-all remote.scratch.fetch >../actual.replace
+ ) &&
+ test_cmp expect.initial actual.initial &&
+ test_cmp expect.replace actual.replace
+'
+
+test_expect_success 'new remote' '
+ git remote add someremote foo &&
+ echo foo >expect &&
+ git config --get-all remote.someremote.url >actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url bar' '
+ git remote set-url someremote bar &&
+ echo bar >expect &&
+ git config --get-all remote.someremote.url >actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url baz bar' '
+ git remote set-url someremote baz bar &&
+ echo baz >expect &&
+ git config --get-all remote.someremote.url >actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url zot bar' '
+ test_must_fail git remote set-url someremote zot bar &&
+ echo baz >expect &&
+ git config --get-all remote.someremote.url >actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --push zot baz' '
+ test_must_fail git remote set-url --push someremote zot baz &&
+ echo "YYY" >expect &&
+ echo baz >>expect &&
+ test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --push zot' '
+ git remote set-url --push someremote zot &&
+ echo zot >expect &&
+ echo "YYY" >>expect &&
+ echo baz >>expect &&
+ git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --push qux zot' '
+ git remote set-url --push someremote qux zot &&
+ echo qux >expect &&
+ echo "YYY" >>expect &&
+ echo baz >>expect &&
+ git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --push foo qu+x' '
+ git remote set-url --push someremote foo qu+x &&
+ echo foo >expect &&
+ echo "YYY" >>expect &&
+ echo baz >>expect &&
+ git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --push --add aaa' '
+ git remote set-url --push --add someremote aaa &&
+ echo foo >expect &&
+ echo aaa >>expect &&
+ echo "YYY" >>expect &&
+ echo baz >>expect &&
+ git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --push bar aaa' '
+ git remote set-url --push someremote bar aaa &&
+ echo foo >expect &&
+ echo bar >>expect &&
+ echo "YYY" >>expect &&
+ echo baz >>expect &&
+ git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --push --delete bar' '
+ git remote set-url --push --delete someremote bar &&
+ echo foo >expect &&
+ echo "YYY" >>expect &&
+ echo baz >>expect &&
+ git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --push --delete foo' '
+ git remote set-url --push --delete someremote foo &&
+ echo "YYY" >expect &&
+ echo baz >>expect &&
+ test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --add bbb' '
+ git remote set-url --add someremote bbb &&
+ echo "YYY" >expect &&
+ echo baz >>expect &&
+ echo bbb >>expect &&
+ test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --delete .*' '
+ test_must_fail git remote set-url --delete someremote .\* &&
+ echo "YYY" >expect &&
+ echo baz >>expect &&
+ echo bbb >>expect &&
+ test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --delete bbb' '
+ git remote set-url --delete someremote bbb &&
+ echo "YYY" >expect &&
+ echo baz >>expect &&
+ test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --delete baz' '
+ test_must_fail git remote set-url --delete someremote baz &&
+ echo "YYY" >expect &&
+ echo baz >>expect &&
+ test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --add ccc' '
+ git remote set-url --add someremote ccc &&
+ echo "YYY" >expect &&
+ echo baz >>expect &&
+ echo ccc >>expect &&
+ test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_expect_success 'remote set-url --delete baz' '
+ git remote set-url --delete someremote baz &&
+ echo "YYY" >expect &&
+ echo ccc >>expect &&
+ test_must_fail git config --get-all remote.someremote.pushurl >actual &&
+ echo "YYY" >>actual &&
+ git config --get-all remote.someremote.url >>actual &&
+ cmp expect actual
+'
+
+test_done
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
new file mode 100755
index 0000000..b7b7dda
--- /dev/null
+++ b/t/t5506-remote-groups.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='git remote group handling'
+. ./test-lib.sh
+
+mark() {
+ echo "$1" >mark
+}
+
+update_repo() {
+ (cd $1 &&
+ echo content >>file &&
+ git add file &&
+ git commit -F ../mark)
+}
+
+update_repos() {
+ update_repo one $1 &&
+ update_repo two $1
+}
+
+repo_fetched() {
+ if test "`git log -1 --pretty=format:%s $1 --`" = "`cat mark`"; then
+ echo >&2 "repo was fetched: $1"
+ return 0
+ fi
+ echo >&2 "repo was not fetched: $1"
+ return 1
+}
+
+test_expect_success 'setup' '
+ mkdir one && (cd one && git init) &&
+ mkdir two && (cd two && git init) &&
+ git remote add -m master one one &&
+ git remote add -m master two two
+'
+
+test_expect_success 'no group updates all' '
+ mark update-all &&
+ update_repos &&
+ git remote update &&
+ repo_fetched one &&
+ repo_fetched two
+'
+
+test_expect_success 'nonexistant group produces error' '
+ mark nonexistant &&
+ update_repos &&
+ test_must_fail git remote update nonexistant &&
+ ! repo_fetched one &&
+ ! repo_fetched two
+'
+
+test_expect_success 'updating group updates all members (remote update)' '
+ mark group-all &&
+ update_repos &&
+ git config --add remotes.all one &&
+ git config --add remotes.all two &&
+ git remote update all &&
+ repo_fetched one &&
+ repo_fetched two
+'
+
+test_expect_success 'updating group updates all members (fetch)' '
+ mark fetch-group-all &&
+ update_repos &&
+ git fetch all &&
+ repo_fetched one &&
+ repo_fetched two
+'
+
+test_expect_success 'updating group does not update non-members (remote update)' '
+ mark group-some &&
+ update_repos &&
+ git config --add remotes.some one &&
+ git remote update some &&
+ repo_fetched one &&
+ ! repo_fetched two
+'
+
+test_expect_success 'updating group does not update non-members (fetch)' '
+ mark fetch-group-some &&
+ update_repos &&
+ git config --add remotes.some one &&
+ git remote update some &&
+ repo_fetched one &&
+ ! repo_fetched two
+'
+
+test_expect_success 'updating remote name updates that remote' '
+ mark remote-name &&
+ update_repos &&
+ git remote update one &&
+ repo_fetched one &&
+ ! repo_fetched two
+'
+
+test_done
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
new file mode 100755
index 0000000..4eb10f6
--- /dev/null
+++ b/t/t5510-fetch.sh
@@ -0,0 +1,362 @@
+#!/bin/sh
+# Copyright (c) 2006, Junio C Hamano.
+
+test_description='Per branch config variables affects "git fetch".
+
+'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+test_bundle_object_count () {
+ git verify-pack -v "$1" >verify.out &&
+ test "$2" = $(grep '^[0-9a-f]\{40\} ' verify.out | wc -l)
+}
+
+test_expect_success setup '
+ echo >file original &&
+ git add file &&
+ git commit -a -m original'
+
+test_expect_success "clone and setup child repos" '
+ git clone . one &&
+ cd one &&
+ echo >file updated by one &&
+ git commit -a -m "updated by one" &&
+ cd .. &&
+ git clone . two &&
+ cd two &&
+ git config branch.master.remote one &&
+ git config remote.one.url ../one/.git/ &&
+ git config remote.one.fetch refs/heads/master:refs/heads/one &&
+ cd .. &&
+ git clone . three &&
+ cd three &&
+ git config branch.master.remote two &&
+ git config branch.master.merge refs/heads/one &&
+ mkdir -p .git/remotes &&
+ {
+ echo "URL: ../two/.git/"
+ echo "Pull: refs/heads/master:refs/heads/two"
+ echo "Pull: refs/heads/one:refs/heads/one"
+ } >.git/remotes/two &&
+ cd .. &&
+ git clone . bundle &&
+ git clone . seven
+'
+
+test_expect_success "fetch test" '
+ cd "$D" &&
+ echo >file updated by origin &&
+ git commit -a -m "updated by origin" &&
+ cd two &&
+ git fetch &&
+ test -f .git/refs/heads/one &&
+ mine=`git rev-parse refs/heads/one` &&
+ his=`cd ../one && git rev-parse refs/heads/master` &&
+ test "z$mine" = "z$his"
+'
+
+test_expect_success "fetch test for-merge" '
+ cd "$D" &&
+ cd three &&
+ git fetch &&
+ test -f .git/refs/heads/two &&
+ test -f .git/refs/heads/one &&
+ master_in_two=`cd ../two && git rev-parse master` &&
+ one_in_two=`cd ../two && git rev-parse one` &&
+ {
+ echo "$master_in_two not-for-merge"
+ echo "$one_in_two "
+ } >expected &&
+ cut -f -2 .git/FETCH_HEAD >actual &&
+ test_cmp expected actual'
+
+test_expect_success 'fetch tags when there is no tags' '
+
+ cd "$D" &&
+
+ mkdir notags &&
+ cd notags &&
+ git init &&
+
+ git fetch -t ..
+
+'
+
+test_expect_success 'fetch following tags' '
+
+ cd "$D" &&
+ git tag -a -m 'annotated' anno HEAD &&
+ git tag light HEAD &&
+
+ mkdir four &&
+ cd four &&
+ git init &&
+
+ git fetch .. :track &&
+ git show-ref --verify refs/tags/anno &&
+ git show-ref --verify refs/tags/light
+
+'
+
+test_expect_success 'fetch must not resolve short tag name' '
+
+ cd "$D" &&
+
+ mkdir five &&
+ cd five &&
+ git init &&
+
+ test_must_fail git fetch .. anno:five
+
+'
+
+test_expect_success 'fetch must not resolve short remote name' '
+
+ cd "$D" &&
+ git update-ref refs/remotes/six/HEAD HEAD
+
+ mkdir six &&
+ cd six &&
+ git init &&
+
+ test_must_fail git fetch .. six:six
+
+'
+
+test_expect_success 'create bundle 1' '
+ cd "$D" &&
+ echo >file updated again by origin &&
+ git commit -a -m "tip" &&
+ git bundle create bundle1 master^..master
+'
+
+test_expect_success 'header of bundle looks right' '
+ head -n 1 "$D"/bundle1 | grep "^#" &&
+ head -n 2 "$D"/bundle1 | grep "^-[0-9a-f]\{40\} " &&
+ head -n 3 "$D"/bundle1 | grep "^[0-9a-f]\{40\} " &&
+ head -n 4 "$D"/bundle1 | grep "^$"
+'
+
+test_expect_success 'create bundle 2' '
+ cd "$D" &&
+ git bundle create bundle2 master~2..master
+'
+
+test_expect_success 'unbundle 1' '
+ cd "$D/bundle" &&
+ git checkout -b some-branch &&
+ test_must_fail git fetch "$D/bundle1" master:master
+'
+
+
+test_expect_success 'bundle 1 has only 3 files ' '
+ cd "$D" &&
+ (
+ while read x && test -n "$x"
+ do
+ :;
+ done
+ cat
+ ) <bundle1 >bundle.pack &&
+ git index-pack bundle.pack &&
+ test_bundle_object_count bundle.pack 3
+'
+
+test_expect_success 'unbundle 2' '
+ cd "$D/bundle" &&
+ git fetch ../bundle2 master:master &&
+ test "tip" = "$(git log -1 --pretty=oneline master | cut -b42-)"
+'
+
+test_expect_success 'bundle does not prerequisite objects' '
+ cd "$D" &&
+ touch file2 &&
+ git add file2 &&
+ git commit -m add.file2 file2 &&
+ git bundle create bundle3 -1 HEAD &&
+ (
+ while read x && test -n "$x"
+ do
+ :;
+ done
+ cat
+ ) <bundle3 >bundle.pack &&
+ git index-pack bundle.pack &&
+ test_bundle_object_count bundle.pack 3
+'
+
+test_expect_success 'bundle should be able to create a full history' '
+
+ cd "$D" &&
+ git tag -a -m '1.0' v1.0 master &&
+ git bundle create bundle4 v1.0
+
+'
+
+! rsync --help > /dev/null 2> /dev/null &&
+say 'Skipping rsync tests because rsync was not found' || {
+test_expect_success 'fetch via rsync' '
+ git pack-refs &&
+ mkdir rsynced &&
+ (cd rsynced &&
+ git init --bare &&
+ git fetch "rsync:$(pwd)/../.git" master:refs/heads/master &&
+ git gc --prune &&
+ test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
+ git fsck --full)
+'
+
+test_expect_success 'push via rsync' '
+ mkdir rsynced2 &&
+ (cd rsynced2 &&
+ git init) &&
+ (cd rsynced &&
+ git push "rsync:$(pwd)/../rsynced2/.git" master) &&
+ (cd rsynced2 &&
+ git gc --prune &&
+ test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
+ git fsck --full)
+'
+
+test_expect_success 'push via rsync' '
+ mkdir rsynced3 &&
+ (cd rsynced3 &&
+ git init) &&
+ git push --all "rsync:$(pwd)/rsynced3/.git" &&
+ (cd rsynced3 &&
+ test $(git rev-parse master) = $(cd .. && git rev-parse master) &&
+ git fsck --full)
+'
+}
+
+test_expect_success 'fetch with a non-applying branch.<name>.merge' '
+ git config branch.master.remote yeti &&
+ git config branch.master.merge refs/heads/bigfoot &&
+ git config remote.blub.url one &&
+ git config remote.blub.fetch "refs/heads/*:refs/remotes/one/*" &&
+ git fetch blub
+'
+
+# the strange name is: a\!'b
+test_expect_success 'quoting of a strangely named repo' '
+ test_must_fail git fetch "a\\!'\''b" > result 2>&1 &&
+ cat result &&
+ grep "fatal: '\''a\\\\!'\''b'\''" result
+'
+
+test_expect_success 'bundle should record HEAD correctly' '
+
+ cd "$D" &&
+ git bundle create bundle5 HEAD master &&
+ git bundle list-heads bundle5 >actual &&
+ for h in HEAD refs/heads/master
+ do
+ echo "$(git rev-parse --verify $h) $h"
+ done >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'explicit fetch should not update tracking' '
+
+ cd "$D" &&
+ git branch -f side &&
+ (
+ cd three &&
+ o=$(git rev-parse --verify refs/remotes/origin/master) &&
+ git fetch origin master &&
+ n=$(git rev-parse --verify refs/remotes/origin/master) &&
+ test "$o" = "$n" &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/side
+ )
+'
+
+test_expect_success 'explicit pull should not update tracking' '
+
+ cd "$D" &&
+ git branch -f side &&
+ (
+ cd three &&
+ o=$(git rev-parse --verify refs/remotes/origin/master) &&
+ git pull origin master &&
+ n=$(git rev-parse --verify refs/remotes/origin/master) &&
+ test "$o" = "$n" &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/side
+ )
+'
+
+test_expect_success 'configured fetch updates tracking' '
+
+ cd "$D" &&
+ git branch -f side &&
+ (
+ cd three &&
+ o=$(git rev-parse --verify refs/remotes/origin/master) &&
+ git fetch origin &&
+ n=$(git rev-parse --verify refs/remotes/origin/master) &&
+ test "$o" != "$n" &&
+ git rev-parse --verify refs/remotes/origin/side
+ )
+'
+
+test_expect_success 'pushing nonexistent branch by mistake should not segv' '
+
+ cd "$D" &&
+ test_must_fail git push seven no:no
+
+'
+
+test_expect_success 'auto tag following fetches minimum' '
+
+ cd "$D" &&
+ git clone .git follow &&
+ git checkout HEAD^0 &&
+ (
+ for i in 1 2 3 4 5 6 7
+ do
+ echo $i >>file &&
+ git commit -m $i -a &&
+ git tag -a -m $i excess-$i || exit 1
+ done
+ ) &&
+ git checkout master &&
+ (
+ cd follow &&
+ git fetch
+ )
+'
+
+test_expect_success 'refuse to fetch into the current branch' '
+
+ test_must_fail git fetch . side:master
+
+'
+
+test_expect_success 'fetch into the current branch with --update-head-ok' '
+
+ git fetch --update-head-ok . side:master
+
+'
+
+test_expect_success 'fetch --dry-run' '
+
+ rm -f .git/FETCH_HEAD &&
+ git fetch --dry-run . &&
+ ! test -f .git/FETCH_HEAD
+'
+
+test_expect_success "should be able to fetch with duplicate refspecs" '
+ mkdir dups &&
+ cd dups &&
+ git init &&
+ git config branch.master.remote three &&
+ git config remote.three.url ../three/.git &&
+ git config remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
+ git config --add remote.three.fetch +refs/heads/*:refs/remotes/origin/* &&
+ git fetch three
+'
+
+test_done
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
new file mode 100755
index 0000000..c289322
--- /dev/null
+++ b/t/t5511-refspec.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='refspec parsing'
+
+. ./test-lib.sh
+
+test_refspec () {
+
+ kind=$1 refspec=$2 expect=$3
+ git config remote.frotz.url "." &&
+ git config --remove-section remote.frotz &&
+ git config remote.frotz.url "." &&
+ git config "remote.frotz.$kind" "$refspec" &&
+ if test "$expect" != invalid
+ then
+ title="$kind $refspec"
+ test='git ls-remote frotz'
+ else
+ title="$kind $refspec (invalid)"
+ test='test_must_fail git ls-remote frotz'
+ fi
+ test_expect_success "$title" "$test"
+}
+
+test_refspec push '' invalid
+test_refspec push ':'
+test_refspec push '::' invalid
+test_refspec push '+:'
+
+test_refspec fetch ''
+test_refspec fetch ':'
+test_refspec fetch '::' invalid
+
+test_refspec push 'refs/heads/*:refs/remotes/frotz/*'
+test_refspec push 'refs/heads/*:refs/remotes/frotz' invalid
+test_refspec push 'refs/heads:refs/remotes/frotz/*' invalid
+test_refspec push 'refs/heads/master:refs/remotes/frotz/xyzzy'
+
+
+# These have invalid LHS, but we do not have a formal "valid sha-1
+# expression syntax checker" so they are not checked with the current
+# code. They will be caught downstream anyway, but we may want to
+# have tighter check later...
+
+: test_refspec push 'refs/heads/master::refs/remotes/frotz/xyzzy' invalid
+: test_refspec push 'refs/heads/maste :refs/remotes/frotz/xyzzy' invalid
+
+test_refspec fetch 'refs/heads/*:refs/remotes/frotz/*'
+test_refspec fetch 'refs/heads/*:refs/remotes/frotz' invalid
+test_refspec fetch 'refs/heads:refs/remotes/frotz/*' invalid
+test_refspec fetch 'refs/heads/master:refs/remotes/frotz/xyzzy'
+test_refspec fetch 'refs/heads/master::refs/remotes/frotz/xyzzy' invalid
+test_refspec fetch 'refs/heads/maste :refs/remotes/frotz/xyzzy' invalid
+
+test_refspec push 'master~1:refs/remotes/frotz/backup'
+test_refspec fetch 'master~1:refs/remotes/frotz/backup' invalid
+test_refspec push 'HEAD~4:refs/remotes/frotz/new'
+test_refspec fetch 'HEAD~4:refs/remotes/frotz/new' invalid
+
+test_refspec push 'HEAD'
+test_refspec fetch 'HEAD'
+test_refspec push 'refs/heads/ nitfol' invalid
+test_refspec fetch 'refs/heads/ nitfol' invalid
+
+test_refspec push 'HEAD:' invalid
+test_refspec fetch 'HEAD:'
+test_refspec push 'refs/heads/ nitfol:' invalid
+test_refspec fetch 'refs/heads/ nitfol:' invalid
+
+test_refspec push ':refs/remotes/frotz/deleteme'
+test_refspec fetch ':refs/remotes/frotz/HEAD-to-me'
+test_refspec push ':refs/remotes/frotz/delete me' invalid
+test_refspec fetch ':refs/remotes/frotz/HEAD to me' invalid
+
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
+
+test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
+
+test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*'
+test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*'
+
+test_done
diff --git a/t/t5512-ls-remote.sh b/t/t5512-ls-remote.sh
new file mode 100755
index 0000000..d191235
--- /dev/null
+++ b/t/t5512-ls-remote.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='git ls-remote'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git tag mark &&
+ git show-ref --tags -d | sed -e "s/ / /" >expected.tag &&
+ (
+ echo "$(git rev-parse HEAD) HEAD"
+ git show-ref -d | sed -e "s/ / /"
+ ) >expected.all &&
+
+ git remote add self "$(pwd)/.git"
+
+'
+
+test_expect_success 'ls-remote --tags .git' '
+
+ git ls-remote --tags .git >actual &&
+ test_cmp expected.tag actual
+
+'
+
+test_expect_success 'ls-remote .git' '
+
+ git ls-remote .git >actual &&
+ test_cmp expected.all actual
+
+'
+
+test_expect_success 'ls-remote --tags self' '
+
+ git ls-remote --tags self >actual &&
+ test_cmp expected.tag actual
+
+'
+
+test_expect_success 'ls-remote self' '
+
+ git ls-remote self >actual &&
+ test_cmp expected.all actual
+
+'
+
+test_expect_success 'dies when no remote specified and no default remotes found' '
+
+ test_must_fail git ls-remote
+
+'
+
+test_expect_success 'use "origin" when no remote specified' '
+
+ URL="$(pwd)/.git" &&
+ echo "From $URL" >exp_err &&
+
+ git remote add origin "$URL" &&
+ git ls-remote 2>actual_err >actual &&
+
+ test_cmp exp_err actual_err &&
+ test_cmp expected.all actual
+
+'
+
+test_expect_success 'suppress "From <url>" with -q' '
+
+ git ls-remote -q 2>actual_err &&
+ test_must_fail test_cmp exp_err actual_err
+
+'
+
+test_expect_success 'use branch.<name>.remote if possible' '
+
+ #
+ # Test that we are indeed using branch.<name>.remote, not "origin", even
+ # though the "origin" remote has been set.
+ #
+
+ # setup a new remote to differentiate from "origin"
+ git clone . other.git &&
+ (
+ cd other.git &&
+ echo "$(git rev-parse HEAD) HEAD"
+ git show-ref | sed -e "s/ / /"
+ ) >exp &&
+
+ URL="other.git" &&
+ echo "From $URL" >exp_err &&
+
+ git remote add other $URL &&
+ git config branch.master.remote other &&
+
+ git ls-remote 2>actual_err >actual &&
+ test_cmp exp_err actual_err &&
+ test_cmp exp actual
+
+'
+
+cat >exp <<EOF
+fatal: 'refs*master' does not appear to be a git repository
+fatal: The remote end hung up unexpectedly
+EOF
+test_expect_success 'confuses pattern as remote when no remote specified' '
+ #
+ # Do not expect "git ls-remote <pattern>" to work; ls-remote, correctly,
+ # confuses <pattern> for <remote>. Although ugly, this behaviour is akin
+ # to the confusion of refspecs for remotes by git-fetch and git-push,
+ # eg:
+ #
+ # $ git fetch branch
+ #
+
+ # 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_done
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
new file mode 100755
index 0000000..9e74862
--- /dev/null
+++ b/t/t5513-fetch-track.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+test_description='fetch follows remote tracking branches correctly'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ >file &&
+ git add . &&
+ test_tick &&
+ git commit -m Initial &&
+ git branch b-0 &&
+ git branch b1 &&
+ git branch b/one &&
+ test_create_repo other &&
+ (
+ cd other &&
+ git config remote.origin.url .. &&
+ git config remote.origin.fetch "+refs/heads/b/*:refs/remotes/b/*"
+ )
+'
+
+test_expect_success fetch '
+ (
+ cd other && git fetch origin &&
+ test "$(git for-each-ref --format="%(refname)")" = refs/remotes/b/one
+ )
+'
+
+test_done
diff --git a/t/t5514-fetch-multiple.sh b/t/t5514-fetch-multiple.sh
new file mode 100755
index 0000000..b737332
--- /dev/null
+++ b/t/t5514-fetch-multiple.sh
@@ -0,0 +1,154 @@
+#!/bin/sh
+
+test_description='fetch --all works correctly'
+
+. ./test-lib.sh
+
+setup_repository () {
+ mkdir "$1" && (
+ cd "$1" &&
+ git init &&
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m "Initial" &&
+ git checkout -b side &&
+ >elif &&
+ git add elif &&
+ test_tick &&
+ git commit -m "Second" &&
+ git checkout master
+ )
+}
+
+test_expect_success setup '
+ setup_repository one &&
+ setup_repository two &&
+ (
+ cd two && git branch another
+ ) &&
+ git clone --mirror two three
+ git clone one test
+'
+
+cat > test/expect << EOF
+ one/master
+ one/side
+ origin/HEAD -> origin/master
+ origin/master
+ origin/side
+ three/another
+ three/master
+ three/side
+ two/another
+ two/master
+ two/side
+EOF
+
+test_expect_success 'git fetch --all' '
+ (cd test &&
+ git remote add one ../one &&
+ git remote add two ../two &&
+ git remote add three ../three &&
+ git fetch --all &&
+ git branch -r > output &&
+ test_cmp expect output)
+'
+
+test_expect_success 'git fetch --all should continue if a remote has errors' '
+ (git clone one test2 &&
+ cd test2 &&
+ git remote add bad ../non-existing &&
+ git remote add one ../one &&
+ git remote add two ../two &&
+ git remote add three ../three &&
+ test_must_fail git fetch --all &&
+ git branch -r > output &&
+ test_cmp ../test/expect output)
+'
+
+test_expect_success 'git fetch --all does not allow non-option arguments' '
+ (cd test &&
+ test_must_fail git fetch --all origin &&
+ test_must_fail git fetch --all origin master)
+'
+
+cat > expect << EOF
+ origin/HEAD -> origin/master
+ origin/master
+ origin/side
+ three/another
+ three/master
+ three/side
+EOF
+
+test_expect_success 'git fetch --multiple (but only one remote)' '
+ (git clone one test3 &&
+ cd test3 &&
+ git remote add three ../three &&
+ git fetch --multiple three &&
+ git branch -r > output &&
+ test_cmp ../expect output)
+'
+
+cat > expect << EOF
+ one/master
+ one/side
+ two/another
+ two/master
+ two/side
+EOF
+
+test_expect_success 'git fetch --multiple (two remotes)' '
+ (git clone one test4 &&
+ cd test4 &&
+ git remote rm origin &&
+ git remote add one ../one &&
+ git remote add two ../two &&
+ git fetch --multiple one two &&
+ git branch -r > output &&
+ test_cmp ../expect output)
+'
+
+test_expect_success 'git fetch --multiple (bad remote names)' '
+ (cd test4 &&
+ test_must_fail git fetch --multiple four)
+'
+
+
+test_expect_success 'git fetch --all (skipFetchAll)' '
+ (cd test4 &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || break
+ done &&
+ git remote add three ../three &&
+ git config remote.three.skipFetchAll true &&
+ git fetch --all &&
+ git branch -r > output &&
+ test_cmp ../expect output)
+'
+
+cat > expect << EOF
+ one/master
+ one/side
+ three/another
+ three/master
+ three/side
+ two/another
+ two/master
+ two/side
+EOF
+
+test_expect_success 'git fetch --multiple (ignoring skipFetchAll)' '
+ (cd test4 &&
+ for b in $(git branch -r)
+ do
+ git branch -r -d $b || break
+ done &&
+ git fetch --multiple one two three &&
+ git branch -r > output &&
+ test_cmp ../expect output)
+'
+
+test_done
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
new file mode 100755
index 0000000..dbb927d
--- /dev/null
+++ b/t/t5515-fetch-merge-logic.sh
@@ -0,0 +1,175 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Santi Béjar, based on t4013 by Junio C Hamano
+#
+#
+
+test_description='Merge logic in fetch'
+
+. ./test-lib.sh
+
+LF='
+'
+
+test_expect_success setup '
+ GIT_AUTHOR_DATE="2006-06-26 00:00:00 +0000" &&
+ GIT_COMMITTER_DATE="2006-06-26 00:00:00 +0000" &&
+ export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+ echo >file original &&
+ git add file &&
+ git commit -a -m One &&
+ git tag tag-one &&
+ git tag tag-one-tree HEAD^{tree} &&
+ git branch one &&
+
+ echo two >> file &&
+ git commit -a -m Two &&
+ git tag -a -m "Tag Two" tag-two &&
+ git branch two &&
+
+ echo three >> file &&
+ git commit -a -m Three &&
+ git tag -a -m "Tag Three" tag-three &&
+ git tag -a -m "Tag Three file" tag-three-file HEAD^{tree}:file &&
+ git branch three &&
+
+ echo master >> file &&
+ git commit -a -m Master &&
+ git tag -a -m "Tag Master" tag-master &&
+
+ git checkout three &&
+
+ git clone . cloned &&
+ cd cloned &&
+ git config remote.origin.url ../.git/ &&
+
+ git config remote.config-explicit.url ../.git/ &&
+ git config remote.config-explicit.fetch refs/heads/master:remotes/rem/master &&
+ git config --add remote.config-explicit.fetch refs/heads/one:remotes/rem/one &&
+ git config --add remote.config-explicit.fetch two:remotes/rem/two &&
+ git config --add remote.config-explicit.fetch refs/heads/three:remotes/rem/three &&
+ remotes="config-explicit" &&
+
+ git config remote.config-glob.url ../.git/ &&
+ git config remote.config-glob.fetch refs/heads/*:refs/remotes/rem/* &&
+ remotes="$remotes config-glob" &&
+
+ mkdir -p .git/remotes &&
+ {
+ echo "URL: ../.git/"
+ echo "Pull: refs/heads/master:remotes/rem/master"
+ echo "Pull: refs/heads/one:remotes/rem/one"
+ echo "Pull: two:remotes/rem/two"
+ echo "Pull: refs/heads/three:remotes/rem/three"
+ } >.git/remotes/remote-explicit &&
+ remotes="$remotes remote-explicit" &&
+
+ {
+ echo "URL: ../.git/"
+ echo "Pull: refs/heads/*:refs/remotes/rem/*"
+ } >.git/remotes/remote-glob &&
+ remotes="$remotes remote-glob" &&
+
+ mkdir -p .git/branches &&
+ echo "../.git" > .git/branches/branches-default &&
+ remotes="$remotes branches-default" &&
+
+ echo "../.git#one" > .git/branches/branches-one &&
+ remotes="$remotes branches-one" &&
+
+ for remote in $remotes ; do
+ git config branch.br-$remote.remote $remote &&
+ git config branch.br-$remote-merge.remote $remote &&
+ git config branch.br-$remote-merge.merge refs/heads/three &&
+ git config branch.br-$remote-octopus.remote $remote &&
+ git config branch.br-$remote-octopus.merge refs/heads/one &&
+ git config --add branch.br-$remote-octopus.merge two
+ done
+'
+
+# Merge logic depends on branch properties and Pull: or .fetch lines
+for remote in $remotes ; do
+ for branch in "" "-merge" "-octopus" ; do
+cat <<EOF
+br-$remote$branch
+br-$remote$branch $remote
+EOF
+ done
+done > tests
+
+# Merge logic does not depend on branch properties,
+# but does depend on Pull: or fetch lines.
+# Use two branches completely unrelated from the arguments,
+# the clone default and one without branch properties
+for branch in master br-unconfig ; do
+ echo $branch
+ for remote in $remotes ; do
+ echo $branch $remote
+ done
+done >> tests
+
+# Merge logic does not depend on branch properties
+# neither in the Pull: or .fetch config
+for branch in master br-unconfig ; do
+ cat <<EOF
+$branch ../.git
+$branch ../.git one
+$branch ../.git one two
+$branch --tags ../.git
+$branch ../.git tag tag-one tag tag-three
+$branch ../.git tag tag-one-tree tag tag-three-file
+$branch ../.git one tag tag-one tag tag-three-file
+EOF
+done >> tests
+
+while read cmd
+do
+ case "$cmd" in
+ '' | '#'*) continue ;;
+ esac
+ test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
+ pfx=`printf "%04d" $test_count`
+ expect_f="$TEST_DIRECTORY/t5515/fetch.$test"
+ actual_f="$pfx-fetch.$test"
+ expect_r="$TEST_DIRECTORY/t5515/refs.$test"
+ actual_r="$pfx-refs.$test"
+
+ test_expect_success "$cmd" '
+ {
+ echo "# $cmd"
+ set x $cmd; shift
+ git symbolic-ref HEAD refs/heads/$1 ; shift
+ rm -f .git/FETCH_HEAD
+ git for-each-ref \
+ refs/heads refs/remotes/rem refs/tags |
+ while read val type refname
+ do
+ git update-ref -d "$refname" "$val"
+ done
+ git fetch "$@" >/dev/null
+ cat .git/FETCH_HEAD
+ } >"$actual_f" &&
+ git show-ref >"$actual_r" &&
+ if test -f "$expect_f"
+ then
+ test_cmp "$expect_f" "$actual_f" &&
+ rm -f "$actual_f"
+ else
+ # this is to help developing new tests.
+ cp "$actual_f" "$expect_f"
+ false
+ fi &&
+ if test -f "$expect_r"
+ then
+ test_cmp "$expect_r" "$actual_r" &&
+ rm -f "$actual_r"
+ else
+ # this is to help developing new tests.
+ cp "$actual_r" "$expect_r"
+ false
+ fi
+ '
+done < tests
+
+test_done
diff --git a/t/t5515/fetch.br-branches-default b/t/t5515/fetch.br-branches-default
new file mode 100644
index 0000000..2e0414f
--- /dev/null
+++ b/t/t5515/fetch.br-branches-default
@@ -0,0 +1,8 @@
+# br-branches-default
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-merge b/t/t5515/fetch.br-branches-default-merge
new file mode 100644
index 0000000..ca2cc1d
--- /dev/null
+++ b/t/t5515/fetch.br-branches-default-merge
@@ -0,0 +1,9 @@
+# br-branches-default-merge
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-merge_branches-default b/t/t5515/fetch.br-branches-default-merge_branches-default
new file mode 100644
index 0000000..7d947cd
--- /dev/null
+++ b/t/t5515/fetch.br-branches-default-merge_branches-default
@@ -0,0 +1,9 @@
+# br-branches-default-merge branches-default
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-octopus b/t/t5515/fetch.br-branches-default-octopus
new file mode 100644
index 0000000..ec39c54
--- /dev/null
+++ b/t/t5515/fetch.br-branches-default-octopus
@@ -0,0 +1,10 @@
+# br-branches-default-octopus
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default-octopus_branches-default b/t/t5515/fetch.br-branches-default-octopus_branches-default
new file mode 100644
index 0000000..6bf42e2
--- /dev/null
+++ b/t/t5515/fetch.br-branches-default-octopus_branches-default
@@ -0,0 +1,10 @@
+# br-branches-default-octopus branches-default
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-default_branches-default b/t/t5515/fetch.br-branches-default_branches-default
new file mode 100644
index 0000000..4a2bf3c
--- /dev/null
+++ b/t/t5515/fetch.br-branches-default_branches-default
@@ -0,0 +1,8 @@
+# br-branches-default branches-default
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one b/t/t5515/fetch.br-branches-one
new file mode 100644
index 0000000..12ac8d2
--- /dev/null
+++ b/t/t5515/fetch.br-branches-one
@@ -0,0 +1,8 @@
+# br-branches-one
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-merge b/t/t5515/fetch.br-branches-one-merge
new file mode 100644
index 0000000..b4b3b35
--- /dev/null
+++ b/t/t5515/fetch.br-branches-one-merge
@@ -0,0 +1,9 @@
+# br-branches-one-merge
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-merge_branches-one b/t/t5515/fetch.br-branches-one-merge_branches-one
new file mode 100644
index 0000000..2ecef38
--- /dev/null
+++ b/t/t5515/fetch.br-branches-one-merge_branches-one
@@ -0,0 +1,9 @@
+# br-branches-one-merge branches-one
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-octopus b/t/t5515/fetch.br-branches-one-octopus
new file mode 100644
index 0000000..96e3029
--- /dev/null
+++ b/t/t5515/fetch.br-branches-one-octopus
@@ -0,0 +1,9 @@
+# br-branches-one-octopus
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one-octopus_branches-one b/t/t5515/fetch.br-branches-one-octopus_branches-one
new file mode 100644
index 0000000..55e0bad
--- /dev/null
+++ b/t/t5515/fetch.br-branches-one-octopus_branches-one
@@ -0,0 +1,9 @@
+# br-branches-one-octopus branches-one
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-branches-one_branches-one b/t/t5515/fetch.br-branches-one_branches-one
new file mode 100644
index 0000000..281fa09
--- /dev/null
+++ b/t/t5515/fetch.br-branches-one_branches-one
@@ -0,0 +1,8 @@
+# br-branches-one branches-one
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit b/t/t5515/fetch.br-config-explicit
new file mode 100644
index 0000000..e2fa9c8
--- /dev/null
+++ b/t/t5515/fetch.br-config-explicit
@@ -0,0 +1,11 @@
+# br-config-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-merge b/t/t5515/fetch.br-config-explicit-merge
new file mode 100644
index 0000000..ec1a723
--- /dev/null
+++ b/t/t5515/fetch.br-config-explicit-merge
@@ -0,0 +1,11 @@
+# br-config-explicit-merge
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-merge_config-explicit b/t/t5515/fetch.br-config-explicit-merge_config-explicit
new file mode 100644
index 0000000..54f6891
--- /dev/null
+++ b/t/t5515/fetch.br-config-explicit-merge_config-explicit
@@ -0,0 +1,11 @@
+# br-config-explicit-merge config-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-octopus b/t/t5515/fetch.br-config-explicit-octopus
new file mode 100644
index 0000000..7011dfc
--- /dev/null
+++ b/t/t5515/fetch.br-config-explicit-octopus
@@ -0,0 +1,11 @@
+# br-config-explicit-octopus
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit-octopus_config-explicit b/t/t5515/fetch.br-config-explicit-octopus_config-explicit
new file mode 100644
index 0000000..bdad51f
--- /dev/null
+++ b/t/t5515/fetch.br-config-explicit-octopus_config-explicit
@@ -0,0 +1,11 @@
+# br-config-explicit-octopus config-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-explicit_config-explicit b/t/t5515/fetch.br-config-explicit_config-explicit
new file mode 100644
index 0000000..1b237dd
--- /dev/null
+++ b/t/t5515/fetch.br-config-explicit_config-explicit
@@ -0,0 +1,11 @@
+# br-config-explicit config-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob b/t/t5515/fetch.br-config-glob
new file mode 100644
index 0000000..e75ec2f
--- /dev/null
+++ b/t/t5515/fetch.br-config-glob
@@ -0,0 +1,11 @@
+# br-config-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-merge b/t/t5515/fetch.br-config-glob-merge
new file mode 100644
index 0000000..ce8f739
--- /dev/null
+++ b/t/t5515/fetch.br-config-glob-merge
@@ -0,0 +1,11 @@
+# br-config-glob-merge
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-merge_config-glob b/t/t5515/fetch.br-config-glob-merge_config-glob
new file mode 100644
index 0000000..5817bed
--- /dev/null
+++ b/t/t5515/fetch.br-config-glob-merge_config-glob
@@ -0,0 +1,11 @@
+# br-config-glob-merge config-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-octopus b/t/t5515/fetch.br-config-glob-octopus
new file mode 100644
index 0000000..938e532
--- /dev/null
+++ b/t/t5515/fetch.br-config-glob-octopus
@@ -0,0 +1,11 @@
+# br-config-glob-octopus
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob-octopus_config-glob b/t/t5515/fetch.br-config-glob-octopus_config-glob
new file mode 100644
index 0000000..c9225bf
--- /dev/null
+++ b/t/t5515/fetch.br-config-glob-octopus_config-glob
@@ -0,0 +1,11 @@
+# br-config-glob-octopus config-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-config-glob_config-glob b/t/t5515/fetch.br-config-glob_config-glob
new file mode 100644
index 0000000..a6c20f9
--- /dev/null
+++ b/t/t5515/fetch.br-config-glob_config-glob
@@ -0,0 +1,11 @@
+# br-config-glob config-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit b/t/t5515/fetch.br-remote-explicit
new file mode 100644
index 0000000..83534d2
--- /dev/null
+++ b/t/t5515/fetch.br-remote-explicit
@@ -0,0 +1,11 @@
+# br-remote-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-merge b/t/t5515/fetch.br-remote-explicit-merge
new file mode 100644
index 0000000..a9064dd
--- /dev/null
+++ b/t/t5515/fetch.br-remote-explicit-merge
@@ -0,0 +1,11 @@
+# br-remote-explicit-merge
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-merge_remote-explicit b/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
new file mode 100644
index 0000000..732a37e
--- /dev/null
+++ b/t/t5515/fetch.br-remote-explicit-merge_remote-explicit
@@ -0,0 +1,11 @@
+# br-remote-explicit-merge remote-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-octopus b/t/t5515/fetch.br-remote-explicit-octopus
new file mode 100644
index 0000000..ecf020d
--- /dev/null
+++ b/t/t5515/fetch.br-remote-explicit-octopus
@@ -0,0 +1,11 @@
+# br-remote-explicit-octopus
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit b/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
new file mode 100644
index 0000000..af77531
--- /dev/null
+++ b/t/t5515/fetch.br-remote-explicit-octopus_remote-explicit
@@ -0,0 +1,11 @@
+# br-remote-explicit-octopus remote-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-explicit_remote-explicit b/t/t5515/fetch.br-remote-explicit_remote-explicit
new file mode 100644
index 0000000..51fae56
--- /dev/null
+++ b/t/t5515/fetch.br-remote-explicit_remote-explicit
@@ -0,0 +1,11 @@
+# br-remote-explicit remote-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob b/t/t5515/fetch.br-remote-glob
new file mode 100644
index 0000000..94e6ad3
--- /dev/null
+++ b/t/t5515/fetch.br-remote-glob
@@ -0,0 +1,11 @@
+# br-remote-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-merge b/t/t5515/fetch.br-remote-glob-merge
new file mode 100644
index 0000000..09362e2
--- /dev/null
+++ b/t/t5515/fetch.br-remote-glob-merge
@@ -0,0 +1,11 @@
+# br-remote-glob-merge
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-merge_remote-glob b/t/t5515/fetch.br-remote-glob-merge_remote-glob
new file mode 100644
index 0000000..e2eabec
--- /dev/null
+++ b/t/t5515/fetch.br-remote-glob-merge_remote-glob
@@ -0,0 +1,11 @@
+# br-remote-glob-merge remote-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-octopus b/t/t5515/fetch.br-remote-glob-octopus
new file mode 100644
index 0000000..b08e046
--- /dev/null
+++ b/t/t5515/fetch.br-remote-glob-octopus
@@ -0,0 +1,11 @@
+# br-remote-glob-octopus
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob-octopus_remote-glob b/t/t5515/fetch.br-remote-glob-octopus_remote-glob
new file mode 100644
index 0000000..d4d547c
--- /dev/null
+++ b/t/t5515/fetch.br-remote-glob-octopus_remote-glob
@@ -0,0 +1,11 @@
+# br-remote-glob-octopus remote-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-remote-glob_remote-glob b/t/t5515/fetch.br-remote-glob_remote-glob
new file mode 100644
index 0000000..646dbc8
--- /dev/null
+++ b/t/t5515/fetch.br-remote-glob_remote-glob
@@ -0,0 +1,11 @@
+# br-remote-glob remote-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig b/t/t5515/fetch.br-unconfig
new file mode 100644
index 0000000..65ce6d9
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig
@@ -0,0 +1,11 @@
+# br-unconfig
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_--tags_.._.git b/t/t5515/fetch.br-unconfig_--tags_.._.git
new file mode 100644
index 0000000..8258c80
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_--tags_.._.git
@@ -0,0 +1,7 @@
+# br-unconfig --tags ../.git
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git b/t/t5515/fetch.br-unconfig_.._.git
new file mode 100644
index 0000000..284bb1f
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_.._.git
@@ -0,0 +1,2 @@
+# br-unconfig ../.git
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_one b/t/t5515/fetch.br-unconfig_.._.git_one
new file mode 100644
index 0000000..11eb5a6
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_.._.git_one
@@ -0,0 +1,2 @@
+# br-unconfig ../.git one
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file b/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
new file mode 100644
index 0000000..f02bab2
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
@@ -0,0 +1,8 @@
+# br-unconfig ../.git one tag tag-one tag tag-three-file
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 tag 'tag-one' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_one_two b/t/t5515/fetch.br-unconfig_.._.git_one_two
new file mode 100644
index 0000000..3f1be22
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_.._.git_one_two
@@ -0,0 +1,3 @@
+# br-unconfig ../.git one two
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
new file mode 100644
index 0000000..85de411
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
@@ -0,0 +1,7 @@
+# br-unconfig ../.git tag tag-one-tree tag tag-three-file
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three
new file mode 100644
index 0000000..0da2337
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_.._.git_tag_tag-one_tag_tag-three
@@ -0,0 +1,7 @@
+# br-unconfig ../.git tag tag-one tag tag-three
+8e32a6d901327a23ef831511badce7bf3bf46689 tag 'tag-one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b tag 'tag-three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_branches-default b/t/t5515/fetch.br-unconfig_branches-default
new file mode 100644
index 0000000..fc7041e
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_branches-default
@@ -0,0 +1,8 @@
+# br-unconfig branches-default
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_branches-one b/t/t5515/fetch.br-unconfig_branches-one
new file mode 100644
index 0000000..e94cde7
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_branches-one
@@ -0,0 +1,8 @@
+# br-unconfig branches-one
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_config-explicit b/t/t5515/fetch.br-unconfig_config-explicit
new file mode 100644
index 0000000..01a283e
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_config-explicit
@@ -0,0 +1,11 @@
+# br-unconfig config-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_config-glob b/t/t5515/fetch.br-unconfig_config-glob
new file mode 100644
index 0000000..3a556c5
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_config-glob
@@ -0,0 +1,11 @@
+# br-unconfig config-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_remote-explicit b/t/t5515/fetch.br-unconfig_remote-explicit
new file mode 100644
index 0000000..db216df
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_remote-explicit
@@ -0,0 +1,11 @@
+# br-unconfig remote-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.br-unconfig_remote-glob b/t/t5515/fetch.br-unconfig_remote-glob
new file mode 100644
index 0000000..aee65c2
--- /dev/null
+++ b/t/t5515/fetch.br-unconfig_remote-glob
@@ -0,0 +1,11 @@
+# br-unconfig remote-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master b/t/t5515/fetch.master
new file mode 100644
index 0000000..950fd07
--- /dev/null
+++ b/t/t5515/fetch.master
@@ -0,0 +1,11 @@
+# master
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_--tags_.._.git b/t/t5515/fetch.master_--tags_.._.git
new file mode 100644
index 0000000..0e59950
--- /dev/null
+++ b/t/t5515/fetch.master_--tags_.._.git
@@ -0,0 +1,7 @@
+# master --tags ../.git
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_.._.git b/t/t5515/fetch.master_.._.git
new file mode 100644
index 0000000..66d1aad
--- /dev/null
+++ b/t/t5515/fetch.master_.._.git
@@ -0,0 +1,2 @@
+# master ../.git
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b ../
diff --git a/t/t5515/fetch.master_.._.git_one b/t/t5515/fetch.master_.._.git_one
new file mode 100644
index 0000000..35deddb
--- /dev/null
+++ b/t/t5515/fetch.master_.._.git_one
@@ -0,0 +1,2 @@
+# master ../.git one
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
diff --git a/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file b/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file
new file mode 100644
index 0000000..8286852
--- /dev/null
+++ b/t/t5515/fetch.master_.._.git_one_tag_tag-one_tag_tag-three-file
@@ -0,0 +1,8 @@
+# master ../.git one tag tag-one tag tag-three-file
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 tag 'tag-one' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_.._.git_one_two b/t/t5515/fetch.master_.._.git_one_two
new file mode 100644
index 0000000..35ec578
--- /dev/null
+++ b/t/t5515/fetch.master_.._.git_one_two
@@ -0,0 +1,3 @@
+# master ../.git one two
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 branch 'two' of ../
diff --git a/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file b/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file
new file mode 100644
index 0000000..2e133ef
--- /dev/null
+++ b/t/t5515/fetch.master_.._.git_tag_tag-one-tree_tag_tag-three-file
@@ -0,0 +1,7 @@
+# master ../.git tag tag-one-tree tag tag-three-file
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three b/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
new file mode 100644
index 0000000..92b18b4
--- /dev/null
+++ b/t/t5515/fetch.master_.._.git_tag_tag-one_tag_tag-three
@@ -0,0 +1,7 @@
+# master ../.git tag tag-one tag tag-three
+8e32a6d901327a23ef831511badce7bf3bf46689 tag 'tag-one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b tag 'tag-three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_branches-default b/t/t5515/fetch.master_branches-default
new file mode 100644
index 0000000..603d6d2
--- /dev/null
+++ b/t/t5515/fetch.master_branches-default
@@ -0,0 +1,8 @@
+# master branches-default
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_branches-one b/t/t5515/fetch.master_branches-one
new file mode 100644
index 0000000..fe9bb0b
--- /dev/null
+++ b/t/t5515/fetch.master_branches-one
@@ -0,0 +1,8 @@
+# master branches-one
+8e32a6d901327a23ef831511badce7bf3bf46689 branch 'one' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_config-explicit b/t/t5515/fetch.master_config-explicit
new file mode 100644
index 0000000..4be97c7
--- /dev/null
+++ b/t/t5515/fetch.master_config-explicit
@@ -0,0 +1,11 @@
+# master config-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_config-glob b/t/t5515/fetch.master_config-glob
new file mode 100644
index 0000000..cb0726f
--- /dev/null
+++ b/t/t5515/fetch.master_config-glob
@@ -0,0 +1,11 @@
+# master config-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_remote-explicit b/t/t5515/fetch.master_remote-explicit
new file mode 100644
index 0000000..44a1ca8
--- /dev/null
+++ b/t/t5515/fetch.master_remote-explicit
@@ -0,0 +1,11 @@
+# master remote-explicit
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/fetch.master_remote-glob b/t/t5515/fetch.master_remote-glob
new file mode 100644
index 0000000..724e8db
--- /dev/null
+++ b/t/t5515/fetch.master_remote-glob
@@ -0,0 +1,11 @@
+# master remote-glob
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge branch 'master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge branch 'one' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge branch 'three' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge branch 'two' of ../
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f not-for-merge tag 'tag-master' of ../
+8e32a6d901327a23ef831511badce7bf3bf46689 not-for-merge tag 'tag-one' of ../
+22feea448b023a2d864ef94b013735af34d238ba not-for-merge tag 'tag-one-tree' of ../
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b not-for-merge tag 'tag-three' of ../
+0e3b14047d3ee365f4f2a1b673db059c3972589c not-for-merge tag 'tag-three-file' of ../
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 not-for-merge tag 'tag-two' of ../
diff --git a/t/t5515/refs.br-branches-default b/t/t5515/refs.br-branches-default
new file mode 100644
index 0000000..21917c1
--- /dev/null
+++ b/t/t5515/refs.br-branches-default
@@ -0,0 +1,12 @@
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/heads/branches-default
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-default-merge b/t/t5515/refs.br-branches-default-merge
new file mode 100644
index 0000000..21917c1
--- /dev/null
+++ b/t/t5515/refs.br-branches-default-merge
@@ -0,0 +1,12 @@
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/heads/branches-default
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-default-merge_branches-default b/t/t5515/refs.br-branches-default-merge_branches-default
new file mode 100644
index 0000000..21917c1
--- /dev/null
+++ b/t/t5515/refs.br-branches-default-merge_branches-default
@@ -0,0 +1,12 @@
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/heads/branches-default
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-default-octopus b/t/t5515/refs.br-branches-default-octopus
new file mode 100644
index 0000000..21917c1
--- /dev/null
+++ b/t/t5515/refs.br-branches-default-octopus
@@ -0,0 +1,12 @@
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/heads/branches-default
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-default-octopus_branches-default b/t/t5515/refs.br-branches-default-octopus_branches-default
new file mode 100644
index 0000000..21917c1
--- /dev/null
+++ b/t/t5515/refs.br-branches-default-octopus_branches-default
@@ -0,0 +1,12 @@
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/heads/branches-default
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-default_branches-default b/t/t5515/refs.br-branches-default_branches-default
new file mode 100644
index 0000000..21917c1
--- /dev/null
+++ b/t/t5515/refs.br-branches-default_branches-default
@@ -0,0 +1,12 @@
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/heads/branches-default
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-one b/t/t5515/refs.br-branches-one
new file mode 100644
index 0000000..8a705a5
--- /dev/null
+++ b/t/t5515/refs.br-branches-one
@@ -0,0 +1,12 @@
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/heads/branches-one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-one-merge b/t/t5515/refs.br-branches-one-merge
new file mode 100644
index 0000000..8a705a5
--- /dev/null
+++ b/t/t5515/refs.br-branches-one-merge
@@ -0,0 +1,12 @@
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/heads/branches-one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-one-merge_branches-one b/t/t5515/refs.br-branches-one-merge_branches-one
new file mode 100644
index 0000000..8a705a5
--- /dev/null
+++ b/t/t5515/refs.br-branches-one-merge_branches-one
@@ -0,0 +1,12 @@
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/heads/branches-one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-one-octopus b/t/t5515/refs.br-branches-one-octopus
new file mode 100644
index 0000000..8a705a5
--- /dev/null
+++ b/t/t5515/refs.br-branches-one-octopus
@@ -0,0 +1,12 @@
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/heads/branches-one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-one-octopus_branches-one b/t/t5515/refs.br-branches-one-octopus_branches-one
new file mode 100644
index 0000000..8a705a5
--- /dev/null
+++ b/t/t5515/refs.br-branches-one-octopus_branches-one
@@ -0,0 +1,12 @@
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/heads/branches-one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-branches-one_branches-one b/t/t5515/refs.br-branches-one_branches-one
new file mode 100644
index 0000000..8a705a5
--- /dev/null
+++ b/t/t5515/refs.br-branches-one_branches-one
@@ -0,0 +1,12 @@
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/heads/branches-one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-explicit b/t/t5515/refs.br-config-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-explicit-merge b/t/t5515/refs.br-config-explicit-merge
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-explicit-merge
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-explicit-merge_config-explicit b/t/t5515/refs.br-config-explicit-merge_config-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-explicit-merge_config-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-explicit-octopus b/t/t5515/refs.br-config-explicit-octopus
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-explicit-octopus
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-explicit-octopus_config-explicit b/t/t5515/refs.br-config-explicit-octopus_config-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-explicit-octopus_config-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-explicit_config-explicit b/t/t5515/refs.br-config-explicit_config-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-explicit_config-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-glob b/t/t5515/refs.br-config-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-glob-merge b/t/t5515/refs.br-config-glob-merge
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-glob-merge
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-glob-merge_config-glob b/t/t5515/refs.br-config-glob-merge_config-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-glob-merge_config-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-glob-octopus b/t/t5515/refs.br-config-glob-octopus
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-glob-octopus
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-glob-octopus_config-glob b/t/t5515/refs.br-config-glob-octopus_config-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-glob-octopus_config-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-config-glob_config-glob b/t/t5515/refs.br-config-glob_config-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-config-glob_config-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-explicit b/t/t5515/refs.br-remote-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-explicit-merge b/t/t5515/refs.br-remote-explicit-merge
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-explicit-merge
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-explicit-merge_remote-explicit b/t/t5515/refs.br-remote-explicit-merge_remote-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-explicit-merge_remote-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-explicit-octopus b/t/t5515/refs.br-remote-explicit-octopus
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-explicit-octopus
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-explicit-octopus_remote-explicit b/t/t5515/refs.br-remote-explicit-octopus_remote-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-explicit-octopus_remote-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-explicit_remote-explicit b/t/t5515/refs.br-remote-explicit_remote-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-explicit_remote-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-glob b/t/t5515/refs.br-remote-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-glob-merge b/t/t5515/refs.br-remote-glob-merge
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-glob-merge
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-glob-merge_remote-glob b/t/t5515/refs.br-remote-glob-merge_remote-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-glob-merge_remote-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-glob-octopus b/t/t5515/refs.br-remote-glob-octopus
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-glob-octopus
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-glob-octopus_remote-glob b/t/t5515/refs.br-remote-glob-octopus_remote-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-glob-octopus_remote-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-remote-glob_remote-glob b/t/t5515/refs.br-remote-glob_remote-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-remote-glob_remote-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig b/t/t5515/refs.br-unconfig
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.br-unconfig
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_--tags_.._.git b/t/t5515/refs.br-unconfig_--tags_.._.git
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_--tags_.._.git
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_.._.git b/t/t5515/refs.br-unconfig_.._.git
new file mode 100644
index 0000000..70962ea
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_.._.git
@@ -0,0 +1,5 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
diff --git a/t/t5515/refs.br-unconfig_.._.git_one b/t/t5515/refs.br-unconfig_.._.git_one
new file mode 100644
index 0000000..70962ea
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_.._.git_one
@@ -0,0 +1,5 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
diff --git a/t/t5515/refs.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file b/t/t5515/refs.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_.._.git_one_tag_tag-one_tag_tag-three-file
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_.._.git_one_two b/t/t5515/refs.br-unconfig_.._.git_one_two
new file mode 100644
index 0000000..70962ea
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_.._.git_one_two
@@ -0,0 +1,5 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
diff --git a/t/t5515/refs.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file b/t/t5515/refs.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_.._.git_tag_tag-one-tree_tag_tag-three-file
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_.._.git_tag_tag-one_tag_tag-three b/t/t5515/refs.br-unconfig_.._.git_tag_tag-one_tag_tag-three
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_.._.git_tag_tag-one_tag_tag-three
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_branches-default b/t/t5515/refs.br-unconfig_branches-default
new file mode 100644
index 0000000..21917c1
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_branches-default
@@ -0,0 +1,12 @@
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/heads/branches-default
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_branches-one b/t/t5515/refs.br-unconfig_branches-one
new file mode 100644
index 0000000..8a705a5
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_branches-one
@@ -0,0 +1,12 @@
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/heads/branches-one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_config-explicit b/t/t5515/refs.br-unconfig_config-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_config-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_config-glob b/t/t5515/refs.br-unconfig_config-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_config-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_remote-explicit b/t/t5515/refs.br-unconfig_remote-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_remote-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.br-unconfig_remote-glob b/t/t5515/refs.br-unconfig_remote-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.br-unconfig_remote-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master b/t/t5515/refs.master
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.master
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_--tags_.._.git b/t/t5515/refs.master_--tags_.._.git
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.master_--tags_.._.git
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_.._.git b/t/t5515/refs.master_.._.git
new file mode 100644
index 0000000..70962ea
--- /dev/null
+++ b/t/t5515/refs.master_.._.git
@@ -0,0 +1,5 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
diff --git a/t/t5515/refs.master_.._.git_one b/t/t5515/refs.master_.._.git_one
new file mode 100644
index 0000000..70962ea
--- /dev/null
+++ b/t/t5515/refs.master_.._.git_one
@@ -0,0 +1,5 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
diff --git a/t/t5515/refs.master_.._.git_one_tag_tag-one_tag_tag-three-file b/t/t5515/refs.master_.._.git_one_tag_tag-one_tag_tag-three-file
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.master_.._.git_one_tag_tag-one_tag_tag-three-file
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_.._.git_one_two b/t/t5515/refs.master_.._.git_one_two
new file mode 100644
index 0000000..70962ea
--- /dev/null
+++ b/t/t5515/refs.master_.._.git_one_two
@@ -0,0 +1,5 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
diff --git a/t/t5515/refs.master_.._.git_tag_tag-one-tree_tag_tag-three-file b/t/t5515/refs.master_.._.git_tag_tag-one-tree_tag_tag-three-file
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.master_.._.git_tag_tag-one-tree_tag_tag-three-file
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_.._.git_tag_tag-one_tag_tag-three b/t/t5515/refs.master_.._.git_tag_tag-one_tag_tag-three
new file mode 100644
index 0000000..13e4ad2
--- /dev/null
+++ b/t/t5515/refs.master_.._.git_tag_tag-one_tag_tag-three
@@ -0,0 +1,11 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_branches-default b/t/t5515/refs.master_branches-default
new file mode 100644
index 0000000..21917c1
--- /dev/null
+++ b/t/t5515/refs.master_branches-default
@@ -0,0 +1,12 @@
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/heads/branches-default
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_branches-one b/t/t5515/refs.master_branches-one
new file mode 100644
index 0000000..8a705a5
--- /dev/null
+++ b/t/t5515/refs.master_branches-one
@@ -0,0 +1,12 @@
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/heads/branches-one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_config-explicit b/t/t5515/refs.master_config-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.master_config-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_config-glob b/t/t5515/refs.master_config-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.master_config-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_remote-explicit b/t/t5515/refs.master_remote-explicit
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.master_remote-explicit
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5515/refs.master_remote-glob b/t/t5515/refs.master_remote-glob
new file mode 100644
index 0000000..9bbbfd9
--- /dev/null
+++ b/t/t5515/refs.master_remote-glob
@@ -0,0 +1,15 @@
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/HEAD
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/origin/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/origin/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/origin/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/origin/two
+754b754407bf032e9a2f9d5a9ad05ca79a6b228f refs/remotes/rem/master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/remotes/rem/one
+0567da4d5edd2ff4bb292a465ba9e64dcad9536b refs/remotes/rem/three
+6134ee8f857693b96ff1cc98d3e2fd62b199e5a8 refs/remotes/rem/two
+6c9dec2b923228c9ff994c6cfe4ae16c12408dc5 refs/tags/tag-master
+8e32a6d901327a23ef831511badce7bf3bf46689 refs/tags/tag-one
+22feea448b023a2d864ef94b013735af34d238ba refs/tags/tag-one-tree
+c61a82b60967180544e3c19f819ddbd0c9f89899 refs/tags/tag-three
+0e3b14047d3ee365f4f2a1b673db059c3972589c refs/tags/tag-three-file
+525b7fb068d59950d185a8779dc957c77eed73ba refs/tags/tag-two
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
new file mode 100755
index 0000000..b11da79
--- /dev/null
+++ b/t/t5516-fetch-push.sh
@@ -0,0 +1,785 @@
+#!/bin/sh
+
+test_description='fetching and pushing, with or without wildcard'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+mk_empty () {
+ rm -fr testrepo &&
+ mkdir testrepo &&
+ (
+ cd testrepo &&
+ git init &&
+ git config receive.denyCurrentBranch warn &&
+ mv .git/hooks .git/hooks-disabled
+ )
+}
+
+mk_test () {
+ mk_empty &&
+ (
+ for ref in "$@"
+ do
+ git push testrepo $the_first_commit:refs/$ref || {
+ echo "Oops, push refs/$ref failure"
+ exit 1
+ }
+ done &&
+ cd testrepo &&
+ for ref in "$@"
+ do
+ r=$(git show-ref -s --verify refs/$ref) &&
+ test "z$r" = "z$the_first_commit" || {
+ echo "Oops, refs/$ref is wrong"
+ exit 1
+ }
+ done &&
+ git fsck --full
+ )
+}
+
+mk_child() {
+ rm -rf "$1" &&
+ git clone testrepo "$1"
+}
+
+check_push_result () {
+ (
+ cd testrepo &&
+ it="$1" &&
+ shift
+ for ref in "$@"
+ do
+ r=$(git show-ref -s --verify refs/$ref) &&
+ test "z$r" = "z$it" || {
+ echo "Oops, refs/$ref is wrong"
+ exit 1
+ }
+ done &&
+ git fsck --full
+ )
+}
+
+test_expect_success setup '
+
+ >path1 &&
+ git add path1 &&
+ test_tick &&
+ git commit -a -m repo &&
+ the_first_commit=$(git show-ref -s --verify refs/heads/master) &&
+
+ >path2 &&
+ git add path2 &&
+ test_tick &&
+ git commit -a -m second &&
+ the_commit=$(git show-ref -s --verify refs/heads/master)
+
+'
+
+test_expect_success 'fetch without wildcard' '
+ mk_empty &&
+ (
+ cd testrepo &&
+ git fetch .. refs/heads/master:refs/remotes/origin/master &&
+
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'fetch with wildcard' '
+ mk_empty &&
+ (
+ cd testrepo &&
+ git config remote.up.url .. &&
+ git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+ git fetch up &&
+
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'fetch with insteadOf' '
+ mk_empty &&
+ (
+ TRASH=$(pwd)/ &&
+ cd testrepo &&
+ git config "url.$TRASH.insteadOf" trash/ &&
+ git config remote.up.url trash/. &&
+ git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+ git fetch up &&
+
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
+ mk_empty &&
+ (
+ TRASH=$(pwd)/ &&
+ cd testrepo &&
+ git config "url.trash/.pushInsteadOf" "$TRASH" &&
+ git config remote.up.url "$TRASH." &&
+ git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+ git fetch up &&
+
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push without wildcard' '
+ mk_empty &&
+
+ git push testrepo refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push with wildcard' '
+ mk_empty &&
+
+ git push testrepo "refs/heads/*:refs/remotes/origin/*" &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push with insteadOf' '
+ mk_empty &&
+ TRASH="$(pwd)/" &&
+ git config "url.$TRASH.insteadOf" trash/ &&
+ git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push with pushInsteadOf' '
+ mk_empty &&
+ TRASH="$(pwd)/" &&
+ git config "url.$TRASH.pushInsteadOf" trash/ &&
+ git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
+ mk_empty &&
+ TRASH="$(pwd)/" &&
+ git config "url.trash2/.pushInsteadOf" trash/ &&
+ git config remote.r.url trash/wrong &&
+ git config remote.r.pushurl "$TRASH/testrepo" &&
+ git push r refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push with matching heads' '
+
+ mk_test heads/master &&
+ git push testrepo &&
+ check_push_result $the_commit heads/master
+
+'
+
+test_expect_success 'push with matching heads on the command line' '
+
+ mk_test heads/master &&
+ git push testrepo : &&
+ check_push_result $the_commit heads/master
+
+'
+
+test_expect_success 'failed (non-fast-forward) push with matching heads' '
+
+ mk_test heads/master &&
+ git push testrepo : &&
+ git commit --amend -massaged &&
+ test_must_fail git push testrepo &&
+ check_push_result $the_commit heads/master &&
+ git reset --hard $the_commit
+
+'
+
+test_expect_success 'push --force with matching heads' '
+
+ mk_test heads/master &&
+ git push testrepo : &&
+ git commit --amend -massaged &&
+ git push --force testrepo &&
+ ! check_push_result $the_commit heads/master &&
+ git reset --hard $the_commit
+
+'
+
+test_expect_success 'push with matching heads and forced update' '
+
+ mk_test heads/master &&
+ git push testrepo : &&
+ git commit --amend -massaged &&
+ git push testrepo +: &&
+ ! check_push_result $the_commit heads/master &&
+ git reset --hard $the_commit
+
+'
+
+test_expect_success 'push with no ambiguity (1)' '
+
+ mk_test heads/master &&
+ git push testrepo master:master &&
+ check_push_result $the_commit heads/master
+
+'
+
+test_expect_success 'push with no ambiguity (2)' '
+
+ mk_test remotes/origin/master &&
+ git push testrepo master:origin/master &&
+ check_push_result $the_commit remotes/origin/master
+
+'
+
+test_expect_success 'push with colon-less refspec, no ambiguity' '
+
+ mk_test heads/master heads/t/master &&
+ git branch -f t/master master &&
+ git push testrepo master &&
+ check_push_result $the_commit heads/master &&
+ check_push_result $the_first_commit heads/t/master
+
+'
+
+test_expect_success 'push with weak ambiguity (1)' '
+
+ mk_test heads/master remotes/origin/master &&
+ git push testrepo master:master &&
+ check_push_result $the_commit heads/master &&
+ check_push_result $the_first_commit remotes/origin/master
+
+'
+
+test_expect_success 'push with weak ambiguity (2)' '
+
+ mk_test heads/master remotes/origin/master remotes/another/master &&
+ git push testrepo master:master &&
+ check_push_result $the_commit heads/master &&
+ check_push_result $the_first_commit remotes/origin/master remotes/another/master
+
+'
+
+test_expect_success 'push with ambiguity' '
+
+ mk_test heads/frotz tags/frotz &&
+ if git push testrepo master:frotz
+ then
+ echo "Oops, should have failed"
+ false
+ else
+ check_push_result $the_first_commit heads/frotz tags/frotz
+ fi
+
+'
+
+test_expect_success 'push with colon-less refspec (1)' '
+
+ mk_test heads/frotz tags/frotz &&
+ git branch -f frotz master &&
+ git push testrepo frotz &&
+ check_push_result $the_commit heads/frotz &&
+ check_push_result $the_first_commit tags/frotz
+
+'
+
+test_expect_success 'push with colon-less refspec (2)' '
+
+ mk_test heads/frotz tags/frotz &&
+ if git show-ref --verify -q refs/heads/frotz
+ then
+ git branch -D frotz
+ fi &&
+ git tag -f frotz &&
+ git push testrepo frotz &&
+ check_push_result $the_commit tags/frotz &&
+ check_push_result $the_first_commit heads/frotz
+
+'
+
+test_expect_success 'push with colon-less refspec (3)' '
+
+ mk_test &&
+ if git show-ref --verify -q refs/tags/frotz
+ then
+ git tag -d frotz
+ fi &&
+ git branch -f frotz master &&
+ git push testrepo frotz &&
+ check_push_result $the_commit heads/frotz &&
+ test 1 = $( cd testrepo && git show-ref | wc -l )
+'
+
+test_expect_success 'push with colon-less refspec (4)' '
+
+ mk_test &&
+ if git show-ref --verify -q refs/heads/frotz
+ then
+ git branch -D frotz
+ fi &&
+ git tag -f frotz &&
+ git push testrepo frotz &&
+ check_push_result $the_commit tags/frotz &&
+ test 1 = $( cd testrepo && git show-ref | wc -l )
+
+'
+
+test_expect_success 'push head with non-existant, incomplete dest' '
+
+ mk_test &&
+ git push testrepo master:branch &&
+ check_push_result $the_commit heads/branch
+
+'
+
+test_expect_success 'push tag with non-existant, incomplete dest' '
+
+ mk_test &&
+ git tag -f v1.0 &&
+ git push testrepo v1.0:tag &&
+ check_push_result $the_commit tags/tag
+
+'
+
+test_expect_success 'push sha1 with non-existant, incomplete dest' '
+
+ mk_test &&
+ test_must_fail git push testrepo `git rev-parse master`:foo
+
+'
+
+test_expect_success 'push ref expression with non-existant, incomplete dest' '
+
+ mk_test &&
+ test_must_fail git push testrepo master^:branch
+
+'
+
+test_expect_success 'push with HEAD' '
+
+ mk_test heads/master &&
+ git checkout master &&
+ git push testrepo HEAD &&
+ check_push_result $the_commit heads/master
+
+'
+
+test_expect_success 'push with HEAD nonexisting at remote' '
+
+ mk_test heads/master &&
+ git checkout -b local master &&
+ git push testrepo HEAD &&
+ check_push_result $the_commit heads/local
+'
+
+test_expect_success 'push with +HEAD' '
+
+ mk_test heads/master &&
+ git checkout master &&
+ git branch -D local &&
+ git checkout -b local &&
+ git push testrepo master local &&
+ check_push_result $the_commit heads/master &&
+ check_push_result $the_commit heads/local &&
+
+ # Without force rewinding should fail
+ git reset --hard HEAD^ &&
+ test_must_fail git push testrepo HEAD &&
+ check_push_result $the_commit heads/local &&
+
+ # With force rewinding should succeed
+ git push testrepo +HEAD &&
+ check_push_result $the_first_commit heads/local
+
+'
+
+test_expect_success 'push HEAD with non-existant, incomplete dest' '
+
+ mk_test &&
+ git checkout master &&
+ git push testrepo HEAD:branch &&
+ check_push_result $the_commit heads/branch
+
+'
+
+test_expect_success 'push with config remote.*.push = HEAD' '
+
+ mk_test heads/local &&
+ git checkout master &&
+ git branch -f local $the_commit &&
+ (
+ cd testrepo &&
+ git checkout local &&
+ git reset --hard $the_first_commit
+ ) &&
+ git config remote.there.url testrepo &&
+ git config remote.there.push HEAD &&
+ git config branch.master.remote there &&
+ git push &&
+ check_push_result $the_commit heads/master &&
+ check_push_result $the_first_commit heads/local
+'
+
+# clean up the cruft left with the previous one
+git config --remove-section remote.there
+git config --remove-section branch.master
+
+test_expect_success 'push with config remote.*.pushurl' '
+
+ mk_test heads/master &&
+ git checkout master &&
+ git config remote.there.url test2repo &&
+ git config remote.there.pushurl testrepo &&
+ git push there &&
+ check_push_result $the_commit heads/master
+'
+
+# clean up the cruft left with the previous one
+git config --remove-section remote.there
+
+test_expect_success 'push with dry-run' '
+
+ mk_test heads/master &&
+ (
+ cd testrepo &&
+ old_commit=$(git show-ref -s --verify refs/heads/master)
+ ) &&
+ git push --dry-run testrepo &&
+ check_push_result $old_commit heads/master
+'
+
+test_expect_success 'push updates local refs' '
+
+ mk_test heads/master &&
+ mk_child child &&
+ (
+ cd child &&
+ git pull .. master &&
+ git push &&
+ test $(git rev-parse master) = \
+ $(git rev-parse remotes/origin/master)
+ )
+
+'
+
+test_expect_success 'push updates up-to-date local refs' '
+
+ mk_test heads/master &&
+ mk_child child1 &&
+ mk_child child2 &&
+ (cd child1 && git pull .. master && git push) &&
+ (
+ cd child2 &&
+ git pull ../child1 master &&
+ git push &&
+ test $(git rev-parse master) = \
+ $(git rev-parse remotes/origin/master)
+ )
+
+'
+
+test_expect_success 'push preserves up-to-date packed refs' '
+
+ mk_test heads/master &&
+ mk_child child &&
+ (
+ cd child &&
+ git push &&
+ ! test -f .git/refs/remotes/origin/master
+ )
+
+'
+
+test_expect_success 'push does not update local refs on failure' '
+
+ mk_test heads/master &&
+ mk_child child &&
+ mkdir testrepo/.git/hooks &&
+ echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive &&
+ chmod +x testrepo/.git/hooks/pre-receive &&
+ (
+ cd child &&
+ git pull .. master
+ test_must_fail git push &&
+ test $(git rev-parse master) != \
+ $(git rev-parse remotes/origin/master)
+ )
+
+'
+
+test_expect_success 'allow deleting an invalid remote ref' '
+
+ mk_test heads/master &&
+ rm -f testrepo/.git/objects/??/* &&
+ git push testrepo :refs/heads/master &&
+ (cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
+
+'
+
+test_expect_success 'allow deleting a ref using --delete' '
+ mk_test heads/master &&
+ (cd testrepo && git config receive.denyDeleteCurrent warn) &&
+ git push testrepo --delete master &&
+ (cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
+'
+
+test_expect_success 'allow deleting a tag using --delete' '
+ mk_test heads/master &&
+ git tag -a -m dummy_message deltag heads/master &&
+ git push testrepo --tags &&
+ (cd testrepo && git rev-parse --verify -q refs/tags/deltag) &&
+ git push testrepo --delete tag deltag &&
+ (cd testrepo && test_must_fail git rev-parse --verify refs/tags/deltag)
+'
+
+test_expect_success 'push --delete without args aborts' '
+ mk_test heads/master &&
+ test_must_fail git push testrepo --delete
+'
+
+test_expect_success 'push --delete refuses src:dest refspecs' '
+ mk_test heads/master &&
+ test_must_fail git push testrepo --delete master:foo
+'
+
+test_expect_success 'warn on push to HEAD of non-bare repository' '
+ mk_test heads/master
+ (
+ cd testrepo &&
+ git checkout master &&
+ git config receive.denyCurrentBranch warn
+ ) &&
+ git push testrepo master 2>stderr &&
+ grep "warning: updating the current branch" stderr
+'
+
+test_expect_success 'deny push to HEAD of non-bare repository' '
+ mk_test heads/master
+ (
+ cd testrepo &&
+ git checkout master &&
+ git config receive.denyCurrentBranch true
+ ) &&
+ test_must_fail git push testrepo master
+'
+
+test_expect_success 'allow push to HEAD of bare repository (bare)' '
+ mk_test heads/master
+ (
+ cd testrepo &&
+ git checkout master &&
+ git config receive.denyCurrentBranch true &&
+ git config core.bare true
+ ) &&
+ git push testrepo master 2>stderr &&
+ ! grep "warning: updating the current branch" stderr
+'
+
+test_expect_success 'allow push to HEAD of non-bare repository (config)' '
+ mk_test heads/master
+ (
+ cd testrepo &&
+ git checkout master &&
+ git config receive.denyCurrentBranch false
+ ) &&
+ git push testrepo master 2>stderr &&
+ ! grep "warning: updating the current branch" stderr
+'
+
+test_expect_success 'fetch with branches' '
+ mk_empty &&
+ git branch second $the_first_commit &&
+ git checkout second &&
+ echo ".." > testrepo/.git/branches/branch1 &&
+ (
+ cd testrepo &&
+ git fetch branch1 &&
+ r=$(git show-ref -s --verify refs/heads/branch1) &&
+ test "z$r" = "z$the_commit" &&
+ test 1 = $(git for-each-ref refs/heads | wc -l)
+ ) &&
+ git checkout master
+'
+
+test_expect_success 'fetch with branches containing #' '
+ mk_empty &&
+ echo "..#second" > testrepo/.git/branches/branch2 &&
+ (
+ cd testrepo &&
+ git fetch branch2 &&
+ r=$(git show-ref -s --verify refs/heads/branch2) &&
+ test "z$r" = "z$the_first_commit" &&
+ test 1 = $(git for-each-ref refs/heads | wc -l)
+ ) &&
+ git checkout master
+'
+
+test_expect_success 'push with branches' '
+ mk_empty &&
+ git checkout second &&
+ echo "testrepo" > .git/branches/branch1 &&
+ git push branch1 &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$r" = "z$the_first_commit" &&
+ test 1 = $(git for-each-ref refs/heads | wc -l)
+ )
+'
+
+test_expect_success 'push with branches containing #' '
+ mk_empty &&
+ echo "testrepo#branch3" > .git/branches/branch2 &&
+ git push branch2 &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/heads/branch3) &&
+ test "z$r" = "z$the_first_commit" &&
+ test 1 = $(git for-each-ref refs/heads | wc -l)
+ ) &&
+ git checkout master
+'
+
+test_expect_success 'push into aliased refs (consistent)' '
+ mk_test heads/master &&
+ mk_child child1 &&
+ mk_child child2 &&
+ (
+ cd child1 &&
+ git branch foo &&
+ git symbolic-ref refs/heads/bar refs/heads/foo
+ git config receive.denyCurrentBranch false
+ ) &&
+ (
+ cd child2 &&
+ >path2 &&
+ git add path2 &&
+ test_tick &&
+ git commit -a -m child2 &&
+ git branch foo &&
+ git branch bar &&
+ git push ../child1 foo bar
+ )
+'
+
+test_expect_success 'push into aliased refs (inconsistent)' '
+ mk_test heads/master &&
+ mk_child child1 &&
+ mk_child child2 &&
+ (
+ cd child1 &&
+ git branch foo &&
+ git symbolic-ref refs/heads/bar refs/heads/foo
+ git config receive.denyCurrentBranch false
+ ) &&
+ (
+ cd child2 &&
+ >path2 &&
+ git add path2 &&
+ test_tick &&
+ git commit -a -m child2 &&
+ git branch foo &&
+ >path3 &&
+ git add path3 &&
+ test_tick &&
+ git commit -a -m child2 &&
+ git branch bar &&
+ test_must_fail git push ../child1 foo bar 2>stderr &&
+ grep "refusing inconsistent update" stderr
+ )
+'
+
+test_expect_success 'push --porcelain' '
+ mk_empty &&
+ echo >.git/foo "To testrepo" &&
+ echo >>.git/foo "* refs/heads/master:refs/remotes/origin/master [new branch]" &&
+ echo >>.git/foo "Done" &&
+ git push >.git/bar --porcelain testrepo refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ ) &&
+ test_cmp .git/foo .git/bar
+'
+
+test_expect_success 'push --porcelain bad url' '
+ mk_empty &&
+ test_must_fail git push >.git/bar --porcelain asdfasdfasd refs/heads/master:refs/remotes/origin/master &&
+ test_must_fail grep -q Done .git/bar
+'
+
+test_expect_success 'push --porcelain rejected' '
+ mk_empty &&
+ git push testrepo refs/heads/master:refs/remotes/origin/master &&
+ (cd testrepo &&
+ git reset --hard origin/master^
+ git config receive.denyCurrentBranch true) &&
+
+ echo >.git/foo "To testrepo" &&
+ echo >>.git/foo "! refs/heads/master:refs/heads/master [remote rejected] (branch is currently checked out)" &&
+
+ test_must_fail git push >.git/bar --porcelain testrepo refs/heads/master:refs/heads/master &&
+ test_cmp .git/foo .git/bar
+'
+
+test_expect_success 'push --porcelain --dry-run rejected' '
+ mk_empty &&
+ git push testrepo refs/heads/master:refs/remotes/origin/master &&
+ (cd testrepo &&
+ git reset --hard origin/master
+ git config receive.denyCurrentBranch true) &&
+
+ echo >.git/foo "To testrepo" &&
+ echo >>.git/foo "! refs/heads/master^:refs/heads/master [rejected] (non-fast-forward)" &&
+ echo >>.git/foo "Done" &&
+
+ test_must_fail git push >.git/bar --porcelain --dry-run testrepo refs/heads/master^:refs/heads/master &&
+ test_cmp .git/foo .git/bar
+'
+
+test_done
diff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh
new file mode 100755
index 0000000..e2ad260
--- /dev/null
+++ b/t/t5517-push-mirror.sh
@@ -0,0 +1,268 @@
+#!/bin/sh
+
+test_description='pushing to a mirror repository'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+invert () {
+ if "$@"; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+mk_repo_pair () {
+ rm -rf master mirror &&
+ mkdir mirror &&
+ (
+ cd mirror &&
+ git init &&
+ git config receive.denyCurrentBranch warn
+ ) &&
+ mkdir master &&
+ (
+ cd master &&
+ git init &&
+ git remote add $1 up ../mirror
+ )
+}
+
+
+# BRANCH tests
+test_expect_success 'push mirror creates new branches' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git push --mirror up
+ ) &&
+ master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+ mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+ test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror updates existing branches' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git push --mirror up &&
+ echo two >foo && git add foo && git commit -m two &&
+ git push --mirror up
+ ) &&
+ master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+ mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+ test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror force updates existing branches' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git push --mirror up &&
+ echo two >foo && git add foo && git commit -m two &&
+ git push --mirror up &&
+ git reset --hard HEAD^
+ git push --mirror up
+ ) &&
+ master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+ mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+ test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror removes branches' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git branch remove master &&
+ git push --mirror up &&
+ git branch -D remove
+ git push --mirror up
+ ) &&
+ (
+ cd mirror &&
+ invert git show-ref -s --verify refs/heads/remove
+ )
+
+'
+
+test_expect_success 'push mirror adds, updates and removes branches together' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git branch remove master &&
+ git push --mirror up &&
+ git branch -D remove &&
+ git branch add master &&
+ echo two >foo && git add foo && git commit -m two &&
+ git push --mirror up
+ ) &&
+ master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
+ master_add=$(cd master && git show-ref -s --verify refs/heads/add) &&
+ mirror_master=$(cd mirror && git show-ref -s --verify refs/heads/master) &&
+ mirror_add=$(cd mirror && git show-ref -s --verify refs/heads/add) &&
+ test "$master_master" = "$mirror_master" &&
+ test "$master_add" = "$mirror_add" &&
+ (
+ cd mirror &&
+ invert git show-ref -s --verify refs/heads/remove
+ )
+
+'
+
+
+# TAG tests
+test_expect_success 'push mirror creates new tags' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git tag -f tmaster master &&
+ git push --mirror up
+ ) &&
+ master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+ mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+ test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror updates existing tags' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git tag -f tmaster master &&
+ git push --mirror up &&
+ echo two >foo && git add foo && git commit -m two &&
+ git tag -f tmaster master &&
+ git push --mirror up
+ ) &&
+ master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+ mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+ test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror force updates existing tags' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git tag -f tmaster master &&
+ git push --mirror up &&
+ echo two >foo && git add foo && git commit -m two &&
+ git tag -f tmaster master &&
+ git push --mirror up &&
+ git reset --hard HEAD^
+ git tag -f tmaster master &&
+ git push --mirror up
+ ) &&
+ master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+ mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+ test "$master_master" = "$mirror_master"
+
+'
+
+test_expect_success 'push mirror removes tags' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git tag -f tremove master &&
+ git push --mirror up &&
+ git tag -d tremove
+ git push --mirror up
+ ) &&
+ (
+ cd mirror &&
+ invert git show-ref -s --verify refs/tags/tremove
+ )
+
+'
+
+test_expect_success 'push mirror adds, updates and removes tags together' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git tag -f tmaster master &&
+ git tag -f tremove master &&
+ git push --mirror up &&
+ git tag -d tremove &&
+ git tag tadd master &&
+ echo two >foo && git add foo && git commit -m two &&
+ git tag -f tmaster master &&
+ git push --mirror up
+ ) &&
+ master_master=$(cd master && git show-ref -s --verify refs/tags/tmaster) &&
+ master_add=$(cd master && git show-ref -s --verify refs/tags/tadd) &&
+ mirror_master=$(cd mirror && git show-ref -s --verify refs/tags/tmaster) &&
+ mirror_add=$(cd mirror && git show-ref -s --verify refs/tags/tadd) &&
+ test "$master_master" = "$mirror_master" &&
+ test "$master_add" = "$mirror_add" &&
+ (
+ cd mirror &&
+ invert git show-ref -s --verify refs/tags/tremove
+ )
+
+'
+
+test_expect_success 'remote.foo.mirror adds and removes branches' '
+
+ mk_repo_pair --mirror &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git branch keep master &&
+ git branch remove master &&
+ git push up &&
+ git branch -D remove
+ git push up
+ ) &&
+ (
+ cd mirror &&
+ git show-ref -s --verify refs/heads/keep &&
+ invert git show-ref -s --verify refs/heads/remove
+ )
+
+'
+
+test_expect_success 'remote.foo.mirror=no has no effect' '
+
+ mk_repo_pair &&
+ (
+ cd master &&
+ echo one >foo && git add foo && git commit -m one &&
+ git config --add remote.up.mirror no &&
+ git branch keep master &&
+ git push --mirror up &&
+ git branch -D keep &&
+ git push up
+ ) &&
+ (
+ cd mirror &&
+ git show-ref -s --verify refs/heads/keep
+ )
+
+'
+
+test_done
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
new file mode 100755
index 0000000..c2060bb
--- /dev/null
+++ b/t/t5518-fetch-exit-status.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Dmitry V. Levin
+#
+
+test_description='fetch exit status test'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >file &&
+ git add file &&
+ git commit -m initial &&
+
+ git checkout -b side &&
+ echo side >file &&
+ git commit -a -m side &&
+
+ git checkout master &&
+ echo next >file &&
+ git commit -a -m next
+'
+
+test_expect_success 'non-fast-forward fetch' '
+
+ test_must_fail git fetch . master:side
+
+'
+
+test_expect_success 'forced update' '
+
+ git fetch . +master:side
+
+'
+
+test_done
diff --git a/t/t5519-push-alternates.sh b/t/t5519-push-alternates.sh
new file mode 100755
index 0000000..96be523
--- /dev/null
+++ b/t/t5519-push-alternates.sh
@@ -0,0 +1,143 @@
+#!/bin/sh
+
+test_description='push to a repository that borrows from elsewhere'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir alice-pub &&
+ (
+ cd alice-pub &&
+ GIT_DIR=. git init
+ ) &&
+ mkdir alice-work &&
+ (
+ cd alice-work &&
+ git init &&
+ >file &&
+ git add . &&
+ git commit -m initial &&
+ git push ../alice-pub master
+ ) &&
+
+ # Project Bob is a fork of project Alice
+ mkdir bob-pub &&
+ (
+ cd bob-pub &&
+ GIT_DIR=. git init &&
+ mkdir -p objects/info &&
+ echo ../../alice-pub/objects >objects/info/alternates
+ ) &&
+ git clone alice-pub bob-work &&
+ (
+ cd bob-work &&
+ git push ../bob-pub master
+ )
+'
+
+test_expect_success 'alice works and pushes' '
+ (
+ cd alice-work &&
+ echo more >file &&
+ git commit -a -m second &&
+ git push ../alice-pub
+ )
+'
+
+test_expect_success 'bob fetches from alice, works and pushes' '
+ (
+ # Bob acquires what Alice did in his work tree first.
+ # Even though these objects are not directly in
+ # the public repository of Bob, this push does not
+ # need to send the commit Bob received from Alice
+ # to his public repository, as all the object Alice
+ # has at her public repository are available to it
+ # via its alternates.
+ cd bob-work &&
+ git pull ../alice-pub master &&
+ echo more bob >file &&
+ git commit -a -m third &&
+ git push ../bob-pub
+ ) &&
+
+ # Check that the second commit by Alice is not sent
+ # to ../bob-pub
+ (
+ cd bob-pub &&
+ second=$(git rev-parse HEAD^) &&
+ rm -f objects/info/alternates &&
+ test_must_fail git cat-file -t $second &&
+ echo ../../alice-pub/objects >objects/info/alternates
+ )
+'
+
+test_expect_success 'clean-up in case the previous failed' '
+ (
+ cd bob-pub &&
+ echo ../../alice-pub/objects >objects/info/alternates
+ )
+'
+
+test_expect_success 'alice works and pushes again' '
+ (
+ # Alice does not care what Bob does. She does not
+ # even have to be aware of his existence. She just
+ # keeps working and pushing
+ cd alice-work &&
+ echo more alice >file &&
+ git commit -a -m fourth &&
+ git push ../alice-pub
+ )
+'
+
+test_expect_success 'bob works and pushes' '
+ (
+ # This time Bob does not pull from Alice, and
+ # the master branch at her public repository points
+ # at a commit Bob does not know about. This should
+ # not prevent the push by Bob from succeeding.
+ cd bob-work &&
+ echo yet more bob >file &&
+ git commit -a -m fifth &&
+ git push ../bob-pub
+ )
+'
+
+test_expect_success 'alice works and pushes yet again' '
+ (
+ # Alice does not care what Bob does. She does not
+ # even have to be aware of his existence. She just
+ # keeps working and pushing
+ cd alice-work &&
+ echo more and more alice >file &&
+ git commit -a -m sixth.1 &&
+ echo more and more alice >>file &&
+ git commit -a -m sixth.2 &&
+ echo more and more alice >>file &&
+ git commit -a -m sixth.3 &&
+ git push ../alice-pub
+ )
+'
+
+test_expect_success 'bob works and pushes again' '
+ (
+ cd alice-pub &&
+ git cat-file commit master >../bob-work/commit
+ )
+ (
+ # This time Bob does not pull from Alice, and
+ # the master branch at her public repository points
+ # at a commit Bob does not fully know about, but
+ # he happens to have the commit object (but not the
+ # necessary tree) in his repository from Alice.
+ # This should not prevent the push by Bob from
+ # succeeding.
+ cd bob-work &&
+ git hash-object -t commit -w commit &&
+ echo even more bob >file &&
+ git commit -a -m seventh &&
+ git push ../bob-pub
+ )
+'
+
+test_done
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
new file mode 100755
index 0000000..319e389
--- /dev/null
+++ b/t/t5520-pull.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+
+test_description='pulling into void'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+test_expect_success setup '
+
+ echo file >file &&
+ git add file &&
+ git commit -a -m original
+
+'
+
+test_expect_success 'pulling into void' '
+ mkdir cloned &&
+ cd cloned &&
+ git init &&
+ git pull ..
+'
+
+cd "$D"
+
+test_expect_success 'checking the results' '
+ test -f file &&
+ test -f cloned/file &&
+ test_cmp file cloned/file
+'
+
+test_expect_success 'pulling into void using master:master' '
+ mkdir cloned-uho &&
+ (
+ cd cloned-uho &&
+ git init &&
+ git pull .. master:master
+ ) &&
+ test -f file &&
+ test -f cloned-uho/file &&
+ test_cmp file cloned-uho/file
+'
+
+test_expect_success 'test . as a remote' '
+
+ git branch copy master &&
+ git config branch.copy.remote . &&
+ git config branch.copy.merge refs/heads/master &&
+ echo updated >file &&
+ git commit -a -m updated &&
+ git checkout copy &&
+ test `cat file` = file &&
+ git pull &&
+ test `cat file` = updated
+'
+
+test_expect_success 'the default remote . should not break explicit pull' '
+ git checkout -b second master^ &&
+ echo modified >file &&
+ git commit -a -m modified &&
+ git checkout copy &&
+ git reset --hard HEAD^ &&
+ test `cat file` = file &&
+ git pull . second &&
+ test `cat file` = modified
+'
+
+test_expect_success '--rebase' '
+ git branch to-rebase &&
+ echo modified again > file &&
+ git commit -m file file &&
+ git checkout to-rebase &&
+ echo new > file2 &&
+ git add file2 &&
+ git commit -m "new file" &&
+ git tag before-rebase &&
+ git pull --rebase . copy &&
+ test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
+ test new = $(git show HEAD:file2)
+'
+
+test_expect_success 'branch.to-rebase.rebase' '
+ git reset --hard before-rebase &&
+ git config branch.to-rebase.rebase 1 &&
+ git pull . copy &&
+ git config branch.to-rebase.rebase 0 &&
+ test $(git rev-parse HEAD^) = $(git rev-parse copy) &&
+ test new = $(git show HEAD:file2)
+'
+
+test_expect_success '--rebase with rebased upstream' '
+
+ git remote add -f me . &&
+ git checkout copy &&
+ git tag copy-orig &&
+ git reset --hard HEAD^ &&
+ echo conflicting modification > file &&
+ git commit -m conflict file &&
+ git checkout to-rebase &&
+ echo file > file2 &&
+ git commit -m to-rebase file2 &&
+ git tag to-rebase-orig &&
+ git pull --rebase me copy &&
+ test "conflicting modification" = "$(cat file)" &&
+ test file = $(cat file2)
+
+'
+
+test_expect_success '--rebase with rebased default upstream' '
+
+ git update-ref refs/remotes/me/copy copy-orig &&
+ git checkout --track -b to-rebase2 me/copy &&
+ git reset --hard to-rebase-orig &&
+ git pull --rebase &&
+ test "conflicting modification" = "$(cat file)" &&
+ test file = $(cat file2)
+
+'
+
+test_expect_success 'rebased upstream + fetch + pull --rebase' '
+
+ git update-ref refs/remotes/me/copy copy-orig &&
+ git reset --hard to-rebase-orig &&
+ git checkout --track -b to-rebase3 me/copy &&
+ git reset --hard to-rebase-orig &&
+ git fetch &&
+ git pull --rebase &&
+ test "conflicting modification" = "$(cat file)" &&
+ test file = "$(cat file2)"
+
+'
+
+test_expect_success 'pull --rebase dies early with dirty working directory' '
+
+ git checkout to-rebase &&
+ git update-ref refs/remotes/me/copy copy^ &&
+ COPY=$(git rev-parse --verify me/copy) &&
+ git rebase --onto $COPY copy &&
+ git config branch.to-rebase.remote me &&
+ git config branch.to-rebase.merge refs/heads/copy &&
+ git config branch.to-rebase.rebase true &&
+ echo dirty >> file &&
+ git add file &&
+ test_must_fail git pull &&
+ test $COPY = $(git rev-parse --verify me/copy) &&
+ git checkout HEAD -- file &&
+ git pull &&
+ test $COPY != $(git rev-parse --verify me/copy)
+
+'
+
+test_expect_success 'pull --rebase works on branch yet to be born' '
+ git rev-parse master >expect &&
+ mkdir empty_repo &&
+ (cd empty_repo &&
+ git init &&
+ git pull --rebase .. master &&
+ git rev-parse HEAD >../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t5521-pull-options.sh b/t/t5521-pull-options.sh
new file mode 100755
index 0000000..1b06691
--- /dev/null
+++ b/t/t5521-pull-options.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='pull options'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir parent &&
+ (cd parent && git init &&
+ echo one >file && git add file &&
+ git commit -m one)
+'
+
+test_expect_success 'git pull -q' '
+ mkdir clonedq &&
+ (cd clonedq && git init &&
+ git pull -q "../parent" >out 2>err &&
+ test ! -s err &&
+ test ! -s out)
+'
+
+test_expect_success 'git pull' '
+ mkdir cloned &&
+ (cd cloned && git init &&
+ git pull "../parent" >out 2>err &&
+ test -s err &&
+ test ! -s out)
+'
+
+test_expect_success 'git pull -v' '
+ mkdir clonedv &&
+ (cd clonedv && git init &&
+ git pull -v "../parent" >out 2>err &&
+ test -s err &&
+ test ! -s out)
+'
+
+test_expect_success 'git pull -v -q' '
+ mkdir clonedvq &&
+ (cd clonedvq && git init &&
+ git pull -v -q "../parent" >out 2>err &&
+ test ! -s out &&
+ test ! -s err)
+'
+
+test_expect_success 'git pull -q -v' '
+ mkdir clonedqv &&
+ (cd clonedqv && git init &&
+ git pull -q -v "../parent" >out 2>err &&
+ test ! -s out &&
+ test -s err)
+'
+
+test_expect_success 'git pull --force' '
+ mkdir clonedoldstyle &&
+ (cd clonedoldstyle && git init &&
+ cat >>.git/config <<-\EOF &&
+ [remote "one"]
+ url = ../parent
+ fetch = refs/heads/master:refs/heads/mirror
+ [remote "two"]
+ url = ../parent
+ fetch = refs/heads/master:refs/heads/origin
+ [branch "master"]
+ remote = two
+ merge = refs/heads/master
+ EOF
+ git pull two &&
+ test_commit A &&
+ git branch -f origin &&
+ git pull --all --force
+ )
+'
+
+test_expect_success 'git pull --all' '
+ mkdir clonedmulti &&
+ (cd clonedmulti && git init &&
+ cat >>.git/config <<-\EOF &&
+ [remote "one"]
+ url = ../parent
+ fetch = refs/heads/*:refs/remotes/one/*
+ [remote "two"]
+ url = ../parent
+ fetch = refs/heads/*:refs/remotes/two/*
+ [branch "master"]
+ remote = one
+ merge = refs/heads/master
+ EOF
+ git pull --all
+ )
+'
+
+test_done
diff --git a/t/t5522-pull-symlink.sh b/t/t5522-pull-symlink.sh
new file mode 100755
index 0000000..298200f
--- /dev/null
+++ b/t/t5522-pull-symlink.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='pulling from symlinked subdir'
+
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='Symbolic links not supported, skipping tests.'
+ test_done
+fi
+
+# The scenario we are building:
+#
+# trash\ directory/
+# clone-repo/
+# subdir/
+# bar
+# subdir-link -> clone-repo/subdir/
+#
+# The working directory is subdir-link.
+
+test_expect_success setup '
+ mkdir subdir &&
+ echo file >subdir/file &&
+ git add subdir/file &&
+ git commit -q -m file &&
+ git clone -q . clone-repo &&
+ ln -s clone-repo/subdir/ subdir-link &&
+ (
+ cd clone-repo &&
+ git config receive.denyCurrentBranch warn
+ ) &&
+ git config receive.denyCurrentBranch warn
+'
+
+# Demonstrate that things work if we just avoid the symlink
+#
+test_expect_success 'pulling from real subdir' '
+ (
+ echo real >subdir/file &&
+ git commit -m real subdir/file &&
+ cd clone-repo/subdir/ &&
+ git pull &&
+ test real = $(cat file)
+ )
+'
+
+# From subdir-link, pulling should work as it does from
+# clone-repo/subdir/.
+#
+# Instead, the error pull gave was:
+#
+# fatal: 'origin': unable to chdir or not a git archive
+# fatal: The remote end hung up unexpectedly
+#
+# because git would find the .git/config for the "trash directory"
+# repo, not for the clone-repo repo. The "trash directory" repo
+# had no entry for origin. Git found the wrong .git because
+# git rev-parse --show-cdup printed a path relative to
+# clone-repo/subdir/, not subdir-link/. Git rev-parse --show-cdup
+# used the correct .git, but when the git pull shell script did
+# "cd `git rev-parse --show-cdup`", it ended up in the wrong
+# directory. A POSIX shell's "cd" works a little differently
+# than chdir() in C; "cd -P" is much closer to chdir().
+#
+test_expect_success 'pulling from symlinked subdir' '
+ (
+ echo link >subdir/file &&
+ git commit -m link subdir/file &&
+ cd subdir-link/ &&
+ git pull &&
+ test link = $(cat file)
+ )
+'
+
+# Prove that the remote end really is a repo, and other commands
+# work fine in this context. It's just that "git pull" breaks.
+#
+test_expect_success 'pushing from symlinked subdir' '
+ (
+ cd subdir-link/ &&
+ echo push >file &&
+ git commit -m push ./file &&
+ git push
+ ) &&
+ test push = $(git show HEAD:subdir/file)
+'
+
+test_done
diff --git a/t/t5523-push-upstream.sh b/t/t5523-push-upstream.sh
new file mode 100755
index 0000000..00da707
--- /dev/null
+++ b/t/t5523-push-upstream.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='push with --set-upstream'
+. ./test-lib.sh
+
+test_expect_success 'setup bare parent' '
+ git init --bare parent &&
+ git remote add upstream parent
+'
+
+test_expect_success 'setup local commit' '
+ echo content >file &&
+ git add file &&
+ git commit -m one
+'
+
+check_config() {
+ (echo $2; echo $3) >expect.$1
+ (git config branch.$1.remote
+ git config branch.$1.merge) >actual.$1
+ test_cmp expect.$1 actual.$1
+}
+
+test_expect_success 'push -u master:master' '
+ git push -u upstream master:master &&
+ check_config master upstream refs/heads/master
+'
+
+test_expect_success 'push -u master:other' '
+ git push -u upstream master:other &&
+ check_config master upstream refs/heads/other
+'
+
+test_expect_success 'push -u --dry-run master:otherX' '
+ git push -u --dry-run upstream master:otherX &&
+ check_config master upstream refs/heads/other
+'
+
+test_expect_success 'push -u master2:master2' '
+ git branch master2 &&
+ git push -u upstream master2:master2 &&
+ check_config master2 upstream refs/heads/master2
+'
+
+test_expect_success 'push -u master2:other2' '
+ git push -u upstream master2:other2 &&
+ check_config master2 upstream refs/heads/other2
+'
+
+test_expect_success 'push -u :master2' '
+ git push -u upstream :master2 &&
+ check_config master2 upstream refs/heads/other2
+'
+
+test_expect_success 'push -u --all' '
+ git branch all1 &&
+ git branch all2 &&
+ git push -u --all &&
+ check_config all1 upstream refs/heads/all1 &&
+ check_config all2 upstream refs/heads/all2
+'
+
+test_expect_success 'push -u HEAD' '
+ git checkout -b headbranch &&
+ git push -u upstream HEAD &&
+ check_config headbranch upstream refs/heads/headbranch
+'
+
+test_done
diff --git a/t/t5524-pull-msg.sh b/t/t5524-pull-msg.sh
new file mode 100755
index 0000000..8cccecc
--- /dev/null
+++ b/t/t5524-pull-msg.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='git pull message generation'
+
+. ./test-lib.sh
+
+dollar='$Dollar'
+
+test_expect_success setup '
+ test_commit initial afile original &&
+ git clone . cloned &&
+ (
+ cd cloned &&
+ echo added >bfile &&
+ git add bfile &&
+ test_tick &&
+ git commit -m "add bfile"
+ ) &&
+ test_tick && test_tick &&
+ echo "original $dollar" >afile &&
+ git add afile &&
+ git commit -m "do not clobber $dollar signs"
+'
+
+test_expect_success pull '
+(
+ cd cloned &&
+ git pull --log &&
+ git log -2 &&
+ git cat-file commit HEAD >result &&
+ grep Dollar result
+)
+'
+
+test_done
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
new file mode 100755
index 0000000..044603c
--- /dev/null
+++ b/t/t5530-upload-pack-error.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+test_description='errors in upload-pack'
+
+. ./test-lib.sh
+
+D=`pwd`
+
+corrupt_repo () {
+ object_sha1=$(git rev-parse "$1") &&
+ ob=$(expr "$object_sha1" : "\(..\)") &&
+ ject=$(expr "$object_sha1" : "..\(..*\)") &&
+ rm -f ".git/objects/$ob/$ject"
+}
+
+test_expect_success 'setup and corrupt repository' '
+
+ echo file >file &&
+ git add file &&
+ git rev-parse :file &&
+ git commit -a -m original &&
+ test_tick &&
+ echo changed >file &&
+ git commit -a -m changed &&
+ corrupt_repo HEAD:file
+
+'
+
+test_expect_success 'fsck fails' '
+ test_must_fail git fsck
+'
+
+test_expect_success 'upload-pack fails due to error in pack-objects packing' '
+
+ printf "0032want %s\n00000009done\n0000" \
+ $(git rev-parse HEAD) >input &&
+ test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
+ grep "unable to read" output.err &&
+ grep "pack-objects died" output.err
+'
+
+test_expect_success 'corrupt repo differently' '
+
+ git hash-object -w file &&
+ corrupt_repo HEAD^^{tree}
+
+'
+
+test_expect_success 'fsck fails' '
+ test_must_fail git fsck
+'
+test_expect_success 'upload-pack fails due to error in rev-list' '
+
+ printf "0032want %s\n0034shallow %s00000009done\n0000" \
+ $(git rev-parse HEAD) $(git rev-parse HEAD^) >input &&
+ test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
+ # pack-objects survived
+ grep "Total.*, reused" output.err &&
+ # but there was an error, which must have been in rev-list
+ grep "bad tree object" output.err
+'
+
+test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
+
+ printf "0032want %s\n00000009done\n0000" \
+ $(git rev-parse HEAD) >input &&
+ test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
+ grep "bad tree object" output.err &&
+ grep "pack-objects died" output.err
+'
+
+test_expect_success 'create empty repository' '
+
+ mkdir foo &&
+ cd foo &&
+ git init
+
+'
+
+test_expect_success 'fetch fails' '
+
+ test_must_fail git fetch .. master
+
+'
+
+test_done
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
new file mode 100755
index 0000000..65d8d47
--- /dev/null
+++ b/t/t5531-deep-submodule-push.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='unpack-objects'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir pub.git &&
+ GIT_DIR=pub.git git init --bare
+ GIT_DIR=pub.git git config receive.fsckobjects true &&
+ mkdir work &&
+ (
+ cd work &&
+ git init &&
+ mkdir -p gar/bage &&
+ (
+ cd gar/bage &&
+ git init &&
+ >junk &&
+ git add junk &&
+ git commit -m "Initial junk"
+ ) &&
+ git add gar/bage &&
+ git commit -m "Initial superproject"
+ )
+'
+
+test_expect_success push '
+ (
+ cd work &&
+ git push ../pub.git master
+ )
+'
+
+test_done
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
new file mode 100755
index 0000000..a266ca5
--- /dev/null
+++ b/t/t5540-http-push.sh
@@ -0,0 +1,145 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test WebDAV http-push
+
+This test runs various sanity checks on http-push.'
+
+. ./test-lib.sh
+
+if git http-push > /dev/null 2>&1 || [ $? -eq 128 ]
+then
+ skip_all="skipping test, USE_CURL_MULTI is not defined"
+ test_done
+fi
+
+LIB_HTTPD_DAV=t
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5540'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+ROOT_PATH="$PWD"
+start_httpd
+
+test_expect_success 'setup remote repository' '
+ cd "$ROOT_PATH" &&
+ mkdir test_repo &&
+ cd test_repo &&
+ git init &&
+ : >path1 &&
+ git add path1 &&
+ test_tick &&
+ git commit -m initial &&
+ cd - &&
+ git clone --bare test_repo test_repo.git &&
+ cd test_repo.git &&
+ git --bare update-server-info &&
+ mv hooks/post-update.sample hooks/post-update &&
+ ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+ cd - &&
+ mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+test_expect_success 'clone remote repository' '
+ cd "$ROOT_PATH" &&
+ git clone $HTTPD_URL/dumb/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository with packed refs' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ : >path2 &&
+ git add path2 &&
+ test_tick &&
+ git commit -m path2 &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+ git push &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push already up-to-date' '
+ git push
+'
+
+test_expect_success 'push to remote repository with unpacked refs' '
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ rm packed-refs &&
+ git update-ref refs/heads/master $ORIG_HEAD &&
+ git --bare update-server-info) &&
+ git push &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'http-push fetches unpacked objects' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+ "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_unpacked.git &&
+
+ git clone $HTTPD_URL/dumb/test_repo_unpacked.git \
+ "$ROOT_PATH"/fetch_unpacked &&
+
+ # By reset, we force git to retrieve the object
+ (cd "$ROOT_PATH"/fetch_unpacked &&
+ git reset --hard HEAD^ &&
+ git remote rm origin &&
+ git reflog expire --expire=0 --all &&
+ git prune &&
+ git push -f -v $HTTPD_URL/dumb/test_repo_unpacked.git master)
+'
+
+test_expect_success 'http-push fetches packed objects' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+ "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
+
+ git clone $HTTPD_URL/dumb/test_repo_packed.git \
+ "$ROOT_PATH"/test_repo_clone_packed &&
+
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo_packed.git &&
+ git --bare repack &&
+ git --bare prune-packed) &&
+
+ # By reset, we force git to retrieve the packed object
+ (cd "$ROOT_PATH"/test_repo_clone_packed &&
+ git reset --hard HEAD^ &&
+ git remote rm origin &&
+ git reflog expire --expire=0 --all &&
+ git prune &&
+ git push -f -v $HTTPD_URL/dumb/test_repo_packed.git master)
+'
+
+test_expect_success 'create and delete remote branch' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ git checkout -b dev &&
+ : >path3 &&
+ git add path3 &&
+ test_tick &&
+ git commit -m dev &&
+ git push origin dev &&
+ git push origin :dev &&
+ test_must_fail git show-ref --verify refs/remotes/origin/dev
+'
+
+test_expect_success 'MKCOL sends directory names with trailing slashes' '
+
+ ! grep "\"MKCOL.*[^/] HTTP/[^ ]*\"" < "$HTTPD_ROOT_PATH"/access.log
+
+'
+
+x1="[0-9a-f]"
+x2="$x1$x1"
+x5="$x1$x1$x1$x1$x1"
+x38="$x5$x5$x5$x5$x5$x5$x5$x1$x1$x1"
+x40="$x38$x2"
+
+test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
+ sed -e "s/PUT /OP /" -e "s/MOVE /OP /" "$HTTPD_ROOT_PATH"/access.log |
+ grep -e "\"OP .*/objects/$x2/${x38}_$x40 HTTP/[.0-9]*\" 20[0-9] "
+
+'
+
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+ "$ROOT_PATH"/test_repo_clone master
+
+stop_httpd
+
+test_done
diff --git a/t/t5541-http-push.sh b/t/t5541-http-push.sh
new file mode 100755
index 0000000..b0c2a2c
--- /dev/null
+++ b/t/t5541-http-push.sh
@@ -0,0 +1,141 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Clemens Buchacher <drizzd@aon.at>
+#
+
+test_description='test smart pushing over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+ skip_all='skipping test, git built without http support'
+ test_done
+fi
+
+ROOT_PATH="$PWD"
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5541'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup remote repository' '
+ cd "$ROOT_PATH" &&
+ mkdir test_repo &&
+ cd test_repo &&
+ git init &&
+ : >path1 &&
+ git add path1 &&
+ test_tick &&
+ git commit -m initial &&
+ cd - &&
+ git clone --bare test_repo test_repo.git &&
+ cd test_repo.git &&
+ git config http.receivepack true &&
+ ORIG_HEAD=$(git rev-parse --verify HEAD) &&
+ cd - &&
+ mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
+'
+
+cat >exp <<EOF
+GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'no empty path components' '
+ # In the URL, add a trailing slash, and see if git appends yet another
+ # slash.
+ cd "$ROOT_PATH" &&
+ git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
+
+ sed -e "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " >act <"$HTTPD_ROOT_PATH"/access.log &&
+
+ # Clear the log, so that it does not affect the "used receive-pack
+ # service" test which reads the log too.
+ #
+ # We do this before the actual comparison to ensure the log is cleared.
+ echo > "$HTTPD_ROOT_PATH"/access.log &&
+
+ test_cmp exp act
+'
+
+test_expect_success 'clone remote repository' '
+ rm -rf test_repo_clone &&
+ git clone $HTTPD_URL/smart/test_repo.git test_repo_clone
+'
+
+test_expect_success 'push to remote repository' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ : >path2 &&
+ git add path2 &&
+ test_tick &&
+ git commit -m path2 &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+ git push &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+ test $HEAD = $(git rev-parse --verify HEAD))
+'
+
+test_expect_success 'push already up-to-date' '
+ git push
+'
+
+test_expect_success 'create and delete remote branch' '
+ cd "$ROOT_PATH"/test_repo_clone &&
+ git checkout -b dev &&
+ : >path3 &&
+ git add path3 &&
+ test_tick &&
+ git commit -m dev &&
+ git push origin dev &&
+ git push origin :dev &&
+ test_must_fail git show-ref --verify refs/remotes/origin/dev
+'
+
+cat >exp <<EOF
+
+GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
+EOF
+test_expect_success 'used receive-pack service' '
+ sed -e "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " >act <"$HTTPD_ROOT_PATH"/access.log &&
+ test_cmp exp act
+'
+
+test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
+ "$ROOT_PATH"/test_repo_clone master
+
+test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
+ # create a dissimilarly-named remote ref so that git is unable to match the
+ # two refs (viz. local, remote) unless an explicit refspec is provided.
+ git push origin master:retsam
+
+ echo "change changed" > path2 &&
+ git commit -a -m path2 --amend &&
+
+ # push master too; this ensures there is at least one '"'push'"' command to
+ # the remote helper and triggers interaction with the helper.
+ test_must_fail git push -v origin +master master:retsam >output 2>&1 &&
+
+ grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
+ grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output &&
+
+ grep "To prevent you from losing history, non-fast-forward updates were rejected" \
+ output
+'
+
+stop_httpd
+test_done
diff --git a/t/t5550-http-fetch.sh b/t/t5550-http-fetch.sh
new file mode 100755
index 0000000..2fb48d0
--- /dev/null
+++ b/t/t5550-http-fetch.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='test dumb fetching over http via static file'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+ skip_all='skipping test, git built without http support'
+ test_done
+fi
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5550'}
+start_httpd
+
+test_expect_success 'setup repository' '
+ echo content >file &&
+ git add file &&
+ git commit -m one
+'
+
+test_expect_success 'create http-accessible bare repository' '
+ mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git --bare init &&
+ echo "exec git update-server-info" >hooks/post-update &&
+ chmod +x hooks/post-update
+ ) &&
+ git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git push public master:master
+'
+
+test_expect_success 'clone http repository' '
+ git clone $HTTPD_URL/dumb/repo.git clone &&
+ test_cmp file clone/file
+'
+
+test_expect_success 'fetch changes via http' '
+ echo content >>file &&
+ git commit -a -m two &&
+ git push public
+ (cd clone && git pull) &&
+ test_cmp file clone/file
+'
+
+test_expect_success 'http remote detects correct HEAD' '
+ git push public master:other &&
+ (cd clone &&
+ git remote set-head origin -d &&
+ git remote set-head origin -a &&
+ git symbolic-ref refs/remotes/origin/HEAD > output &&
+ echo refs/remotes/origin/master > expect &&
+ test_cmp expect output
+ )
+'
+
+test_expect_success 'fetch packed objects' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git &&
+ git --bare repack &&
+ git --bare prune-packed
+ ) &&
+ git clone $HTTPD_URL/dumb/repo_pack.git
+'
+
+test_expect_success 'fetch notices corrupt pack' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad1.git &&
+ p=`ls objects/pack/pack-*.pack` &&
+ chmod u+w $p &&
+ printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+ ) &&
+ mkdir repo_bad1.git &&
+ (cd repo_bad1.git &&
+ git --bare init &&
+ test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad1.git &&
+ test 0 = `ls objects/pack/pack-*.pack | wc -l`
+ )
+'
+
+test_expect_success 'fetch notices corrupt idx' '
+ cp -R "$HTTPD_DOCUMENT_ROOT_PATH"/repo_pack.git "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH"/repo_bad2.git &&
+ p=`ls objects/pack/pack-*.idx` &&
+ chmod u+w $p &&
+ printf %0256d 0 | dd of=$p bs=256 count=1 seek=1 conv=notrunc
+ ) &&
+ mkdir repo_bad2.git &&
+ (cd repo_bad2.git &&
+ git --bare init &&
+ test_must_fail git --bare fetch $HTTPD_URL/dumb/repo_bad2.git &&
+ test 0 = `ls objects/pack | wc -l`
+ )
+'
+
+test_expect_success 'did not use upload-pack service' '
+ grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
+ : >exp
+ test_cmp exp act
+'
+
+stop_httpd
+test_done
diff --git a/t/t5551-http-fetch.sh b/t/t5551-http-fetch.sh
new file mode 100755
index 0000000..fd19121
--- /dev/null
+++ b/t/t5551-http-fetch.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='test smart fetching over http via http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+ skip_all='skipping test, git built without http support'
+ test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5551'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'setup repository' '
+ echo content >file &&
+ git add file &&
+ git commit -m one
+'
+
+test_expect_success 'create http-accessible bare repository' '
+ mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git --bare init
+ ) &&
+ git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git push public master:master
+'
+
+cat >exp <<EOF
+> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
+> Accept: */*
+> Pragma: no-cache
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-advertisement
+> POST /smart/repo.git/git-upload-pack HTTP/1.1
+> Accept-Encoding: deflate, gzip
+> Content-Type: application/x-git-upload-pack-request
+> Accept: application/x-git-upload-pack-result
+> Content-Length: xxx
+< HTTP/1.1 200 OK
+< Pragma: no-cache
+< Cache-Control: no-cache, max-age=0, must-revalidate
+< Content-Type: application/x-git-upload-pack-result
+EOF
+test_expect_success 'clone http repository' '
+ GIT_CURL_VERBOSE=1 git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
+ test_cmp file clone/file &&
+ tr '\''\015'\'' Q <err |
+ sed -e "
+ s/Q\$//
+ /^[*] /d
+ /^$/d
+ /^< $/d
+
+ /^[^><]/{
+ s/^/> /
+ }
+
+ /^> User-Agent: /d
+ /^> Host: /d
+ /^> POST /,$ {
+ /^> Accept: [*]\\/[*]/d
+ }
+ s/^> Content-Length: .*/> Content-Length: xxx/
+ /^> 00..want /d
+ /^> 00.*done/d
+
+ /^< Server: /d
+ /^< Expires: /d
+ /^< Date: /d
+ /^< Content-Length: /d
+ /^< Transfer-Encoding: /d
+ " >act &&
+ test_cmp exp act
+'
+
+test_expect_success 'fetch changes via http' '
+ echo content >>file &&
+ git commit -a -m two &&
+ git push public
+ (cd clone && git pull) &&
+ test_cmp file clone/file
+'
+
+cat >exp <<EOF
+GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'used upload-pack service' '
+ sed -e "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " >act <"$HTTPD_ROOT_PATH"/access.log &&
+ test_cmp exp act
+'
+
+stop_httpd
+test_done
diff --git a/t/t5560-http-backend-noserver.sh b/t/t5560-http-backend-noserver.sh
new file mode 100755
index 0000000..44885b8
--- /dev/null
+++ b/t/t5560-http-backend-noserver.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='test git-http-backend-noserver'
+. ./test-lib.sh
+
+HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
+
+run_backend() {
+ echo "$2" |
+ QUERY_STRING="${1#*\?}" \
+ GIT_PROJECT_ROOT="$HTTPD_DOCUMENT_ROOT_PATH" \
+ PATH_INFO="${1%%\?*}" \
+ git http-backend >act.out 2>act.err
+}
+
+GET() {
+ export REQUEST_METHOD="GET" &&
+ run_backend "/repo.git/$1" &&
+ unset REQUEST_METHOD &&
+ if ! grep "Status" act.out >act
+ then
+ printf "Status: 200 OK\r\n" >act
+ fi
+ printf "Status: $2\r\n" >exp &&
+ test_cmp exp act
+}
+
+POST() {
+ export REQUEST_METHOD="POST" &&
+ export CONTENT_TYPE="application/x-$1-request" &&
+ run_backend "/repo.git/$1" "$2" &&
+ unset REQUEST_METHOD &&
+ unset CONTENT_TYPE &&
+ if ! grep "Status" act.out >act
+ then
+ printf "Status: 200 OK\r\n" >act
+ fi
+ printf "Status: $3\r\n" >exp &&
+ test_cmp exp act
+}
+
+log_div() {
+ return 0
+}
+
+. "$TEST_DIRECTORY"/t556x_common
+
+expect_aliased() {
+ export REQUEST_METHOD="GET" &&
+ if test $1 = 0; then
+ run_backend "$2"
+ else
+ run_backend "$2" &&
+ echo "fatal: '$2': aliased" >exp.err &&
+ test_cmp exp.err act.err
+ fi
+ unset REQUEST_METHOD
+}
+
+test_expect_success 'http-backend blocks bad PATH_INFO' '
+ config http.getanyfile true &&
+
+ expect_aliased 0 /repo.git/HEAD &&
+
+ expect_aliased 1 /repo.git/../HEAD &&
+ expect_aliased 1 /../etc/passwd &&
+ expect_aliased 1 ../etc/passwd &&
+ expect_aliased 1 /etc//passwd &&
+ expect_aliased 1 /etc/./passwd &&
+ expect_aliased 1 //domain/data.txt
+'
+
+test_done
diff --git a/t/t5561-http-backend.sh b/t/t5561-http-backend.sh
new file mode 100755
index 0000000..b5d7fbc
--- /dev/null
+++ b/t/t5561-http-backend.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+
+test_description='test git-http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+ skip_all='skipping test, git built without http support'
+ test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5561'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+GET() {
+ curl --include "$HTTPD_URL/$SMART/repo.git/$1" >out 2>/dev/null &&
+ tr '\015' Q <out |
+ sed '
+ s/Q$//
+ 1q
+ ' >act &&
+ echo "HTTP/1.1 $2" >exp &&
+ test_cmp exp act
+}
+
+POST() {
+ curl --include --data "$2" \
+ --header "Content-Type: application/x-$1-request" \
+ "$HTTPD_URL/smart/repo.git/$1" >out 2>/dev/null &&
+ tr '\015' Q <out |
+ sed '
+ s/Q$//
+ 1q
+ ' >act &&
+ echo "HTTP/1.1 $3" >exp &&
+ test_cmp exp act
+}
+
+log_div() {
+ echo >>"$HTTPD_ROOT_PATH"/access.log
+ echo "### $1" >>"$HTTPD_ROOT_PATH"/access.log
+ echo "###" >>"$HTTPD_ROOT_PATH"/access.log
+}
+
+. "$TEST_DIRECTORY"/t556x_common
+
+cat >exp <<EOF
+
+### refs/heads/master
+###
+GET /smart/repo.git/refs/heads/master HTTP/1.1 404 -
+
+### getanyfile default
+###
+GET /smart/repo.git/HEAD HTTP/1.1 200
+GET /smart/repo.git/info/refs HTTP/1.1 200
+GET /smart/repo.git/objects/info/packs HTTP/1.1 200
+GET /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET /smart/repo.git/$LOOSE_URL HTTP/1.1 200
+GET /smart/repo.git/$PACK_URL HTTP/1.1 200
+GET /smart/repo.git/$IDX_URL HTTP/1.1 200
+
+### no git-daemon-export-ok
+###
+GET /smart_noexport/repo.git/HEAD HTTP/1.1 404 -
+GET /smart_noexport/repo.git/info/refs HTTP/1.1 404 -
+GET /smart_noexport/repo.git/objects/info/packs HTTP/1.1 404 -
+GET /smart_noexport/repo.git/objects/info/alternates HTTP/1.1 404 -
+GET /smart_noexport/repo.git/objects/info/http-alternates HTTP/1.1 404 -
+GET /smart_noexport/repo.git/$LOOSE_URL HTTP/1.1 404 -
+GET /smart_noexport/repo.git/$PACK_URL HTTP/1.1 404 -
+GET /smart_noexport/repo.git/$IDX_URL HTTP/1.1 404 -
+
+### git-daemon-export-ok
+###
+GET /smart_noexport/repo.git/HEAD HTTP/1.1 200
+GET /smart_noexport/repo.git/info/refs HTTP/1.1 200
+GET /smart_noexport/repo.git/objects/info/packs HTTP/1.1 200
+GET /smart_noexport/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET /smart_noexport/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET /smart_noexport/repo.git/$LOOSE_URL HTTP/1.1 200
+GET /smart_noexport/repo.git/$PACK_URL HTTP/1.1 200
+GET /smart_noexport/repo.git/$IDX_URL HTTP/1.1 200
+
+### getanyfile true
+###
+GET /smart/repo.git/HEAD HTTP/1.1 200
+GET /smart/repo.git/info/refs HTTP/1.1 200
+GET /smart/repo.git/objects/info/packs HTTP/1.1 200
+GET /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET /smart/repo.git/$LOOSE_URL HTTP/1.1 200
+GET /smart/repo.git/$PACK_URL HTTP/1.1 200
+GET /smart/repo.git/$IDX_URL HTTP/1.1 200
+
+### getanyfile false
+###
+GET /smart/repo.git/HEAD HTTP/1.1 403 -
+GET /smart/repo.git/info/refs HTTP/1.1 403 -
+GET /smart/repo.git/objects/info/packs HTTP/1.1 403 -
+GET /smart/repo.git/objects/info/alternates HTTP/1.1 403 -
+GET /smart/repo.git/objects/info/http-alternates HTTP/1.1 403 -
+GET /smart/repo.git/$LOOSE_URL HTTP/1.1 403 -
+GET /smart/repo.git/$PACK_URL HTTP/1.1 403 -
+GET /smart/repo.git/$IDX_URL HTTP/1.1 403 -
+
+### uploadpack default
+###
+GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
+
+### uploadpack true
+###
+GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
+
+### uploadpack false
+###
+GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-upload-pack HTTP/1.1 403 -
+
+### receivepack default
+###
+GET /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
+
+### receivepack true
+###
+GET /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/repo.git/git-receive-pack HTTP/1.1 200 -
+
+### receivepack false
+###
+GET /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
+EOF
+test_expect_success 'server request log matches test results' '
+ sed -e "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " >act <"$HTTPD_ROOT_PATH"/access.log &&
+ test_cmp exp act
+'
+
+stop_httpd
+test_done
diff --git a/t/t556x_common b/t/t556x_common
new file mode 100755
index 0000000..be024e5
--- /dev/null
+++ b/t/t556x_common
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+find_file() {
+ cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ find $1 -type f |
+ sed -e 1q
+}
+
+config() {
+ git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" config $1 $2
+}
+
+test_expect_success 'setup repository' '
+ echo content >file &&
+ git add file &&
+ git commit -m one &&
+
+ mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git --bare init &&
+ : >objects/info/alternates &&
+ : >objects/info/http-alternates
+ ) &&
+ git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git push public master:master &&
+
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ git repack -a -d
+ ) &&
+
+ echo other >file &&
+ git add file &&
+ git commit -m two &&
+ git push public master:master &&
+
+ LOOSE_URL=$(find_file objects/??) &&
+ PACK_URL=$(find_file objects/pack/*.pack) &&
+ IDX_URL=$(find_file objects/pack/*.idx)
+'
+
+get_static_files() {
+ GET HEAD "$1" &&
+ GET info/refs "$1" &&
+ GET objects/info/packs "$1" &&
+ GET objects/info/alternates "$1" &&
+ GET objects/info/http-alternates "$1" &&
+ GET $LOOSE_URL "$1" &&
+ GET $PACK_URL "$1" &&
+ GET $IDX_URL "$1"
+}
+
+SMART=smart
+export GIT_HTTP_EXPORT_ALL=1
+test_expect_success 'direct refs/heads/master not found' '
+ log_div "refs/heads/master"
+ GET refs/heads/master "404 Not Found"
+'
+test_expect_success 'static file is ok' '
+ log_div "getanyfile default"
+ get_static_files "200 OK"
+'
+SMART=smart_noexport
+unset GIT_HTTP_EXPORT_ALL
+test_expect_success 'no export by default' '
+ log_div "no git-daemon-export-ok"
+ get_static_files "404 Not Found"
+'
+test_expect_success 'export if git-daemon-export-ok' '
+ log_div "git-daemon-export-ok"
+ (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+ touch git-daemon-export-ok
+ ) &&
+ get_static_files "200 OK"
+'
+SMART=smart
+export GIT_HTTP_EXPORT_ALL=1
+test_expect_success 'static file if http.getanyfile true is ok' '
+ log_div "getanyfile true"
+ config http.getanyfile true &&
+ get_static_files "200 OK"
+'
+test_expect_success 'static file if http.getanyfile false fails' '
+ log_div "getanyfile false"
+ config http.getanyfile false &&
+ get_static_files "403 Forbidden"
+'
+
+test_expect_success 'http.uploadpack default enabled' '
+ log_div "uploadpack default"
+ GET info/refs?service=git-upload-pack "200 OK" &&
+ POST git-upload-pack 0000 "200 OK"
+'
+test_expect_success 'http.uploadpack true' '
+ log_div "uploadpack true"
+ config http.uploadpack true &&
+ GET info/refs?service=git-upload-pack "200 OK" &&
+ POST git-upload-pack 0000 "200 OK"
+'
+test_expect_success 'http.uploadpack false' '
+ log_div "uploadpack false"
+ config http.uploadpack false &&
+ GET info/refs?service=git-upload-pack "403 Forbidden" &&
+ POST git-upload-pack 0000 "403 Forbidden"
+'
+
+test_expect_success 'http.receivepack default disabled' '
+ log_div "receivepack default"
+ GET info/refs?service=git-receive-pack "403 Forbidden" &&
+ POST git-receive-pack 0000 "403 Forbidden"
+'
+test_expect_success 'http.receivepack true' '
+ log_div "receivepack true"
+ config http.receivepack true &&
+ GET info/refs?service=git-receive-pack "200 OK" &&
+ POST git-receive-pack 0000 "200 OK"
+'
+test_expect_success 'http.receivepack false' '
+ log_div "receivepack false"
+ config http.receivepack false &&
+ GET info/refs?service=git-receive-pack "403 Forbidden" &&
+ POST git-receive-pack 0000 "403 Forbidden"
+'
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
new file mode 100755
index 0000000..ee06d28
--- /dev/null
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (C) 2006 Carl D. Worth <cworth@cworth.org>
+#
+
+test_description='test git clone to cleanup after failure
+
+This test covers the fact that if git clone fails, it should remove
+the directory it created, to avoid the user having to manually
+remove the directory before attempting a clone again.'
+
+. ./test-lib.sh
+
+test_expect_success \
+ 'clone of non-existent source should fail' \
+ 'test_must_fail git clone foo bar'
+
+test_expect_success \
+ 'failed clone should not leave a directory' \
+ '! test -d bar'
+
+# Need a repo to clone
+test_create_repo foo
+
+# clone doesn't like it if there is no HEAD. Is that a bug?
+(cd foo && touch file && git add file && git commit -m 'add file' >/dev/null 2>&1)
+
+# source repository given to git clone should be relative to the
+# current path not to the target dir
+test_expect_success \
+ 'clone of non-existent (relative to $PWD) source should fail' \
+ 'test_must_fail git clone ../foo baz'
+
+test_expect_success \
+ 'clone should work now that source exists' \
+ 'git clone foo bar'
+
+test_expect_success \
+ 'successful clone must leave the directory' \
+ 'cd bar'
+
+test_done
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
new file mode 100755
index 0000000..8abb71a
--- /dev/null
+++ b/t/t5601-clone.sh
@@ -0,0 +1,191 @@
+#!/bin/sh
+
+test_description=clone
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ rm -fr .git &&
+ test_create_repo src &&
+ (
+ cd src
+ >file
+ git add file
+ git commit -m initial
+ )
+
+'
+
+test_expect_success 'clone with excess parameters (1)' '
+
+ rm -fr dst &&
+ test_must_fail git clone -n src dst junk
+
+'
+
+test_expect_success 'clone with excess parameters (2)' '
+
+ rm -fr dst &&
+ test_must_fail git clone -n "file://$(pwd)/src" dst junk
+
+'
+
+test_expect_success 'output from clone' '
+ rm -fr dst &&
+ git clone -n "file://$(pwd)/src" dst >output &&
+ test $(grep Clon output | wc -l) = 1
+'
+
+test_expect_success 'clone does not keep pack' '
+
+ rm -fr dst &&
+ git clone -n "file://$(pwd)/src" dst &&
+ ! test -f dst/file &&
+ ! (echo dst/.git/objects/pack/pack-* | grep "\.keep")
+
+'
+
+test_expect_success 'clone checks out files' '
+
+ rm -fr dst &&
+ git clone src dst &&
+ test -f dst/file
+
+'
+
+test_expect_success 'clone respects GIT_WORK_TREE' '
+
+ GIT_WORK_TREE=worktree git clone src bare &&
+ test -f bare/config &&
+ test -f worktree/file
+
+'
+
+test_expect_success 'clone creates intermediate directories' '
+
+ git clone src long/path/to/dst &&
+ test -f long/path/to/dst/file
+
+'
+
+test_expect_success 'clone creates intermediate directories for bare repo' '
+
+ git clone --bare src long/path/to/bare/dst &&
+ test -f long/path/to/bare/dst/config
+
+'
+
+test_expect_success 'clone --mirror' '
+
+ git clone --mirror src mirror &&
+ test -f mirror/HEAD &&
+ test ! -f mirror/file &&
+ FETCH="$(cd mirror && git config remote.origin.fetch)" &&
+ test "+refs/*:refs/*" = "$FETCH" &&
+ MIRROR="$(cd mirror && git config --bool remote.origin.mirror)" &&
+ test "$MIRROR" = true
+
+'
+
+test_expect_success 'clone --bare names the local repository <name>.git' '
+
+ git clone --bare src &&
+ test -d src.git
+
+'
+
+test_expect_success 'clone --mirror does not repeat tags' '
+
+ (cd src &&
+ git tag some-tag HEAD) &&
+ git clone --mirror src mirror2 &&
+ (cd mirror2 &&
+ git show-ref 2> clone.err > clone.out) &&
+ test_must_fail grep Duplicate mirror2/clone.err &&
+ grep some-tag mirror2/clone.out
+
+'
+
+test_expect_success 'clone to destination with trailing /' '
+
+ git clone src target-1/ &&
+ T=$( cd target-1 && git rev-parse HEAD ) &&
+ S=$( cd src && git rev-parse HEAD ) &&
+ test "$T" = "$S"
+
+'
+
+test_expect_success 'clone to destination with extra trailing /' '
+
+ git clone src target-2/// &&
+ T=$( cd target-2 && git rev-parse HEAD ) &&
+ S=$( cd src && git rev-parse HEAD ) &&
+ test "$T" = "$S"
+
+'
+
+test_expect_success 'clone to an existing empty directory' '
+ mkdir target-3 &&
+ git clone src target-3 &&
+ T=$( cd target-3 && git rev-parse HEAD ) &&
+ S=$( cd src && git rev-parse HEAD ) &&
+ test "$T" = "$S"
+'
+
+test_expect_success 'clone to an existing non-empty directory' '
+ mkdir target-4 &&
+ >target-4/Fakefile &&
+ test_must_fail git clone src target-4
+'
+
+test_expect_success 'clone to an existing path' '
+ >target-5 &&
+ test_must_fail git clone src target-5
+'
+
+test_expect_success 'clone a void' '
+ mkdir src-0 &&
+ (
+ cd src-0 && git init
+ ) &&
+ git clone "file://$(pwd)/src-0" target-6 2>err-6 &&
+ ! grep "fatal:" err-6 &&
+ (
+ cd src-0 && test_commit A
+ ) &&
+ git clone "file://$(pwd)/src-0" target-7 2>err-7 &&
+ ! grep "fatal:" err-7 &&
+ # There is no reason to insist they are bit-for-bit
+ # identical, but this test should suffice for now.
+ test_cmp target-6/.git/config target-7/.git/config
+'
+
+test_expect_success 'clone respects global branch.autosetuprebase' '
+ (
+ HOME=$(pwd) &&
+ export HOME &&
+ test_config="$HOME/.gitconfig" &&
+ unset GIT_CONFIG_NOGLOBAL &&
+ git config -f "$test_config" branch.autosetuprebase remote &&
+ rm -fr dst &&
+ git clone src dst &&
+ cd dst &&
+ actual="z$(git config branch.master.rebase)" &&
+ test ztrue = $actual
+ )
+'
+
+test_expect_success 'respect url-encoding of file://' '
+ git init x+y &&
+ test_must_fail git clone "file://$PWD/x+y" xy-url &&
+ git clone "file://$PWD/x%2By" xy-url
+'
+
+test_expect_success 'do not respect url-encoding of non-url path' '
+ git init x+y &&
+ test_must_fail git clone x%2By xy-regular &&
+ git clone x+y xy-regular
+'
+
+test_done
diff --git a/t/t5602-clone-remote-exec.sh b/t/t5602-clone-remote-exec.sh
new file mode 100755
index 0000000..deffdae
--- /dev/null
+++ b/t/t5602-clone-remote-exec.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+test_description=clone
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo "#!/bin/sh" > not_ssh
+ echo "echo \"\$*\" > not_ssh_output" >> not_ssh
+ echo "exit 1" >> not_ssh
+ chmod +x not_ssh
+'
+
+test_expect_success 'clone calls git upload-pack unqualified with no -u option' '
+ GIT_SSH=./not_ssh git clone localhost:/path/to/repo junk
+ echo "localhost git-upload-pack '\''/path/to/repo'\''" >expected
+ test_cmp expected not_ssh_output
+'
+
+test_expect_success 'clone calls specified git upload-pack with -u option' '
+ GIT_SSH=./not_ssh git clone -u ./something/bin/git-upload-pack localhost:/path/to/repo junk
+ echo "localhost ./something/bin/git-upload-pack '\''/path/to/repo'\''" >expected
+ test_cmp expected not_ssh_output
+'
+
+test_done
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
new file mode 100755
index 0000000..895f559
--- /dev/null
+++ b/t/t5700-clone-reference.sh
@@ -0,0 +1,149 @@
+#!/bin/sh
+#
+# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
+#
+
+test_description='test clone --reference'
+. ./test-lib.sh
+
+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 initial'
+
+cd "$base_dir"
+
+test_expect_success 'preparing second repository' \
+'git clone A B && cd B &&
+echo second > file2 &&
+git add file2 &&
+git commit -m addition &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'cloning with reference (-l -s)' \
+'git clone -l -s --reference B A C'
+
+cd "$base_dir"
+
+test_expect_success 'existence of info/alternates' \
+'test `wc -l <C/.git/objects/info/alternates` = 2'
+
+cd "$base_dir"
+
+test_expect_success 'pulling from reference' \
+'cd C &&
+git pull ../B master'
+
+cd "$base_dir"
+
+test_expect_success 'that reference gets used' \
+'cd C &&
+echo "0 objects, 0 kilobytes" > expected &&
+git count-objects > current &&
+test_cmp expected current'
+
+cd "$base_dir"
+
+rm -f "$U"
+
+test_expect_success 'cloning with reference (no -l -s)' \
+'GIT_DEBUG_SEND_PACK=3 git clone --reference B "file://$(pwd)/A" D 3>"$U"'
+
+test_expect_success 'fetched no objects' \
+'! grep "^want" "$U"'
+
+cd "$base_dir"
+
+test_expect_success 'existence of info/alternates' \
+'test `wc -l <D/.git/objects/info/alternates` = 1'
+
+cd "$base_dir"
+
+test_expect_success 'pulling from reference' \
+'cd D && git pull ../B master'
+
+cd "$base_dir"
+
+test_expect_success 'that reference gets used' \
+'cd D && echo "0 objects, 0 kilobytes" > expected &&
+git count-objects > current &&
+test_cmp expected current'
+
+cd "$base_dir"
+
+test_expect_success 'updating origin' \
+'cd A &&
+echo third > file3 &&
+git add file3 &&
+git commit -m update &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'pulling changes from origin' \
+'cd C &&
+git pull origin'
+
+cd "$base_dir"
+
+# the 2 local objects are commit and tree from the merge
+test_expect_success 'that alternate to origin gets used' \
+'cd C &&
+echo "2 objects" > expected &&
+git count-objects | cut -d, -f1 > current &&
+test_cmp expected current'
+
+cd "$base_dir"
+
+test_expect_success 'pulling changes from origin' \
+'cd D &&
+git pull origin'
+
+cd "$base_dir"
+
+# the 5 local objects are expected; file3 blob, commit in A to add it
+# and its tree, and 2 are our tree and the merge commit.
+test_expect_success 'check objects expected to exist locally' \
+'cd D &&
+echo "5 objects" > expected &&
+git count-objects | cut -d, -f1 > current &&
+test_cmp expected current'
+
+cd "$base_dir"
+
+test_expect_success 'preparing alternate repository #1' \
+'test_create_repo F && cd F &&
+echo first > file1 &&
+git add file1 &&
+git commit -m initial'
+
+cd "$base_dir"
+
+test_expect_success 'cloning alternate repo #2 and adding changes to repo #1' \
+'git clone F G && cd F &&
+echo second > file2 &&
+git add file2 &&
+git commit -m addition'
+
+cd "$base_dir"
+
+test_expect_success 'cloning alternate repo #1, using #2 as reference' \
+'git clone --reference G F H'
+
+cd "$base_dir"
+
+test_expect_success 'cloning with reference being subset of source (-l -s)' \
+'git clone -l -s --reference A B E'
+
+cd "$base_dir"
+
+test_done
diff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh
new file mode 100755
index 0000000..8b4c356
--- /dev/null
+++ b/t/t5701-clone-local.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+
+test_description='test local clone'
+. ./test-lib.sh
+
+D=`pwd`
+
+test_expect_success 'preparing origin repository' '
+ : >file && git add . && git commit -m1 &&
+ git clone --bare . a.git &&
+ git clone --bare . x &&
+ test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true &&
+ test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true
+ git bundle create b1.bundle --all &&
+ git bundle create b2.bundle master &&
+ mkdir dir &&
+ cp b1.bundle dir/b3
+ cp b1.bundle b4
+'
+
+test_expect_success 'local clone without .git suffix' '
+ cd "$D" &&
+ git clone -l -s a b &&
+ cd b &&
+ test "$(GIT_CONFIG=.git/config git config --bool core.bare)" = false &&
+ git fetch
+'
+
+test_expect_success 'local clone with .git suffix' '
+ cd "$D" &&
+ git clone -l -s a.git c &&
+ cd c &&
+ git fetch
+'
+
+test_expect_success 'local clone from x' '
+ cd "$D" &&
+ git clone -l -s x y &&
+ cd y &&
+ git fetch
+'
+
+test_expect_success 'local clone from x.git that does not exist' '
+ cd "$D" &&
+ if git clone -l -s x.git z
+ then
+ echo "Oops, should have failed"
+ false
+ else
+ echo happy
+ fi
+'
+
+test_expect_success 'With -no-hardlinks, local will make a copy' '
+ cd "$D" &&
+ git clone --bare --no-hardlinks x w &&
+ cd w &&
+ linked=$(find objects -type f ! -links 1 | wc -l) &&
+ test 0 = $linked
+'
+
+test_expect_success 'Even without -l, local will make a hardlink' '
+ cd "$D" &&
+ rm -fr w &&
+ git clone -l --bare x w &&
+ cd w &&
+ copied=$(find objects -type f -links 1 | wc -l) &&
+ test 0 = $copied
+'
+
+test_expect_success 'local clone of repo with nonexistent ref in HEAD' '
+ cd "$D" &&
+ echo "ref: refs/heads/nonexistent" > a.git/HEAD &&
+ git clone a d &&
+ cd d &&
+ git fetch &&
+ test ! -e .git/refs/remotes/origin/HEAD'
+
+test_expect_success 'bundle clone without .bundle suffix' '
+ cd "$D" &&
+ git clone dir/b3 &&
+ cd b3 &&
+ git fetch
+'
+
+test_expect_success 'bundle clone with .bundle suffix' '
+ cd "$D" &&
+ git clone b1.bundle &&
+ cd b1 &&
+ git fetch
+'
+
+test_expect_success 'bundle clone from b4' '
+ cd "$D" &&
+ git clone b4 bdl &&
+ cd bdl &&
+ git fetch
+'
+
+test_expect_success 'bundle clone from b4.bundle that does not exist' '
+ cd "$D" &&
+ if git clone b4.bundle bb
+ then
+ echo "Oops, should have failed"
+ false
+ else
+ echo happy
+ fi
+'
+
+test_expect_success 'bundle clone with nonexistent HEAD' '
+ cd "$D" &&
+ git clone b2.bundle b2 &&
+ cd b2 &&
+ git fetch
+ test ! -e .git/refs/heads/master
+'
+
+test_expect_success 'clone empty repository' '
+ cd "$D" &&
+ mkdir empty &&
+ (cd empty &&
+ git init &&
+ git config receive.denyCurrentBranch warn) &&
+ git clone empty empty-clone &&
+ test_tick &&
+ (cd empty-clone
+ echo "content" >> foo &&
+ git add foo &&
+ git commit -m "Initial commit" &&
+ git push origin master &&
+ expected=$(git rev-parse master) &&
+ actual=$(git --git-dir=../empty/.git rev-parse master) &&
+ test $actual = $expected)
+'
+
+test_expect_success 'clone empty repository, and then push should not segfault.' '
+ cd "$D" &&
+ rm -fr empty/ empty-clone/ &&
+ mkdir empty &&
+ (cd empty && git init) &&
+ git clone empty empty-clone &&
+ (cd empty-clone &&
+ test_must_fail git push)
+'
+
+test_done
diff --git a/t/t5702-clone-options.sh b/t/t5702-clone-options.sh
new file mode 100755
index 0000000..02cb024
--- /dev/null
+++ b/t/t5702-clone-options.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='basic clone options'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ mkdir parent &&
+ (cd parent && git init &&
+ echo one >file && git add file &&
+ git commit -m one)
+
+'
+
+test_expect_success 'clone -o' '
+
+ git clone -o foo parent clone-o &&
+ (cd clone-o && git rev-parse --verify refs/remotes/foo/master)
+
+'
+
+test_expect_success 'redirected clone' '
+
+ git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
+ test ! -s err
+
+'
+test_expect_success 'redirected clone -v' '
+
+ git clone --progress "file://$(pwd)/parent" clone-redirected-progress \
+ >out 2>err &&
+ test -s err
+
+'
+
+test_done
diff --git a/t/t5704-bundle.sh b/t/t5704-bundle.sh
new file mode 100755
index 0000000..ddc3dc5
--- /dev/null
+++ b/t/t5704-bundle.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='some bundle related tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ : > file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ test_tick &&
+ git tag -m tag tag &&
+ : > file2 &&
+ git add file2 &&
+ : > file3 &&
+ test_tick &&
+ git commit -m second &&
+ git add file3 &&
+ test_tick &&
+ git commit -m third
+
+'
+
+test_expect_success 'tags can be excluded by rev-list options' '
+
+ git bundle create bundle --all --since=7.Apr.2005.15:16:00.-0700 &&
+ git ls-remote bundle > output &&
+ ! grep tag output
+
+'
+
+test_expect_failure 'bundle --stdin' '
+
+ echo master | git bundle create stdin-bundle.bdl --stdin &&
+ git ls-remote stdin-bundle.bdl >output &&
+ grep master output
+
+'
+
+test_expect_failure 'bundle --stdin <rev-list options>' '
+
+ echo master | git bundle create hybrid-bundle.bdl --stdin tag &&
+ git ls-remote hybrid-bundle.bdl >output &&
+ grep master output
+
+'
+
+test_done
diff --git a/t/t5705-clone-2gb.sh b/t/t5705-clone-2gb.sh
new file mode 100755
index 0000000..e4d1b6a
--- /dev/null
+++ b/t/t5705-clone-2gb.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='Test cloning a repository larger than 2 gigabyte'
+. ./test-lib.sh
+
+test -z "$GIT_TEST_CLONE_2GB" &&
+skip_all="Skipping expensive 2GB clone test; enable it with GIT_TEST_CLONE_2GB=t" &&
+test_done &&
+exit
+
+test_expect_success 'setup' '
+
+ git config pack.compression 0 &&
+ git config pack.depth 0 &&
+ blobsize=$((100*1024*1024)) &&
+ blobcount=$((2*1024*1024*1024/$blobsize+1)) &&
+ i=1 &&
+ (while test $i -le $blobcount
+ do
+ printf "Generating blob $i/$blobcount\r" >&2 &&
+ printf "blob\nmark :$i\ndata $blobsize\n" &&
+ #test-genrandom $i $blobsize &&
+ printf "%-${blobsize}s" $i &&
+ echo "M 100644 :$i $i" >> commit
+ i=$(($i+1)) ||
+ echo $? > exit-status
+ done &&
+ echo "commit refs/heads/master" &&
+ echo "author A U Thor <author@email.com> 123456789 +0000" &&
+ echo "committer C O Mitter <committer@email.com> 123456789 +0000" &&
+ echo "data 5" &&
+ echo ">2gb" &&
+ cat commit) |
+ git fast-import --big-file-threshold=2 &&
+ test ! -f exit-status
+
+'
+
+test_expect_success 'clone - bare' '
+
+ git clone --bare --no-hardlinks . clone-bare
+
+'
+
+test_expect_success 'clone - with worktree, file:// protocol' '
+
+ git clone file://. clone-wt
+
+'
+
+test_done
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
new file mode 100755
index 0000000..f3f9a76
--- /dev/null
+++ b/t/t5706-clone-branch.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+test_description='clone --branch option'
+. ./test-lib.sh
+
+check_HEAD() {
+ echo refs/heads/"$1" >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+}
+
+check_file() {
+ echo "$1" >expect &&
+ test_cmp expect file
+}
+
+test_expect_success 'setup' '
+ mkdir parent &&
+ (cd parent && git init &&
+ echo one >file && git add file && git commit -m one &&
+ git checkout -b two &&
+ echo two >file && git add file && git commit -m two &&
+ git checkout master)
+'
+
+test_expect_success 'vanilla clone chooses HEAD' '
+ git clone parent clone &&
+ (cd clone &&
+ check_HEAD master &&
+ check_file one
+ )
+'
+
+test_expect_success 'clone -b chooses specified branch' '
+ git clone -b two parent clone-two &&
+ (cd clone-two &&
+ check_HEAD two &&
+ check_file two
+ )
+'
+
+test_expect_success 'clone -b sets up tracking' '
+ (cd clone-two &&
+ echo origin >expect &&
+ git config branch.two.remote >actual &&
+ echo refs/heads/two >>expect &&
+ git config branch.two.merge >>actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
+ (cd clone-two &&
+ echo refs/remotes/origin/master >expect &&
+ git symbolic-ref refs/remotes/origin/HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'clone -b with bogus branch chooses HEAD' '
+ git clone -b bogus parent clone-bogus &&
+ (cd clone-bogus &&
+ check_HEAD master &&
+ check_file one
+ )
+'
+
+test_done
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
new file mode 100755
index 0000000..ef7127c
--- /dev/null
+++ b/t/t5710-info-alternate.sh
@@ -0,0 +1,112 @@
+#!/bin/sh
+#
+# Copyright (C) 2006 Martin Waitz <tali@admingilde.org>
+#
+
+test_description='test transitive info/alternate entries'
+. ./test-lib.sh
+
+# test that a file is not reachable in the current repository
+# but that it is after creating a info/alternate entry
+reachable_via() {
+ alternate="$1"
+ file="$2"
+ if git cat-file -e "HEAD:$file"; then return 1; fi
+ echo "$alternate" >> .git/objects/info/alternate
+ git cat-file -e "HEAD:$file"
+}
+
+test_valid_repo() {
+ git fsck --full > fsck.log &&
+ test `wc -l < fsck.log` = 0
+}
+
+base_dir=`pwd`
+
+test_expect_success 'preparing first repository' \
+'test_create_repo A && cd A &&
+echo "Hello World" > file1 &&
+git add file1 &&
+git commit -m "Initial commit" file1 &&
+git repack -a -d &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'preparing second repository' \
+'git clone -l -s A B && cd B &&
+echo "foo bar" > file2 &&
+git add file2 &&
+git commit -m "next commit" file2 &&
+git repack -a -d -l &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_success 'preparing third repository' \
+'git clone -l -s B C && cd C &&
+echo "Goodbye, cruel world" > file3 &&
+git add file3 &&
+git commit -m "one more" file3 &&
+git repack -a -d -l &&
+git prune'
+
+cd "$base_dir"
+
+test_expect_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 -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 'validity of third repository' \
+'cd C &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'validity of fourth repository' \
+'cd D &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'breaking of loops' \
+'echo "$base_dir"/B/.git/objects >> "$base_dir"/A/.git/objects/info/alternates&&
+cd C &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success 'that info/alternates is necessary' \
+'cd C &&
+rm -f .git/objects/info/alternates &&
+! (test_valid_repo)'
+
+cd "$base_dir"
+
+test_expect_success 'that relative alternate is possible for current dir' \
+'cd C &&
+echo "../../../B/.git/objects" > .git/objects/info/alternates &&
+test_valid_repo'
+
+cd "$base_dir"
+
+test_expect_success \
+ 'that relative alternate is only possible for current dir' '
+ cd D &&
+ ! (test_valid_repo)
+'
+
+cd "$base_dir"
+
+test_done
diff --git a/t/t5800-remote-helpers.sh b/t/t5800-remote-helpers.sh
new file mode 100755
index 0000000..637d8e9
--- /dev/null
+++ b/t/t5800-remote-helpers.sh
@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Sverre Rabbelier
+#
+
+test_description='Test remote-helper import and export commands'
+
+. ./test-lib.sh
+
+if test_have_prereq PYTHON && "$PYTHON_PATH" -c '
+import sys
+if sys.hexversion < 0x02040000:
+ sys.exit(1)
+'
+then
+ :
+else
+ skip_all='skipping git remote-testgit tests: requires Python 2.4 or newer'
+ test_done
+fi
+
+test_expect_success 'setup repository' '
+ git init --bare server/.git &&
+ git clone server public &&
+ (cd public &&
+ echo content >file &&
+ git add file &&
+ git commit -m one &&
+ git push origin master)
+'
+
+test_expect_success 'cloning from local repo' '
+ git clone "testgit::${PWD}/server" localclone &&
+ test_cmp public/file localclone/file
+'
+
+test_expect_success 'cloning from remote repo' '
+ git clone "testgit::file://${PWD}/server" clone &&
+ test_cmp public/file clone/file
+'
+
+test_expect_success 'create new commit on remote' '
+ (cd public &&
+ echo content >>file &&
+ git commit -a -m two &&
+ git push)
+'
+
+test_expect_success 'pulling from local repo' '
+ (cd localclone && git pull) &&
+ test_cmp public/file localclone/file
+'
+
+test_expect_success 'pulling from remote remote' '
+ (cd clone && git pull) &&
+ test_cmp public/file clone/file
+'
+
+test_expect_success 'pushing to local repo' '
+ (cd localclone &&
+ echo content >>file &&
+ git commit -a -m three &&
+ git push) &&
+ HEAD=$(git --git-dir=localclone/.git rev-parse --verify HEAD) &&
+ test $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)
+'
+
+test_expect_success 'synch with changes from localclone' '
+ (cd clone &&
+ git pull)
+'
+
+test_expect_success 'pushing remote local repo' '
+ (cd clone &&
+ echo content >>file &&
+ git commit -a -m four &&
+ git push) &&
+ HEAD=$(git --git-dir=clone/.git rev-parse --verify HEAD) &&
+ test $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)
+'
+
+test_done
diff --git a/t/t6001-rev-list-graft.sh b/t/t6001-rev-list-graft.sh
new file mode 100755
index 0000000..fc57e7d
--- /dev/null
+++ b/t/t6001-rev-list-graft.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+test_description='Revision traversal vs grafts and path limiter'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir subdir &&
+ echo >fileA fileA &&
+ echo >subdir/fileB fileB &&
+ git add fileA subdir/fileB &&
+ git commit -a -m "Initial in one history." &&
+ A0=`git rev-parse --verify HEAD` &&
+
+ echo >fileA fileA modified &&
+ git commit -a -m "Second in one history." &&
+ A1=`git rev-parse --verify HEAD` &&
+
+ echo >subdir/fileB fileB modified &&
+ git commit -a -m "Third in one history." &&
+ A2=`git rev-parse --verify HEAD` &&
+
+ rm -f .git/refs/heads/master .git/index &&
+
+ echo >fileA fileA again &&
+ echo >subdir/fileB fileB again &&
+ git add fileA subdir/fileB &&
+ git commit -a -m "Initial in alternate history." &&
+ B0=`git rev-parse --verify HEAD` &&
+
+ echo >fileA fileA modified in alternate history &&
+ git commit -a -m "Second in alternate history." &&
+ B1=`git rev-parse --verify HEAD` &&
+
+ echo >subdir/fileB fileB modified in alternate history &&
+ git commit -a -m "Third in alternate history." &&
+ B2=`git rev-parse --verify HEAD` &&
+ : done
+'
+
+check () {
+ type=$1
+ shift
+
+ arg=
+ which=arg
+ rm -f test.expect
+ for a
+ do
+ if test "z$a" = z--
+ then
+ which=expect
+ child=
+ continue
+ fi
+ if test "$which" = arg
+ then
+ arg="$arg$a "
+ continue
+ fi
+ if test "$type" = basic
+ then
+ echo "$a"
+ else
+ if test "z$child" != z
+ then
+ echo "$child $a"
+ fi
+ child="$a"
+ fi
+ done >test.expect
+ if test "$type" != basic && test "z$child" != z
+ then
+ echo >>test.expect $child
+ fi
+ if test $type = basic
+ then
+ git rev-list $arg >test.actual
+ elif test $type = parents
+ then
+ git rev-list --parents $arg >test.actual
+ elif test $type = parents-raw
+ then
+ git rev-list --parents --pretty=raw $arg |
+ sed -n -e 's/^commit //p' >test.actual
+ fi
+ test_cmp test.expect test.actual
+}
+
+for type in basic parents parents-raw
+do
+ test_expect_success 'without grafts' "
+ rm -f .git/info/grafts
+ check $type $B2 -- $B2 $B1 $B0
+ "
+
+ test_expect_success 'with grafts' "
+ echo '$B0 $A2' >.git/info/grafts
+ check $type $B2 -- $B2 $B1 $B0 $A2 $A1 $A0
+ "
+
+ test_expect_success 'without grafts, with pathlimit' "
+ rm -f .git/info/grafts
+ check $type $B2 subdir -- $B2 $B0
+ "
+
+ test_expect_success 'with grafts, with pathlimit' "
+ echo '$B0 $A2' >.git/info/grafts
+ check $type $B2 subdir -- $B2 $B0 $A2 $A0
+ "
+
+done
+test_done
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
new file mode 100755
index 0000000..fb07536
--- /dev/null
+++ b/t/t6002-rev-list-bisect.sh
@@ -0,0 +1,238 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Jon Seymour
+#
+test_description='Tests git rev-list --bisect functionality'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
+
+# usage: test_bisection max-diff bisect-option head ^prune...
+#
+# e.g. test_bisection 1 --bisect l1 ^l0
+#
+test_bisection_diff()
+{
+ _max_diff=$1
+ _bisect_option=$2
+ shift 2
+ _bisection=$(git rev-list $_bisect_option "$@")
+ _list_size=$(git rev-list "$@" | wc -l)
+ _head=$1
+ shift 1
+ _bisection_size=$(git rev-list $_bisection "$@" | wc -l)
+ [ -n "$_list_size" -a -n "$_bisection_size" ] ||
+ error "test_bisection_diff failed"
+
+ # Test if bisection size is close to half of list size within
+ # tolerance.
+ #
+ _bisect_err=`expr $_list_size - $_bisection_size \* 2`
+ test "$_bisect_err" -lt 0 && _bisect_err=`expr 0 - $_bisect_err`
+ _bisect_err=`expr $_bisect_err / 2` ; # floor
+
+ test_expect_success \
+ "bisection diff $_bisect_option $_head $* <= $_max_diff" \
+ 'test $_bisect_err -le $_max_diff'
+}
+
+date >path0
+git update-index --add path0
+save_tag tree git write-tree
+on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
+on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
+on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
+on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
+on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
+on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
+on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
+on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
+on_committer_date "1971-08-16 00:00:08" save_tag b2 unique_commit b2 tree -p b1
+on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b2 tree -p b2
+on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
+on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
+on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
+on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
+on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
+on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
+git update-ref HEAD $(tag l5)
+
+
+# E
+# / \
+# e1 |
+# | |
+# e2 |
+# | |
+# e3 |
+# | |
+# e4 |
+# | |
+# | f1
+# | |
+# | f2
+# | |
+# | f3
+# | |
+# | f4
+# | |
+# e5 |
+# | |
+# e6 |
+# | |
+# e7 |
+# | |
+# e8 |
+# \ /
+# F
+
+
+on_committer_date "1971-08-16 00:00:00" hide_error save_tag F unique_commit F tree
+on_committer_date "1971-08-16 00:00:01" save_tag e8 unique_commit e8 tree -p F
+on_committer_date "1971-08-16 00:00:02" save_tag e7 unique_commit e7 tree -p e8
+on_committer_date "1971-08-16 00:00:03" save_tag e6 unique_commit e6 tree -p e7
+on_committer_date "1971-08-16 00:00:04" save_tag e5 unique_commit e5 tree -p e6
+on_committer_date "1971-08-16 00:00:05" save_tag f4 unique_commit f4 tree -p F
+on_committer_date "1971-08-16 00:00:06" save_tag f3 unique_commit f3 tree -p f4
+on_committer_date "1971-08-16 00:00:07" save_tag f2 unique_commit f2 tree -p f3
+on_committer_date "1971-08-16 00:00:08" save_tag f1 unique_commit f1 tree -p f2
+on_committer_date "1971-08-16 00:00:09" save_tag e4 unique_commit e4 tree -p e5
+on_committer_date "1971-08-16 00:00:10" save_tag e3 unique_commit e3 tree -p e4
+on_committer_date "1971-08-16 00:00:11" save_tag e2 unique_commit e2 tree -p e3
+on_committer_date "1971-08-16 00:00:12" save_tag e1 unique_commit e1 tree -p e2
+on_committer_date "1971-08-16 00:00:13" save_tag E unique_commit E tree -p e1 -p f1
+
+on_committer_date "1971-08-16 00:00:00" hide_error save_tag U unique_commit U tree
+on_committer_date "1971-08-16 00:00:01" save_tag u0 unique_commit u0 tree -p U
+on_committer_date "1971-08-16 00:00:01" save_tag u1 unique_commit u1 tree -p u0
+on_committer_date "1971-08-16 00:00:02" save_tag u2 unique_commit u2 tree -p u0
+on_committer_date "1971-08-16 00:00:03" save_tag u3 unique_commit u3 tree -p u0
+on_committer_date "1971-08-16 00:00:04" save_tag u4 unique_commit u4 tree -p u0
+on_committer_date "1971-08-16 00:00:05" save_tag u5 unique_commit u5 tree -p u0
+on_committer_date "1971-08-16 00:00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
+
+test_sequence()
+{
+ _bisect_option=$1
+
+ test_bisection_diff 0 $_bisect_option l0 ^root
+ test_bisection_diff 0 $_bisect_option l1 ^root
+ test_bisection_diff 0 $_bisect_option l2 ^root
+ test_bisection_diff 0 $_bisect_option a0 ^root
+ test_bisection_diff 0 $_bisect_option a1 ^root
+ test_bisection_diff 0 $_bisect_option a2 ^root
+ test_bisection_diff 0 $_bisect_option a3 ^root
+ test_bisection_diff 0 $_bisect_option b1 ^root
+ test_bisection_diff 0 $_bisect_option b2 ^root
+ test_bisection_diff 0 $_bisect_option b3 ^root
+ test_bisection_diff 0 $_bisect_option c1 ^root
+ test_bisection_diff 0 $_bisect_option c2 ^root
+ test_bisection_diff 0 $_bisect_option c3 ^root
+ test_bisection_diff 0 $_bisect_option E ^F
+ test_bisection_diff 0 $_bisect_option e1 ^F
+ test_bisection_diff 0 $_bisect_option e2 ^F
+ test_bisection_diff 0 $_bisect_option e3 ^F
+ test_bisection_diff 0 $_bisect_option e4 ^F
+ test_bisection_diff 0 $_bisect_option e5 ^F
+ test_bisection_diff 0 $_bisect_option e6 ^F
+ test_bisection_diff 0 $_bisect_option e7 ^F
+ test_bisection_diff 0 $_bisect_option f1 ^F
+ test_bisection_diff 0 $_bisect_option f2 ^F
+ test_bisection_diff 0 $_bisect_option f3 ^F
+ test_bisection_diff 0 $_bisect_option f4 ^F
+ test_bisection_diff 0 $_bisect_option E ^F
+
+ test_bisection_diff 1 $_bisect_option V ^U
+ test_bisection_diff 0 $_bisect_option V ^U ^u1 ^u2 ^u3
+ test_bisection_diff 0 $_bisect_option u1 ^U
+ test_bisection_diff 0 $_bisect_option u2 ^U
+ test_bisection_diff 0 $_bisect_option u3 ^U
+ test_bisection_diff 0 $_bisect_option u4 ^U
+ test_bisection_diff 0 $_bisect_option u5 ^U
+
+#
+# the following illustrates Linus' binary bug blatt idea.
+#
+# assume the bug is actually at l3, but you don't know that - all you know is that l3 is broken
+# and it wasn't broken before
+#
+# keep bisecting the list, advancing the "bad" head and accumulating "good" heads until
+# the bisection point is the head - this is the bad point.
+#
+
+test_output_expect_success "$_bisect_option l5 ^root" 'git rev-list $_bisect_option l5 ^root' <<EOF
+c3
+EOF
+
+test_output_expect_success "$_bisect_option l5 ^root ^c3" 'git rev-list $_bisect_option l5 ^root ^c3' <<EOF
+b4
+EOF
+
+test_output_expect_success "$_bisect_option l5 ^root ^c3 ^b4" 'git rev-list $_bisect_option l5 ^c3 ^b4' <<EOF
+l3
+EOF
+
+test_output_expect_success "$_bisect_option l3 ^root ^c3 ^b4" 'git rev-list $_bisect_option l3 ^root ^c3 ^b4' <<EOF
+a4
+EOF
+
+test_output_expect_success "$_bisect_option l5 ^b3 ^a3 ^b4 ^a4" 'git rev-list $_bisect_option l3 ^b3 ^a3 ^a4' <<EOF
+l3
+EOF
+
+#
+# if l3 is bad, then l4 is bad too - so advance the bad pointer by making b4 the known bad head
+#
+
+test_output_expect_success "$_bisect_option l4 ^a2 ^a3 ^b ^a4" 'git rev-list $_bisect_option l4 ^a2 ^a3 ^a4' <<EOF
+l3
+EOF
+
+test_output_expect_success "$_bisect_option l3 ^a2 ^a3 ^b ^a4" 'git rev-list $_bisect_option l3 ^a2 ^a3 ^a4' <<EOF
+l3
+EOF
+
+# found!
+
+#
+# as another example, let's consider a4 to be the bad head, in which case
+#
+
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4" 'git rev-list $_bisect_option a4 ^a2 ^a3 ^b4' <<EOF
+c2
+EOF
+
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4 ^c2" 'git rev-list $_bisect_option a4 ^a2 ^a3 ^b4 ^c2' <<EOF
+c3
+EOF
+
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4 ^c2 ^c3" 'git rev-list $_bisect_option a4 ^a2 ^a3 ^b4 ^c2 ^c3' <<EOF
+a4
+EOF
+
+# found!
+
+#
+# or consider c3 to be the bad head
+#
+
+test_output_expect_success "$_bisect_option a4 ^a2 ^a3 ^b4" 'git rev-list $_bisect_option a4 ^a2 ^a3 ^b4' <<EOF
+c2
+EOF
+
+test_output_expect_success "$_bisect_option c3 ^a2 ^a3 ^b4 ^c2" 'git rev-list $_bisect_option c3 ^a2 ^a3 ^b4 ^c2' <<EOF
+c3
+EOF
+
+# found!
+
+}
+
+test_sequence "--bisect"
+
+#
+#
+test_done
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
new file mode 100755
index 0000000..e4c52b0
--- /dev/null
+++ b/t/t6003-rev-list-topo-order.sh
@@ -0,0 +1,408 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Jon Seymour
+#
+
+test_description='Tests git rev-list --topo-order functionality'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
+
+list_duplicates()
+{
+ "$@" | sort | uniq -d
+}
+
+date >path0
+git update-index --add path0
+save_tag tree git write-tree
+on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
+on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
+on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
+on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
+on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
+on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
+on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
+on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
+on_committer_date "1971-08-16 00:00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
+on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b3 tree -p b2
+on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
+on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
+on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
+on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
+on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
+on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
+on_committer_date "1971-08-16 00:00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
+on_committer_date "1971-08-16 00:00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
+on_committer_date "1971-08-16 00:00:21" hide_error save_tag alt_root unique_commit alt_root tree
+on_committer_date "1971-08-16 00:00:22" save_tag r0 unique_commit r0 tree -p alt_root
+on_committer_date "1971-08-16 00:00:23" save_tag r1 unique_commit r1 tree -p r0
+on_committer_date "1971-08-16 00:00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
+on_committer_date "1971-08-16 00:00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
+
+
+#
+# note: as of 20/6, it isn't possible to create duplicate parents, so this
+# can't be tested.
+#
+#on_committer_date "1971-08-16 00:00:20" save_tag m3 unique_commit m3 tree -p c3 -p a4 -p c3
+hide_error save_tag e1 as_author e@example.com unique_commit e1 tree
+save_tag e2 as_author e@example.com unique_commit e2 tree -p e1
+save_tag f1 as_author f@example.com unique_commit f1 tree -p e1
+save_tag e3 as_author e@example.com unique_commit e3 tree -p e2
+save_tag f2 as_author f@example.com unique_commit f2 tree -p f1
+save_tag e4 as_author e@example.com unique_commit e4 tree -p e3 -p f2
+save_tag e5 as_author e@example.com unique_commit e5 tree -p e4
+save_tag f3 as_author f@example.com unique_commit f3 tree -p f2
+save_tag f4 as_author f@example.com unique_commit f4 tree -p f3
+save_tag e6 as_author e@example.com unique_commit e6 tree -p e5 -p f4
+save_tag f5 as_author f@example.com unique_commit f5 tree -p f4
+save_tag f6 as_author f@example.com unique_commit f6 tree -p f5 -p e6
+save_tag e7 as_author e@example.com unique_commit e7 tree -p e6
+save_tag e8 as_author e@example.com unique_commit e8 tree -p e7
+save_tag e9 as_author e@example.com unique_commit e9 tree -p e8
+save_tag f7 as_author f@example.com unique_commit f7 tree -p f6
+save_tag f8 as_author f@example.com unique_commit f8 tree -p f7
+save_tag f9 as_author f@example.com unique_commit f9 tree -p f8
+save_tag e10 as_author e@example.com unique_commit e1 tree -p e9 -p f8
+
+hide_error save_tag g0 unique_commit g0 tree
+save_tag g1 unique_commit g1 tree -p g0
+save_tag h1 unique_commit g2 tree -p g0
+save_tag g2 unique_commit g3 tree -p g1 -p h1
+save_tag h2 unique_commit g4 tree -p g2
+save_tag g3 unique_commit g5 tree -p g2
+save_tag g4 unique_commit g6 tree -p g3 -p h2
+
+git update-ref HEAD $(tag l5)
+
+test_output_expect_success 'rev-list has correct number of entries' 'git rev-list HEAD | wc -l | tr -d \" \"' <<EOF
+19
+EOF
+
+test_output_expect_success 'simple topo order' 'git rev-list --topo-order HEAD' <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'two diamonds topo order (g6)' 'git rev-list --topo-order g4' <<EOF
+g4
+h2
+g3
+g2
+h1
+g1
+g0
+EOF
+
+test_output_expect_success 'multiple heads' 'git rev-list --topo-order a3 b3 c3' <<EOF
+a3
+a2
+a1
+c3
+c2
+c1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'multiple heads, prune at a1' 'git rev-list --topo-order a3 b3 c3 ^a1' <<EOF
+a3
+a2
+c3
+c2
+c1
+b3
+b2
+b1
+EOF
+
+test_output_expect_success 'multiple heads, prune at l1' 'git rev-list --topo-order a3 b3 c3 ^l1' <<EOF
+a3
+a2
+a1
+c3
+c2
+c1
+b3
+b2
+b1
+a0
+l2
+EOF
+
+test_output_expect_success 'cross-epoch, head at l5, prune at l1' 'git rev-list --topo-order l5 ^l1' <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+EOF
+
+test_output_expect_success 'duplicated head arguments' 'git rev-list --topo-order l5 l5 ^l1' <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+EOF
+
+test_output_expect_success 'prune near topo' 'git rev-list --topo-order a4 ^c3' <<EOF
+a4
+b4
+a3
+a2
+a1
+b3
+EOF
+
+test_output_expect_success "head has no parent" 'git rev-list --topo-order root' <<EOF
+root
+EOF
+
+test_output_expect_success "two nodes - one head, one base" 'git rev-list --topo-order l0' <<EOF
+l0
+root
+EOF
+
+test_output_expect_success "three nodes one head, one internal, one base" 'git rev-list --topo-order l1' <<EOF
+l1
+l0
+root
+EOF
+
+test_output_expect_success "linear prune l2 ^root" 'git rev-list --topo-order l2 ^root' <<EOF
+l2
+l1
+l0
+EOF
+
+test_output_expect_success "linear prune l2 ^l0" 'git rev-list --topo-order l2 ^l0' <<EOF
+l2
+l1
+EOF
+
+test_output_expect_success "linear prune l2 ^l1" 'git rev-list --topo-order l2 ^l1' <<EOF
+l2
+EOF
+
+test_output_expect_success "linear prune l5 ^a4" 'git rev-list --topo-order l5 ^a4' <<EOF
+l5
+l4
+l3
+EOF
+
+test_output_expect_success "linear prune l5 ^l3" 'git rev-list --topo-order l5 ^l3' <<EOF
+l5
+l4
+EOF
+
+test_output_expect_success "linear prune l5 ^l4" 'git rev-list --topo-order l5 ^l4' <<EOF
+l5
+EOF
+
+test_output_expect_success "max-count 10 - topo order" 'git rev-list --topo-order --max-count=10 l5' <<EOF
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+EOF
+
+test_output_expect_success "max-count 10 - non topo order" 'git rev-list --max-count=10 l5' <<EOF
+l5
+l4
+l3
+a4
+b4
+a3
+a2
+c3
+c2
+b3
+EOF
+
+test_output_expect_success '--max-age=c3, no --topo-order' "git rev-list --max-age=$(commit_date c3) l5" <<EOF
+l5
+l4
+l3
+a4
+b4
+a3
+a2
+c3
+EOF
+
+#
+# this test fails on --topo-order - a fix is required
+#
+#test_output_expect_success '--max-age=c3, --topo-order' "git rev-list --topo-order --max-age=$(commit_date c3) l5" <<EOF
+#l5
+#l4
+#l3
+#a4
+#c3
+#b4
+#a3
+#a2
+#EOF
+
+test_output_expect_success 'one specified head reachable from another a4, c3, --topo-order' "list_duplicates git rev-list --topo-order a4 c3" <<EOF
+EOF
+
+test_output_expect_success 'one specified head reachable from another c3, a4, --topo-order' "list_duplicates git rev-list --topo-order c3 a4" <<EOF
+EOF
+
+test_output_expect_success 'one specified head reachable from another a4, c3, no --topo-order' "list_duplicates git rev-list a4 c3" <<EOF
+EOF
+
+test_output_expect_success 'one specified head reachable from another c3, a4, no --topo-order' "list_duplicates git rev-list c3 a4" <<EOF
+EOF
+
+test_output_expect_success 'graph with c3 and a4 parents of head' "list_duplicates git rev-list m1" <<EOF
+EOF
+
+test_output_expect_success 'graph with a4 and c3 parents of head' "list_duplicates git rev-list m2" <<EOF
+EOF
+
+test_expect_success "head ^head --topo-order" 'git rev-list --topo-order a3 ^a3' <<EOF
+EOF
+
+test_expect_success "head ^head no --topo-order" 'git rev-list a3 ^a3' <<EOF
+EOF
+
+test_output_expect_success 'simple topo order (l5r1)' 'git rev-list --topo-order l5r1' <<EOF
+l5r1
+r1
+r0
+alt_root
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'simple topo order (r1l5)' 'git rev-list --topo-order r1l5' <<EOF
+r1l5
+l5
+l4
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+r1
+r0
+alt_root
+EOF
+
+test_output_expect_success "don't print things unreachable from one branch" "git rev-list a3 ^b3 --topo-order" <<EOF
+a3
+a2
+a1
+EOF
+
+test_output_expect_success "--topo-order a4 l3" "git rev-list --topo-order a4 l3" <<EOF
+l3
+a4
+c3
+c2
+c1
+b4
+a3
+a2
+a1
+b3
+b2
+b1
+a0
+l2
+l1
+l0
+root
+EOF
+
+#
+#
+
+test_done
diff --git a/t/t6004-rev-list-path-optim.sh b/t/t6004-rev-list-path-optim.sh
new file mode 100755
index 0000000..5dabf1c
--- /dev/null
+++ b/t/t6004-rev-list-path-optim.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='git rev-list trivial path optimization test'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+echo Hello > a &&
+git add a &&
+git commit -m "Initial commit" a &&
+initial=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success path-optimization '
+ commit=$(echo "Unchanged tree" | git commit-tree "HEAD^{tree}" -p HEAD) &&
+ test $(git rev-list $commit | wc -l) = 2 &&
+ test $(git rev-list $commit -- . | wc -l) = 1
+'
+
+test_expect_success 'further setup' '
+ git checkout -b side &&
+ echo Irrelevant >c &&
+ git add c &&
+ git commit -m "Side makes an irrelevant commit" &&
+ echo "More Irrelevancy" >c &&
+ git add c &&
+ git commit -m "Side makes another irrelevant commit" &&
+ echo Bye >a &&
+ git add a &&
+ git commit -m "Side touches a" &&
+ side=$(git rev-parse --verify HEAD) &&
+ echo "Yet more Irrelevancy" >c &&
+ git add c &&
+ git commit -m "Side makes yet another irrelevant commit" &&
+ git checkout master &&
+ echo Another >b &&
+ git add b &&
+ git commit -m "Master touches b" &&
+ git merge side &&
+ echo Touched >b &&
+ git add b &&
+ git commit -m "Master touches b again"
+'
+
+test_expect_success 'path optimization 2' '
+ ( echo "$side"; echo "$initial" ) >expected &&
+ git rev-list HEAD -- a >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
new file mode 100755
index 0000000..0b64822
--- /dev/null
+++ b/t/t6005-rev-list-count.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='git rev-list --max-count and --skip test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ for n in 1 2 3 4 5 ; do \
+ echo $n > a ; \
+ git add a ; \
+ git commit -m "$n" ; \
+ done
+'
+
+test_expect_success 'no options' '
+ test $(git rev-list HEAD | wc -l) = 5
+'
+
+test_expect_success '--max-count' '
+ test $(git rev-list HEAD --max-count=0 | wc -l) = 0 &&
+ test $(git rev-list HEAD --max-count=3 | wc -l) = 3 &&
+ test $(git rev-list HEAD --max-count=5 | wc -l) = 5 &&
+ test $(git rev-list HEAD --max-count=10 | wc -l) = 5
+'
+
+test_expect_success '--max-count all forms' '
+ test $(git rev-list HEAD --max-count=1 | wc -l) = 1 &&
+ test $(git rev-list HEAD -1 | wc -l) = 1 &&
+ test $(git rev-list HEAD -n1 | wc -l) = 1 &&
+ test $(git rev-list HEAD -n 1 | wc -l) = 1
+'
+
+test_expect_success '--skip' '
+ test $(git rev-list HEAD --skip=0 | wc -l) = 5 &&
+ test $(git rev-list HEAD --skip=3 | wc -l) = 2 &&
+ test $(git rev-list HEAD --skip=5 | wc -l) = 0 &&
+ test $(git rev-list HEAD --skip=10 | wc -l) = 0
+'
+
+test_expect_success '--skip --max-count' '
+ test $(git rev-list HEAD --skip=0 --max-count=0 | wc -l) = 0 &&
+ test $(git rev-list HEAD --skip=0 --max-count=10 | wc -l) = 5 &&
+ test $(git rev-list HEAD --skip=3 --max-count=0 | wc -l) = 0 &&
+ test $(git rev-list HEAD --skip=3 --max-count=1 | wc -l) = 1 &&
+ test $(git rev-list HEAD --skip=3 --max-count=2 | wc -l) = 2 &&
+ test $(git rev-list HEAD --skip=3 --max-count=10 | wc -l) = 2 &&
+ test $(git rev-list HEAD --skip=5 --max-count=10 | wc -l) = 0 &&
+ test $(git rev-list HEAD --skip=10 --max-count=10 | wc -l) = 0
+'
+
+test_done
diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh
new file mode 100755
index 0000000..cccacd4
--- /dev/null
+++ b/t/t6006-rev-list-format.sh
@@ -0,0 +1,271 @@
+#!/bin/sh
+
+test_description='git rev-list --pretty=format test'
+
+. ./test-lib.sh
+
+test_tick
+test_expect_success 'setup' '
+touch foo && git add foo && git commit -m "added foo" &&
+ echo changed >foo && git commit -a -m "changed foo"
+'
+
+# usage: test_format name format_string <expected_output
+test_format() {
+ cat >expect.$1
+ test_expect_success "format $1" "
+git rev-list --pretty=format:'$2' master >output.$1 &&
+test_cmp expect.$1 output.$1
+"
+}
+
+test_format percent %%h <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+%h
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+%h
+EOF
+
+test_format hash %H%n%h <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+131a310eb913d107dd3c09a65d1651175898735d
+131a310
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+86c75cf
+EOF
+
+test_format tree %T%n%t <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+fe722612f26da5064c32ca3843aa154bdb0b08a0
+fe72261
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+4d5fcadc293a348e88f777dc0920f11e7d71441c
+4d5fcad
+EOF
+
+test_format parents %P%n%p <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+86c75cf
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+
+
+EOF
+
+# we don't test relative here
+test_format author %an%n%ae%n%ad%n%aD%n%at <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+A U Thor
+author@example.com
+Thu Apr 7 15:13:13 2005 -0700
+Thu, 7 Apr 2005 15:13:13 -0700
+1112911993
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+A U Thor
+author@example.com
+Thu Apr 7 15:13:13 2005 -0700
+Thu, 7 Apr 2005 15:13:13 -0700
+1112911993
+EOF
+
+test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+C O Mitter
+committer@example.com
+Thu Apr 7 15:13:13 2005 -0700
+Thu, 7 Apr 2005 15:13:13 -0700
+1112911993
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+C O Mitter
+committer@example.com
+Thu Apr 7 15:13:13 2005 -0700
+Thu, 7 Apr 2005 15:13:13 -0700
+1112911993
+EOF
+
+test_format encoding %e <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+EOF
+
+test_format subject %s <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+changed foo
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+added foo
+EOF
+
+test_format body %b <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+EOF
+
+test_format raw-body %B <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+changed foo
+
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+added foo
+
+EOF
+
+test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+foobarbazxyzzy
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+foobarbazxyzzy
+EOF
+
+test_format advanced-colors '%C(red yellow bold)foo%C(reset)' <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+foo
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+foo
+EOF
+
+cat >commit-msg <<'EOF'
+Test printing of complex bodies
+
+This commit message is much longer than the others,
+and it will be encoded in iso8859-1. We should therefore
+include an iso8859 character: ¡bueno!
+EOF
+test_expect_success 'setup complex body' '
+git config i18n.commitencoding iso8859-1 &&
+ echo change2 >foo && git commit -a -F commit-msg
+'
+
+test_format complex-encoding %e <<'EOF'
+commit f58db70b055c5718631e5c61528b28b12090cdea
+iso8859-1
+commit 131a310eb913d107dd3c09a65d1651175898735d
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+EOF
+
+test_format complex-subject %s <<'EOF'
+commit f58db70b055c5718631e5c61528b28b12090cdea
+Test printing of complex bodies
+commit 131a310eb913d107dd3c09a65d1651175898735d
+changed foo
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+added foo
+EOF
+
+test_format complex-body %b <<'EOF'
+commit f58db70b055c5718631e5c61528b28b12090cdea
+This commit message is much longer than the others,
+and it will be encoded in iso8859-1. We should therefore
+include an iso8859 character: ¡bueno!
+
+commit 131a310eb913d107dd3c09a65d1651175898735d
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+EOF
+
+test_expect_success '%ad respects --date=' '
+ echo 2005-04-07 >expect.ad-short &&
+ git log -1 --date=short --pretty=tformat:%ad >output.ad-short master &&
+ test_cmp expect.ad-short output.ad-short
+'
+
+test_expect_success 'empty email' '
+ test_tick &&
+ C=$(GIT_AUTHOR_EMAIL= git commit-tree HEAD^{tree} </dev/null) &&
+ A=$(git show --pretty=format:%an,%ae,%ad%n -s $C) &&
+ test "$A" = "A U Thor,,Thu Apr 7 15:14:13 2005 -0700" || {
+ echo "Eh? $A" >failure
+ false
+ }
+'
+
+test_expect_success 'del LF before empty (1)' '
+ git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD^^ >actual &&
+ test $(wc -l <actual) = 2
+'
+
+test_expect_success 'del LF before empty (2)' '
+ git show -s --pretty=format:"%s%n%-b%nThanks%n" HEAD >actual &&
+ test $(wc -l <actual) = 6 &&
+ grep "^$" actual
+'
+
+test_expect_success 'add LF before non-empty (1)' '
+ git show -s --pretty=format:"%s%+b%nThanks%n" HEAD^^ >actual &&
+ test $(wc -l <actual) = 2
+'
+
+test_expect_success 'add LF before non-empty (2)' '
+ git show -s --pretty=format:"%s%+b%nThanks%n" HEAD >actual &&
+ test $(wc -l <actual) = 6 &&
+ grep "^$" actual
+'
+
+test_expect_success 'add SP before non-empty (1)' '
+ git show -s --pretty=format:"%s% bThanks" HEAD^^ >actual &&
+ test $(wc -w <actual) = 2
+'
+
+test_expect_success 'add SP before non-empty (2)' '
+ git show -s --pretty=format:"%s% sThanks" HEAD^^ >actual &&
+ test $(wc -w <actual) = 4
+'
+
+test_expect_success '--abbrev' '
+ echo SHORT SHORT SHORT >expect2 &&
+ echo LONG LONG LONG >expect3 &&
+ git log -1 --format="%h %h %h" HEAD >actual1 &&
+ git log -1 --abbrev=5 --format="%h %h %h" HEAD >actual2 &&
+ git log -1 --abbrev=5 --format="%H %H %H" HEAD >actual3 &&
+ sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual2 >fuzzy2 &&
+ sed -e "s/$_x40/LONG/g" -e "s/$_x05/SHORT/g" <actual3 >fuzzy3 &&
+ test_cmp expect2 fuzzy2 &&
+ test_cmp expect3 fuzzy3 &&
+ ! test_cmp actual1 actual2
+'
+
+test_expect_success '%H is not affected by --abbrev-commit' '
+ git log -1 --format=%H --abbrev-commit --abbrev=20 HEAD >actual &&
+ len=$(wc -c <actual) &&
+ test $len = 41
+'
+
+test_expect_success '%h is not affected by --abbrev-commit' '
+ git log -1 --format=%h --abbrev-commit --abbrev=20 HEAD >actual &&
+ len=$(wc -c <actual) &&
+ test $len = 21
+'
+
+test_expect_success '"%h %gD: %gs" is same as git-reflog' '
+ git reflog >expect &&
+ git log -g --format="%h %gD: %gs" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"%h %gD: %gs" is same as git-reflog (with date)' '
+ git reflog --date=raw >expect &&
+ git log -g --format="%h %gD: %gs" --date=raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"%h %gD: %gs" is same as git-reflog (with --abbrev)' '
+ git reflog --abbrev=13 --date=raw >expect &&
+ git log -g --abbrev=13 --format="%h %gD: %gs" --date=raw >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '%gd shortens ref name' '
+ echo "master@{0}" >expect.gd-short &&
+ git log -g -1 --format=%gd refs/heads/master >actual.gd-short &&
+ test_cmp expect.gd-short actual.gd-short
+'
+
+test_expect_success 'oneline with empty message' '
+ git commit -m "dummy" --allow-empty &&
+ git commit -m "dummy" --allow-empty &&
+ git filter-branch --msg-filter "sed -e s/dummy//" HEAD^^.. &&
+ git rev-list --oneline HEAD >test.txt &&
+ test $(git rev-list --oneline HEAD | wc -l) -eq 5 &&
+ test $(git rev-list --oneline --graph HEAD | wc -l) -eq 5
+'
+
+test_done
diff --git a/t/t6007-rev-list-cherry-pick-file.sh b/t/t6007-rev-list-cherry-pick-file.sh
new file mode 100755
index 0000000..b565638
--- /dev/null
+++ b/t/t6007-rev-list-cherry-pick-file.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+test_description='test git rev-list --cherry-pick -- file'
+
+. ./test-lib.sh
+
+# A---B
+# \
+# \
+# C
+#
+# B changes a file foo.c, adding a line of text. C changes foo.c as
+# well as bar.c, but the change in foo.c was identical to change B.
+
+test_expect_success setup '
+ echo Hallo > foo &&
+ git add foo &&
+ test_tick &&
+ git commit -m "A" &&
+ git tag A &&
+ git checkout -b branch &&
+ echo Bello > foo &&
+ echo Cello > bar &&
+ git add foo bar &&
+ test_tick &&
+ git commit -m "C" &&
+ git tag C &&
+ git checkout master &&
+ git checkout branch foo &&
+ test_tick &&
+ git commit -m "B" &&
+ git tag B
+'
+
+cat >expect <<EOF
+<tags/B
+>tags/C
+EOF
+
+test_expect_success '--left-right' '
+ git rev-list --left-right B...C > actual &&
+ git name-rev --stdin --name-only --refs="*tags/*" \
+ < actual > actual.named &&
+ test_cmp actual.named expect
+'
+
+test_expect_success '--count' '
+ git rev-list --count B...C > actual &&
+ test "$(cat actual)" = 2
+'
+
+test_expect_success '--cherry-pick foo comes up empty' '
+ test -z "$(git rev-list --left-right --cherry-pick B...C -- foo)"
+'
+
+test_expect_success '--cherry-pick bar does not come up empty' '
+ ! test -z "$(git rev-list --left-right --cherry-pick B...C -- bar)"
+'
+
+test_expect_success '--cherry-pick with independent, but identical branches' '
+ git symbolic-ref HEAD refs/heads/independent &&
+ rm .git/index &&
+ echo Hallo > foo &&
+ git add foo &&
+ test_tick &&
+ git commit -m "independent" &&
+ echo Bello > foo &&
+ test_tick &&
+ git commit -m "independent, too" foo &&
+ test -z "$(git rev-list --left-right --cherry-pick \
+ HEAD...master -- foo)"
+'
+
+cat >expect <<EOF
+1 2
+EOF
+
+# Insert an extra commit to break the symmetry
+test_expect_success '--count --left-right' '
+ git checkout branch &&
+ test_commit D &&
+ git rev-list --count --left-right B...D > actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
new file mode 100755
index 0000000..c4af9ca
--- /dev/null
+++ b/t/t6008-rev-list-submodule.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='git rev-list involving submodules that this repo has'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ : > file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ echo 1 > file &&
+ test_tick &&
+ git commit -m second file &&
+ echo 2 > file &&
+ test_tick &&
+ git commit -m third file &&
+
+ rm .git/index &&
+
+ : > super-file &&
+ git add super-file &&
+ git submodule add "$(pwd)" sub &&
+ git symbolic-ref HEAD refs/heads/super &&
+ test_tick &&
+ git commit -m super-initial &&
+ echo 1 > super-file &&
+ test_tick &&
+ git commit -m super-first super-file &&
+ echo 2 > super-file &&
+ test_tick &&
+ git commit -m super-second super-file
+'
+
+test_expect_success "Ilari's test" '
+ git rev-list --objects super master ^super^
+'
+
+test_done
diff --git a/t/t6009-rev-list-parent.sh b/t/t6009-rev-list-parent.sh
new file mode 100755
index 0000000..c8a96a9
--- /dev/null
+++ b/t/t6009-rev-list-parent.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='properly cull all ancestors'
+
+. ./test-lib.sh
+
+commit () {
+ test_tick &&
+ echo $1 >file &&
+ git commit -a -m $1 &&
+ git tag $1
+}
+
+test_expect_success setup '
+
+ touch file &&
+ git add file &&
+
+ commit one &&
+
+ test_tick=$(($test_tick - 2400))
+
+ commit two &&
+ commit three &&
+ commit four &&
+
+ git log --pretty=oneline --abbrev-commit
+'
+
+test_expect_success 'one is ancestor of others and should not be shown' '
+
+ git rev-list one --not four >result &&
+ >expect &&
+ test_cmp expect result
+
+'
+
+test_done
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
new file mode 100755
index 0000000..0144d9e
--- /dev/null
+++ b/t/t6010-merge-base.sh
@@ -0,0 +1,177 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Merge base computation.
+'
+
+. ./test-lib.sh
+
+T=$(git write-tree)
+
+M=1130000000
+Z=+0000
+
+GIT_COMMITTER_EMAIL=git@comm.iter.xz
+GIT_COMMITTER_NAME='C O Mmiter'
+GIT_AUTHOR_NAME='A U Thor'
+GIT_AUTHOR_EMAIL=git@au.thor.xz
+export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
+
+doit() {
+ OFFSET=$1; shift
+ NAME=$1; shift
+ PARENTS=
+ for P
+ do
+ PARENTS="${PARENTS}-p $P "
+ done
+ GIT_COMMITTER_DATE="$(($M + $OFFSET)) $Z"
+ GIT_AUTHOR_DATE=$GIT_COMMITTER_DATE
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+ commit=$(echo $NAME | git commit-tree $T $PARENTS)
+ echo $commit >.git/refs/tags/$NAME
+ echo $commit
+}
+
+# E---D---C---B---A
+# \'-_ \ \
+# \ `---------G \
+# \ \
+# F----------------H
+
+# Setup...
+E=$(doit 5 E)
+D=$(doit 4 D $E)
+F=$(doit 6 F $E)
+C=$(doit 3 C $D)
+B=$(doit 2 B $C)
+A=$(doit 1 A $B)
+G=$(doit 7 G $B $E)
+H=$(doit 8 H $A $F)
+
+test_expect_success 'compute merge-base (single)' \
+ 'MB=$(git merge-base G H) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
+
+test_expect_success 'compute merge-base (all)' \
+ 'MB=$(git merge-base --all G H) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
+
+test_expect_success 'compute merge-base with show-branch' \
+ 'MB=$(git show-branch --merge-base G H) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/B"'
+
+# Setup for second test to demonstrate that relying on timestamps in a
+# distributed SCM to provide a _consistent_ partial ordering of commits
+# leads to insanity.
+#
+# Relative
+# Structure timestamps
+#
+# PL PR +4 +4
+# / \/ \ / \/ \
+# L2 C2 R2 +3 -1 +3
+# | | | | | |
+# L1 C1 R1 +2 -2 +2
+# | | | | | |
+# L0 C0 R0 +1 -3 +1
+# \ | / \ | /
+# S 0
+#
+# The left and right chains of commits can be of any length and complexity as
+# long as all of the timestamps are greater than that of S.
+
+S=$(doit 0 S)
+
+C0=$(doit -3 C0 $S)
+C1=$(doit -2 C1 $C0)
+C2=$(doit -1 C2 $C1)
+
+L0=$(doit 1 L0 $S)
+L1=$(doit 2 L1 $L0)
+L2=$(doit 3 L2 $L1)
+
+R0=$(doit 1 R0 $S)
+R1=$(doit 2 R1 $R0)
+R2=$(doit 3 R2 $R1)
+
+PL=$(doit 4 PL $L2 $C2)
+PR=$(doit 4 PR $C2 $R2)
+
+test_expect_success 'compute merge-base (single)' \
+ 'MB=$(git merge-base PL PR) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+
+test_expect_success 'compute merge-base (all)' \
+ 'MB=$(git merge-base --all PL PR) &&
+ expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+
+# Another set to demonstrate base between one commit and a merge
+# in the documentation.
+#
+# * C (MMC) * B (MMB) * A (MMA)
+# * o * o * o
+# * o * o * o
+# * o * o * o
+# * o | _______/
+# | |/
+# | * 1 (MM1)
+# | _______/
+# |/
+# * root (MMR)
+
+
+test_expect_success 'merge-base for octopus-step (setup)' '
+ test_tick && git commit --allow-empty -m root && git tag MMR &&
+ test_tick && git commit --allow-empty -m 1 && git tag MM1 &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m A && git tag MMA &&
+ git checkout MM1 &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m B && git tag MMB &&
+ git checkout MMR &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m C && git tag MMC
+'
+
+test_expect_success 'merge-base A B C' '
+ MB=$(git merge-base --all MMA MMB MMC) &&
+ MM1=$(git rev-parse --verify MM1) &&
+ test "$MM1" = "$MB"
+'
+
+test_expect_success 'merge-base A B C using show-branch' '
+ MB=$(git show-branch --merge-base MMA MMB MMC) &&
+ MMR=$(git rev-parse --verify MMR) &&
+ test "$MMR" = "$MB"
+'
+
+test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
+ git reset --hard MMR &&
+ test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
+ git reset --hard E &&
+ test_tick && git commit --allow-empty -m 2 && git tag CC2 &&
+ test_tick && git merge -s ours CC1 &&
+ test_tick && git commit --allow-empty -m o &&
+ test_tick && git commit --allow-empty -m B && git tag CCB &&
+ git reset --hard CC1 &&
+ test_tick && git merge -s ours CC2 &&
+ test_tick && git commit --allow-empty -m A && git tag CCA
+'
+
+test_expect_success 'merge-base B A^^ A^^2' '
+ MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) &&
+ MB1=$(git rev-parse CC1 CC2 | sort) &&
+ test "$MB0" = "$MB1"
+'
+
+test_done
diff --git a/t/t6011-rev-list-with-bad-commit.sh b/t/t6011-rev-list-with-bad-commit.sh
new file mode 100755
index 0000000..e51eb41
--- /dev/null
+++ b/t/t6011-rev-list-with-bad-commit.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='git rev-list should notice bad commits'
+
+. ./test-lib.sh
+
+# Note:
+# - compression level is set to zero to make "corruptions" easier to perform
+# - reflog is disabled to avoid extra references which would twart the test
+
+test_expect_success 'setup' \
+ '
+ git init &&
+ git config core.compression 0 &&
+ git config core.logallrefupdates false &&
+ echo "foo" > foo &&
+ git add foo &&
+ git commit -m "first commit" &&
+ echo "bar" > bar &&
+ git add bar &&
+ git commit -m "second commit" &&
+ echo "baz" > baz &&
+ git add baz &&
+ git commit -m "third commit" &&
+ echo "foo again" >> foo &&
+ git add foo &&
+ git commit -m "fourth commit" &&
+ git repack -a -f -d
+ '
+
+test_expect_success 'verify number of revisions' \
+ '
+ revs=$(git rev-list --all | wc -l) &&
+ test $revs -eq 4 &&
+ first_commit=$(git rev-parse HEAD~3)
+ '
+
+test_expect_success 'corrupt second commit object' \
+ '
+ perl -i.bak -pe "s/second commit/socond commit/" .git/objects/pack/*.pack &&
+ test_must_fail git fsck --full
+ '
+
+test_expect_success 'rev-list should fail' \
+ '
+ test_must_fail git rev-list --all > /dev/null
+ '
+
+test_expect_success 'git repack _MUST_ fail' \
+ '
+ test_must_fail git repack -a -f -d
+ '
+
+test_expect_success 'first commit is still available' \
+ '
+ git log $first_commit
+ '
+
+test_done
+
diff --git a/t/t6012-rev-list-simplify.sh b/t/t6012-rev-list-simplify.sh
new file mode 100755
index 0000000..af34a1e
--- /dev/null
+++ b/t/t6012-rev-list-simplify.sh
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='merge simplification'
+
+. ./test-lib.sh
+
+note () {
+ git tag "$1"
+}
+
+unnote () {
+ git name-rev --tags --stdin | sed -e "s|$_x40 (tags/\([^)]*\)) |\1 |g"
+}
+
+test_expect_success setup '
+ echo "Hi there" >file &&
+ git add file &&
+ test_tick && git commit -m "Initial file" &&
+ note A &&
+
+ git branch other-branch &&
+
+ echo "Hello" >file &&
+ git add file &&
+ test_tick && git commit -m "Modified file" &&
+ note B &&
+
+ git checkout other-branch &&
+
+ echo "Hello" >file &&
+ git add file &&
+ test_tick && git commit -m "Modified the file identically" &&
+ note C &&
+
+ echo "This is a stupid example" >another-file &&
+ git add another-file &&
+ test_tick && git commit -m "Add another file" &&
+ note D &&
+
+ test_tick && git merge -m "merge" master &&
+ note E &&
+
+ echo "Yet another" >elif &&
+ git add elif &&
+ test_tick && git commit -m "Irrelevant change" &&
+ note F &&
+
+ git checkout master &&
+ echo "Yet another" >elif &&
+ git add elif &&
+ test_tick && git commit -m "Another irrelevant change" &&
+ note G &&
+
+ test_tick && git merge -m "merge" other-branch &&
+ note H &&
+
+ echo "Final change" >file &&
+ test_tick && git commit -a -m "Final change" &&
+ note I
+'
+
+FMT='tformat:%P %H | %s'
+
+check_result () {
+ for c in $1
+ do
+ echo "$c"
+ done >expect &&
+ shift &&
+ param="$*" &&
+ test_expect_success "log $param" '
+ git log --pretty="$FMT" --parents $param |
+ unnote >actual &&
+ sed -e "s/^.* \([^ ]*\) .*/\1/" >check <actual &&
+ test_cmp expect check || {
+ cat actual
+ false
+ }
+ '
+}
+
+check_result 'I H G F E D C B A' --full-history
+check_result 'I H E C B A' --full-history -- file
+check_result 'I H E C B A' --full-history --topo-order -- file
+check_result 'I H E C B A' --full-history --date-order -- file
+check_result 'I E C B A' --simplify-merges -- file
+check_result 'I B A' -- file
+check_result 'I B A' --topo-order -- file
+
+test_done
diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh
new file mode 100755
index 0000000..59fc2f0
--- /dev/null
+++ b/t/t6013-rev-list-reverse-parents.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='--reverse combines with --parents'
+
+. ./test-lib.sh
+
+
+commit () {
+ test_tick &&
+ echo $1 > foo &&
+ git add foo &&
+ git commit -m "$1"
+}
+
+test_expect_success 'set up --reverse example' '
+ commit one &&
+ git tag root &&
+ commit two &&
+ git checkout -b side HEAD^ &&
+ commit three &&
+ git checkout master &&
+ git merge -s ours side &&
+ commit five
+ '
+
+test_expect_success '--reverse --parents --full-history combines correctly' '
+ git rev-list --parents --full-history master -- foo |
+ perl -e "print reverse <>" > expected &&
+ git rev-list --reverse --parents --full-history master -- foo \
+ > actual &&
+ test_cmp actual expected
+ '
+
+test_expect_success '--boundary does too' '
+ git rev-list --boundary --parents --full-history master ^root -- foo |
+ perl -e "print reverse <>" > expected &&
+ git rev-list --boundary --reverse --parents --full-history \
+ master ^root -- foo > actual &&
+ test_cmp actual expected
+ '
+
+test_done
diff --git a/t/t6014-rev-list-all.sh b/t/t6014-rev-list-all.sh
new file mode 100755
index 0000000..991ab4a
--- /dev/null
+++ b/t/t6014-rev-list-all.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='--all includes detached HEADs'
+
+. ./test-lib.sh
+
+
+commit () {
+ test_tick &&
+ echo $1 > foo &&
+ git add foo &&
+ git commit -m "$1"
+}
+
+test_expect_success 'setup' '
+
+ commit one &&
+ commit two &&
+ git checkout HEAD^ &&
+ commit detached
+
+'
+
+test_expect_success 'rev-list --all lists detached HEAD' '
+
+ test 3 = $(git rev-list --all | wc -l)
+
+'
+
+test_expect_success 'repack does not lose detached HEAD' '
+
+ git gc &&
+ git prune --expire=now &&
+ git show HEAD
+
+'
+
+test_done
diff --git a/t/t6015-rev-list-show-all-parents.sh b/t/t6015-rev-list-show-all-parents.sh
new file mode 100755
index 0000000..8b146fb
--- /dev/null
+++ b/t/t6015-rev-list-show-all-parents.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='--show-all --parents does not rewrite TREESAME commits'
+
+. ./test-lib.sh
+
+test_expect_success 'set up --show-all --parents test' '
+ test_commit one foo.txt &&
+ commit1=`git rev-list -1 HEAD` &&
+ test_commit two bar.txt &&
+ commit2=`git rev-list -1 HEAD` &&
+ test_commit three foo.txt &&
+ commit3=`git rev-list -1 HEAD`
+ '
+
+test_expect_success '--parents rewrites TREESAME parents correctly' '
+ echo $commit3 $commit1 > expected &&
+ echo $commit1 >> expected &&
+ git rev-list --parents HEAD -- foo.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--parents --show-all does not rewrites TREESAME parents' '
+ echo $commit3 $commit2 > expected &&
+ echo $commit2 $commit1 >> expected &&
+ echo $commit1 >> expected &&
+ git rev-list --parents --show-all HEAD -- foo.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_done
diff --git a/t/t6016-rev-list-graph-simplify-history.sh b/t/t6016-rev-list-graph-simplify-history.sh
new file mode 100755
index 0000000..27fd52b
--- /dev/null
+++ b/t/t6016-rev-list-graph-simplify-history.sh
@@ -0,0 +1,276 @@
+#!/bin/sh
+
+# There's more than one "correct" way to represent the history graphically.
+# These tests depend on the current behavior of the graphing code. If the
+# graphing code is ever changed to draw the output differently, these tests
+# cases will need to be updated to know about the new layout.
+
+test_description='--graph and simplified history'
+
+. ./test-lib.sh
+
+test_expect_success 'set up rev-list --graph test' '
+ # 3 commits on branch A
+ test_commit A1 foo.txt &&
+ test_commit A2 bar.txt &&
+ test_commit A3 bar.txt &&
+ git branch -m master A &&
+
+ # 2 commits on branch B, started from A1
+ git checkout -b B A1 &&
+ test_commit B1 foo.txt &&
+ test_commit B2 abc.txt &&
+
+ # 2 commits on branch C, started from A2
+ git checkout -b C A2 &&
+ test_commit C1 xyz.txt &&
+ test_commit C2 xyz.txt &&
+
+ # Octopus merge B and C into branch A
+ git checkout A &&
+ git merge B C &&
+ git tag A4
+
+ test_commit A5 bar.txt &&
+
+ # More commits on C, then merge C into A
+ git checkout C &&
+ test_commit C3 foo.txt &&
+ test_commit C4 bar.txt &&
+ git checkout A &&
+ git merge -s ours C &&
+ git tag A6
+
+ test_commit A7 bar.txt &&
+
+ # Store commit names in variables for later use
+ A1=$(git rev-parse --verify A1) &&
+ A2=$(git rev-parse --verify A2) &&
+ A3=$(git rev-parse --verify A3) &&
+ A4=$(git rev-parse --verify A4) &&
+ A5=$(git rev-parse --verify A5) &&
+ A6=$(git rev-parse --verify A6) &&
+ A7=$(git rev-parse --verify A7) &&
+ B1=$(git rev-parse --verify B1) &&
+ B2=$(git rev-parse --verify B2) &&
+ C1=$(git rev-parse --verify C1) &&
+ C2=$(git rev-parse --verify C2) &&
+ C3=$(git rev-parse --verify C3) &&
+ C4=$(git rev-parse --verify C4)
+ '
+
+test_expect_success '--graph --all' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| | |/ " >> expected &&
+ echo "| | * $C2" >> expected &&
+ echo "| | * $C1" >> expected &&
+ echo "| * | $B2" >> expected &&
+ echo "| * | $B1" >> expected &&
+ echo "* | | $A3" >> expected &&
+ echo "| |/ " >> expected &&
+ echo "|/| " >> expected &&
+ echo "* | $A2" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --all > actual &&
+ test_cmp expected actual
+ '
+
+# Make sure the graph_is_interesting() code still realizes
+# that undecorated merges are interesting, even with --simplify-by-decoration
+test_expect_success '--graph --simplify-by-decoration' '
+ rm -f expected &&
+ git tag -d A4
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| | |/ " >> expected &&
+ echo "| | * $C2" >> expected &&
+ echo "| | * $C1" >> expected &&
+ echo "| * | $B2" >> expected &&
+ echo "| * | $B1" >> expected &&
+ echo "* | | $A3" >> expected &&
+ echo "| |/ " >> expected &&
+ echo "|/| " >> expected &&
+ echo "* | $A2" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --all --simplify-by-decoration > actual &&
+ test_cmp expected actual
+ '
+
+# Get rid of all decorations on branch B, and graph with it simplified away
+test_expect_success '--graph --simplify-by-decoration prune branch B' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A4" >> expected &&
+ echo "|\\ \\ " >> expected &&
+ echo "| |/ " >> expected &&
+ echo "| * $C2" >> expected &&
+ echo "| * $C1" >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --simplify-by-decoration --all > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --full-history -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A4" >> expected &&
+ echo "|\\ \\ " >> expected &&
+ echo "| |/ " >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --full-history --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --full-history --simplify-merges -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --full-history --simplify-merges --all \
+ -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A3" >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --sparse -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "* $A3" >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "| * $C2" >> expected &&
+ echo "| * $C1" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --sparse --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph ^C4' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $B2" >> expected &&
+ echo "| * $B1" >> expected &&
+ echo "* $A3" >> expected &&
+ git rev-list --graph --all ^C4 > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph ^C3' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $B2" >> expected &&
+ echo "| * $B1" >> expected &&
+ echo "* $A3" >> expected &&
+ git rev-list --graph --all ^C3 > actual &&
+ test_cmp expected actual
+ '
+
+# I don't think the ordering of the boundary commits is really
+# that important, but this test depends on it. If the ordering ever changes
+# in the code, we'll need to update this test.
+test_expect_success '--graph --boundary ^C3' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| * | | $B2" >> expected &&
+ echo "| * | | $B1" >> expected &&
+ echo "* | | | $A3" >> expected &&
+ echo "o | | | $A2" >> expected &&
+ echo "|/ / / " >> expected &&
+ echo "o | | $A1" >> expected &&
+ echo " / / " >> expected &&
+ echo "| o $C3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "o $C2" >> expected &&
+ git rev-list --graph --boundary --all ^C3 > actual &&
+ test_cmp expected actual
+ '
+
+test_done
diff --git a/t/t6017-rev-list-stdin.sh b/t/t6017-rev-list-stdin.sh
new file mode 100755
index 0000000..f1c32db
--- /dev/null
+++ b/t/t6017-rev-list-stdin.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Junio C Hamano
+#
+
+test_description='log family learns --stdin'
+
+. ./test-lib.sh
+
+check () {
+ for cmd in rev-list "log --stat"
+ do
+ for i in "$@"
+ do
+ printf "%s\n" $i
+ done >input &&
+ test_expect_success "check $cmd $*" '
+ git $cmd $(cat input) >expect &&
+ git $cmd --stdin <input >actual &&
+ sed -e "s/^/input /" input &&
+ sed -e "s/^/output /" expect &&
+ test_cmp expect actual
+ '
+ done
+}
+
+them='1 2 3 4 5 6 7'
+
+test_expect_success setup '
+ (
+ for i in 0 $them
+ do
+ for j in $them
+ do
+ echo $i.$j >file-$j &&
+ git add file-$j || exit
+ done &&
+ test_tick &&
+ git commit -m $i || exit
+ done &&
+ for i in $them
+ do
+ git checkout -b side-$i master~$i &&
+ echo updated $i >file-$i &&
+ git add file-$i &&
+ test_tick &&
+ git commit -m side-$i || exit
+ done
+ )
+'
+
+check master
+check side-1 ^side-4
+check side-1 ^side-7 --
+check side-1 ^side-7 -- file-1
+check side-1 ^side-7 -- file-2
+check side-3 ^side-4 -- file-3
+check side-3 ^side-2
+check side-3 ^side-2 -- file-1
+
+test_done
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
new file mode 100755
index 0000000..58428d9
--- /dev/null
+++ b/t/t6018-rev-list-glob.sh
@@ -0,0 +1,210 @@
+#!/bin/sh
+
+test_description='rev-list/rev-parse --glob'
+
+. ./test-lib.sh
+
+commit () {
+ test_tick &&
+ echo $1 > foo &&
+ git add foo &&
+ git commit -m "$1"
+}
+
+compare () {
+ # Split arguments on whitespace.
+ git $1 $2 >expected &&
+ git $1 $3 >actual &&
+ test_cmp expected actual
+}
+
+test_expect_success 'setup' '
+
+ commit master &&
+ git checkout -b subspace/one master &&
+ commit one &&
+ git checkout -b subspace/two master &&
+ commit two &&
+ git checkout -b subspace-x master &&
+ commit subspace-x &&
+ git checkout -b other/three master &&
+ commit three &&
+ git checkout -b someref master &&
+ commit some &&
+ git checkout master &&
+ commit master2 &&
+ git tag foo/bar master &&
+ commit master3 &&
+ git update-ref refs/remotes/foo/baz master &&
+ commit master4
+'
+
+test_expect_success 'rev-parse --glob=refs/heads/subspace/*' '
+
+ compare rev-parse "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace/*' '
+
+ compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace/*"
+
+'
+
+test_expect_success 'rev-parse --glob=refs/heads/subspace/' '
+
+ compare rev-parse "subspace/one subspace/two" "--glob=refs/heads/subspace/"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace/' '
+
+ compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace/"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace' '
+
+ compare rev-parse "subspace/one subspace/two" "--glob=heads/subspace"
+
+'
+
+test_expect_success 'rev-parse --branches=subspace/*' '
+
+ compare rev-parse "subspace/one subspace/two" "--branches=subspace/*"
+
+'
+
+test_expect_success 'rev-parse --branches=subspace/' '
+
+ compare rev-parse "subspace/one subspace/two" "--branches=subspace/"
+
+'
+
+test_expect_success 'rev-parse --branches=subspace' '
+
+ compare rev-parse "subspace/one subspace/two" "--branches=subspace"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/subspace/* --glob=heads/other/*' '
+
+ compare rev-parse "subspace/one subspace/two other/three" "--glob=heads/subspace/* --glob=heads/other/*"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/someref/* master' '
+
+ compare rev-parse "master" "--glob=heads/someref/* master"
+
+'
+
+test_expect_success 'rev-parse --glob=heads/*' '
+
+ compare rev-parse "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
+
+'
+
+test_expect_success 'rev-parse --tags=foo' '
+
+ compare rev-parse "foo/bar" "--tags=foo"
+
+'
+
+test_expect_success 'rev-parse --remotes=foo' '
+
+ compare rev-parse "foo/baz" "--remotes=foo"
+
+'
+
+test_expect_success 'rev-list --glob=refs/heads/subspace/*' '
+
+ compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace/*' '
+
+ compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/*"
+
+'
+
+test_expect_success 'rev-list --glob=refs/heads/subspace/' '
+
+ compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace/' '
+
+ compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace' '
+
+ compare rev-list "subspace/one subspace/two" "--glob=heads/subspace"
+
+'
+
+test_expect_success 'rev-list --branches=subspace/*' '
+
+ compare rev-list "subspace/one subspace/two" "--branches=subspace/*"
+
+'
+
+test_expect_success 'rev-list --branches=subspace/' '
+
+ compare rev-list "subspace/one subspace/two" "--branches=subspace/"
+
+'
+
+test_expect_success 'rev-list --branches=subspace' '
+
+ compare rev-list "subspace/one subspace/two" "--branches=subspace"
+
+'
+
+test_expect_success 'rev-list --branches' '
+
+ compare rev-list "master subspace-x someref other/three subspace/one subspace/two" "--branches"
+
+'
+
+test_expect_success 'rev-list --glob=heads/someref/* master' '
+
+ compare rev-list "master" "--glob=heads/someref/* master"
+
+'
+
+test_expect_success 'rev-list --glob=heads/subspace/* --glob=heads/other/*' '
+
+ compare rev-list "subspace/one subspace/two other/three" "--glob=heads/subspace/* --glob=heads/other/*"
+
+'
+
+test_expect_success 'rev-list --glob=heads/*' '
+
+ compare rev-list "master other/three someref subspace-x subspace/one subspace/two" "--glob=heads/*"
+
+'
+
+test_expect_success 'rev-list --tags=foo' '
+
+ compare rev-list "foo/bar" "--tags=foo"
+
+'
+
+test_expect_success 'rev-list --tags' '
+
+ compare rev-list "foo/bar" "--tags"
+
+'
+
+test_expect_success 'rev-list --remotes=foo' '
+
+ compare rev-list "foo/baz" "--remotes=foo"
+
+'
+
+test_done
diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
new file mode 100755
index 0000000..7641029
--- /dev/null
+++ b/t/t6019-rev-list-ancestry-path.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='--ancestry-path'
+
+# D---E-------F
+# / \ \
+# B---C---G---H---I---J
+# / \
+# A-------K---------------L--M
+#
+# D..M == E F G H I J K L M
+# --ancestry-path D..M == E F H I J L M
+#
+# D..M -- M.t == M
+# --ancestry-path D..M -- M.t == M
+
+. ./test-lib.sh
+
+test_merge () {
+ test_tick &&
+ git merge -s ours -m "$2" "$1" &&
+ git tag "$2"
+}
+
+test_expect_success setup '
+ test_commit A &&
+ test_commit B &&
+ test_commit C &&
+ test_commit D &&
+ test_commit E &&
+ test_commit F &&
+ git reset --hard C &&
+ test_commit G &&
+ test_merge E H &&
+ test_commit I &&
+ test_merge F J &&
+ git reset --hard A &&
+ test_commit K &&
+ test_merge J L &&
+ test_commit M
+'
+
+test_expect_success 'rev-list D..M' '
+ for c in E F G H I J K L M; do echo $c; done >expect &&
+ git rev-list --format=%s D..M |
+ sed -e "/^commit /d" |
+ sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-path D..M' '
+ for c in E F H I J L M; do echo $c; done >expect &&
+ git rev-list --ancestry-path --format=%s D..M |
+ sed -e "/^commit /d" |
+ sort >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list D..M -- M.t' '
+ echo M >expect &&
+ git rev-list --format=%s D..M -- M.t |
+ sed -e "/^commit /d" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'rev-list --ancestry-patch D..M -- M.t' '
+ echo M >expect &&
+ git rev-list --ancestry-path --format=%s D..M -- M.t |
+ sed -e "/^commit /d" >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
new file mode 100755
index 0000000..e71c687
--- /dev/null
+++ b/t/t6020-merge-df.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Fredrik Kuivinen
+#
+
+test_description='Test merge with directory/file conflicts'
+. ./test-lib.sh
+
+test_expect_success 'prepare repository' \
+'echo "Hello" > init &&
+git add init &&
+git commit -m "Initial commit" &&
+git branch B &&
+mkdir dir &&
+echo "foo" > dir/foo &&
+git add dir/foo &&
+git commit -m "File: dir/foo" &&
+git checkout B &&
+echo "file dir" > dir &&
+git add dir &&
+git commit -m "File: dir"'
+
+test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
+
+test_expect_failure 'F/D conflict' '
+ git reset --hard &&
+ git checkout master &&
+ rm .git/index &&
+
+ mkdir before &&
+ echo FILE >before/one &&
+ echo FILE >after &&
+ git add . &&
+ git commit -m first &&
+
+ rm -f after &&
+ git mv before after &&
+ git commit -m move &&
+
+ git checkout -b para HEAD^ &&
+ echo COMPLETELY ANOTHER FILE >another &&
+ git add . &&
+ git commit -m para &&
+
+ git merge master
+'
+
+test_done
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
new file mode 100755
index 0000000..331b9b0
--- /dev/null
+++ b/t/t6021-merge-criss-cross.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Fredrik Kuivinen
+#
+
+# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a
+# nice description of what this is about.
+
+
+test_description='Test criss-cross merge'
+. ./test-lib.sh
+
+test_expect_success 'prepare repository' \
+'echo "1
+2
+3
+4
+5
+6
+7
+8
+9" > file &&
+git add file &&
+git commit -m "Initial commit" file &&
+git branch A &&
+git branch B &&
+git checkout A &&
+echo "1
+2
+3
+4
+5
+6
+7
+8 changed in B8, branch A
+9" > file &&
+git commit -m "B8" file &&
+git checkout B &&
+echo "1
+2
+3 changed in C3, branch B
+4
+5
+6
+7
+8
+9
+" > file &&
+git commit -m "C3" file &&
+git branch C3 &&
+git merge "pre E3 merge" B A &&
+echo "1
+2
+3 changed in E3, branch B. New file size
+4
+5
+6
+7
+8 changed in B8, branch A
+9
+" > file &&
+git commit -m "E3" file &&
+git checkout A &&
+git merge "pre D8 merge" A C3 &&
+echo "1
+2
+3 changed in C3, branch B
+4
+5
+6
+7
+8 changed in D8, branch A. New file size 2
+9" > file &&
+git commit -m D8 file'
+
+test_expect_success 'Criss-cross merge' 'git merge "final merge" A B'
+
+cat > file-expect <<EOF
+1
+2
+3 changed in E3, branch B. New file size
+4
+5
+6
+7
+8 changed in D8, branch A. New file size 2
+9
+EOF
+
+test_expect_success 'Criss-cross merge result' 'cmp file file-expect'
+
+test_expect_success 'Criss-cross merge fails (-s resolve)' \
+'git reset --hard A^ &&
+test_must_fail git merge -s resolve -m "final merge" B'
+
+test_done
diff --git a/t/t6022-merge-rename.sh b/t/t6022-merge-rename.sh
new file mode 100755
index 0000000..b66544b
--- /dev/null
+++ b/t/t6022-merge-rename.sh
@@ -0,0 +1,344 @@
+#!/bin/sh
+
+test_description='Merge-recursive merging renames'
+. ./test-lib.sh
+
+test_expect_success setup \
+'
+cat >A <<\EOF &&
+a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+c cccccccccccccccccccccccccccccccccccccccccccccccc
+d dddddddddddddddddddddddddddddddddddddddddddddddd
+e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+f ffffffffffffffffffffffffffffffffffffffffffffffff
+g gggggggggggggggggggggggggggggggggggggggggggggggg
+h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
+i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
+k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
+l llllllllllllllllllllllllllllllllllllllllllllllll
+m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
+o oooooooooooooooooooooooooooooooooooooooooooooooo
+EOF
+
+cat >M <<\EOF &&
+A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+EOF
+
+git add A M &&
+git commit -m "initial has A and M" &&
+git branch white &&
+git branch red &&
+git branch blue &&
+git branch yellow &&
+git branch change &&
+git branch change+rename &&
+
+sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
+mv A+ A &&
+git commit -a -m "master updates A" &&
+
+git checkout yellow &&
+rm -f M &&
+git commit -a -m "yellow removes M" &&
+
+git checkout white &&
+sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
+sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+rm -f A M &&
+git update-index --add --remove A B M N &&
+git commit -m "white renames A->B, M->N" &&
+
+git checkout red &&
+sed -e "/^g /s/.*/g : red changes a line/" <A >B &&
+sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+rm -f A M &&
+git update-index --add --remove A B M N &&
+git commit -m "red renames A->B, M->N" &&
+
+git checkout blue &&
+sed -e "/^g /s/.*/g : blue changes a line/" <A >C &&
+sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+rm -f A M &&
+git update-index --add --remove A C M N &&
+git commit -m "blue renames A->C, M->N" &&
+
+git checkout change &&
+sed -e "/^g /s/.*/g : changed line/" <A >A+ &&
+mv A+ A &&
+git commit -q -a -m "changed" &&
+
+git checkout change+rename &&
+sed -e "/^g /s/.*/g : changed line/" <A >B &&
+rm A &&
+git update-index --add B &&
+git commit -q -a -m "changed and renamed" &&
+
+git checkout master'
+
+test_expect_success 'pull renaming branch into unrenaming one' \
+'
+ git show-branch
+ git pull . white && {
+ echo "BAD: should have conflicted"
+ return 1
+ }
+ git ls-files -s
+ test "$(git ls-files -u B | wc -l)" -eq 3 || {
+ echo "BAD: should have left stages for B"
+ return 1
+ }
+ test "$(git ls-files -s N | wc -l)" -eq 1 || {
+ echo "BAD: should have merged N"
+ return 1
+ }
+ sed -ne "/^g/{
+ p
+ q
+ }" B | grep master || {
+ echo "BAD: should have listed our change first"
+ return 1
+ }
+ test "$(git diff white N | wc -l)" -eq 0 || {
+ echo "BAD: should have taken colored branch"
+ return 1
+ }
+'
+
+test_expect_success 'pull renaming branch into another renaming one' \
+'
+ rm -f B
+ git reset --hard
+ git checkout red
+ git pull . white && {
+ echo "BAD: should have conflicted"
+ return 1
+ }
+ test "$(git ls-files -u B | wc -l)" -eq 3 || {
+ echo "BAD: should have left stages"
+ return 1
+ }
+ test "$(git ls-files -s N | wc -l)" -eq 1 || {
+ echo "BAD: should have merged N"
+ return 1
+ }
+ sed -ne "/^g/{
+ p
+ q
+ }" B | grep red || {
+ echo "BAD: should have listed our change first"
+ return 1
+ }
+ test "$(git diff white N | wc -l)" -eq 0 || {
+ echo "BAD: should have taken colored branch"
+ return 1
+ }
+'
+
+test_expect_success 'pull unrenaming branch into renaming one' \
+'
+ git reset --hard
+ git show-branch
+ git pull . master && {
+ echo "BAD: should have conflicted"
+ return 1
+ }
+ test "$(git ls-files -u B | wc -l)" -eq 3 || {
+ echo "BAD: should have left stages"
+ return 1
+ }
+ test "$(git ls-files -s N | wc -l)" -eq 1 || {
+ echo "BAD: should have merged N"
+ return 1
+ }
+ sed -ne "/^g/{
+ p
+ q
+ }" B | grep red || {
+ echo "BAD: should have listed our change first"
+ return 1
+ }
+ test "$(git diff white N | wc -l)" -eq 0 || {
+ echo "BAD: should have taken colored branch"
+ return 1
+ }
+'
+
+test_expect_success 'pull conflicting renames' \
+'
+ git reset --hard
+ git show-branch
+ git pull . blue && {
+ echo "BAD: should have conflicted"
+ return 1
+ }
+ test "$(git ls-files -u A | wc -l)" -eq 1 || {
+ echo "BAD: should have left a stage"
+ return 1
+ }
+ test "$(git ls-files -u B | wc -l)" -eq 1 || {
+ echo "BAD: should have left a stage"
+ return 1
+ }
+ test "$(git ls-files -u C | wc -l)" -eq 1 || {
+ echo "BAD: should have left a stage"
+ return 1
+ }
+ test "$(git ls-files -s N | wc -l)" -eq 1 || {
+ echo "BAD: should have merged N"
+ return 1
+ }
+ sed -ne "/^g/{
+ p
+ q
+ }" B | grep red || {
+ echo "BAD: should have listed our change first"
+ return 1
+ }
+ test "$(git diff white N | wc -l)" -eq 0 || {
+ echo "BAD: should have taken colored branch"
+ return 1
+ }
+'
+
+test_expect_success 'interference with untracked working tree file' '
+
+ git reset --hard
+ git show-branch
+ echo >A this file should not matter
+ git pull . white && {
+ echo "BAD: should have conflicted"
+ return 1
+ }
+ test -f A || {
+ echo "BAD: should have left A intact"
+ return 1
+ }
+'
+
+test_expect_success 'interference with untracked working tree file' '
+
+ git reset --hard
+ git checkout white
+ git show-branch
+ rm -f A
+ echo >A this file should not matter
+ git pull . red && {
+ echo "BAD: should have conflicted"
+ return 1
+ }
+ test -f A || {
+ echo "BAD: should have left A intact"
+ return 1
+ }
+'
+
+test_expect_success 'interference with untracked working tree file' '
+
+ git reset --hard
+ rm -f A M
+ git checkout -f master
+ git tag -f anchor
+ git show-branch
+ git pull . yellow || {
+ echo "BAD: should have cleanly merged"
+ return 1
+ }
+ test -f M && {
+ echo "BAD: should have removed M"
+ return 1
+ }
+ git reset --hard anchor
+'
+
+test_expect_success 'updated working tree file should prevent the merge' '
+
+ git reset --hard
+ rm -f A M
+ git checkout -f master
+ git tag -f anchor
+ git show-branch
+ echo >>M one line addition
+ cat M >M.saved
+ git pull . yellow && {
+ echo "BAD: should have complained"
+ return 1
+ }
+ test_cmp M M.saved || {
+ echo "BAD: should have left M intact"
+ return 1
+ }
+ rm -f M.saved
+'
+
+test_expect_success 'updated working tree file should prevent the merge' '
+
+ git reset --hard
+ rm -f A M
+ git checkout -f master
+ git tag -f anchor
+ git show-branch
+ echo >>M one line addition
+ cat M >M.saved
+ git update-index M
+ git pull . yellow && {
+ echo "BAD: should have complained"
+ return 1
+ }
+ test_cmp M M.saved || {
+ echo "BAD: should have left M intact"
+ return 1
+ }
+ rm -f M.saved
+'
+
+test_expect_success 'interference with untracked working tree file' '
+
+ git reset --hard
+ rm -f A M
+ git checkout -f yellow
+ git tag -f anchor
+ git show-branch
+ echo >M this file should not matter
+ git pull . master || {
+ echo "BAD: should have cleanly merged"
+ return 1
+ }
+ test -f M || {
+ echo "BAD: should have left M intact"
+ return 1
+ }
+ git ls-files -s | grep M && {
+ echo "BAD: M must be untracked in the result"
+ return 1
+ }
+ git reset --hard anchor
+'
+
+test_expect_success 'merge of identical changes in a renamed file' '
+ rm -f A M N
+ git reset --hard &&
+ git checkout change+rename &&
+ GIT_MERGE_VERBOSITY=3 git merge change | grep "^Skipped B" &&
+ git reset --hard HEAD^ &&
+ git checkout change &&
+ GIT_MERGE_VERBOSITY=3 git merge change+rename | grep "^Skipped B"
+'
+
+test_done
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
new file mode 100755
index 0000000..d486d73
--- /dev/null
+++ b/t/t6023-merge-file.sh
@@ -0,0 +1,255 @@
+#!/bin/sh
+
+test_description='RCS merge replacement: merge-file'
+. ./test-lib.sh
+
+cat > orig.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.
+EOF
+
+cat > new1.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
+
+cat > new2.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.
+EOF
+
+cat > new3.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.
+EOF
+
+cat > new4.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." >> new4.txt
+
+test_expect_success 'merge with no changes' '
+ cp orig.txt test.txt &&
+ git merge-file test.txt orig.txt orig.txt &&
+ test_cmp test.txt orig.txt
+'
+
+cp new1.txt test.txt
+test_expect_success "merge without conflict" \
+ "git merge-file test.txt orig.txt new2.txt"
+
+cp new1.txt test.txt
+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_success "merge result added missing LF" \
+ "test_cmp test.txt test2.txt"
+
+cp test.txt backup.txt
+test_expect_success "merge with conflicts" \
+ "test_must_fail git merge-file test.txt orig.txt new3.txt"
+
+cat > expect.txt << EOF
+<<<<<<< test.txt
+Dominus regit me, et nihil mihi deerit.
+=======
+DOMINUS regit me,
+et nihil mihi deerit.
+>>>>>>> new3.txt
+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 "expected conflict markers" "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"
+
+cat > expect.txt << EOF
+<<<<<<< 1
+Dominus regit me, et nihil mihi deerit.
+=======
+DOMINUS regit me,
+et nihil mihi deerit.
+>>>>>>> new3.txt
+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 "expected conflict markers, with -L" \
+ "test_cmp test.txt expect.txt"
+
+sed "s/ tu / TU /" < new1.txt > new5.txt
+test_expect_success "conflict in removed tail" \
+ "test_must_fail git merge-file -p orig.txt new1.txt new5.txt > out"
+
+cat > expect << 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.
+<<<<<<< orig.txt
+=======
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam TU mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+>>>>>>> new5.txt
+EOF
+
+test_expect_success "expected conflict markers" "test_cmp expect out"
+
+test_expect_success 'binary files cannot be merged' '
+ test_must_fail git merge-file -p \
+ orig.txt "$TEST_DIRECTORY"/test4012.png new1.txt 2> merge.err &&
+ grep "Cannot merge binary files" merge.err
+'
+
+sed -e "s/deerit.\$/deerit;/" -e "s/me;\$/me./" < new5.txt > new6.txt
+sed -e "s/deerit.\$/deerit,/" -e "s/me;\$/me,/" < new5.txt > new7.txt
+
+test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
+
+ test_must_fail git merge-file -p new6.txt new5.txt new7.txt > output &&
+ test 1 = $(grep ======= < output | wc -l)
+
+'
+
+sed -e 's/deerit./&%%%%/' -e "s/locavit,/locavit;/"< new6.txt | tr '%' '\012' > new8.txt
+sed -e 's/deerit./&%%%%/' -e "s/locavit,/locavit --/" < new7.txt | tr '%' '\012' > new9.txt
+
+test_expect_success 'ZEALOUS_ALNUM' '
+
+ test_must_fail git merge-file -p \
+ new8.txt new5.txt new9.txt > merge.out &&
+ test 1 = $(grep ======= < merge.out | wc -l)
+
+'
+
+cat >expect <<\EOF
+Dominus regit me,
+<<<<<<< new8.txt
+et nihil mihi deerit;
+
+
+
+
+In loco pascuae ibi me collocavit;
+super aquam refectionis educavit me.
+||||||| new5.txt
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+=======
+et nihil mihi deerit,
+
+
+
+
+In loco pascuae ibi me collocavit --
+super aquam refectionis educavit me,
+>>>>>>> new9.txt
+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 '"diff3 -m" style output (1)' '
+ test_must_fail git merge-file -p --diff3 \
+ new8.txt new5.txt new9.txt >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '"diff3 -m" style output (2)' '
+ git config merge.conflictstyle diff3 &&
+ test_must_fail git merge-file -p \
+ new8.txt new5.txt new9.txt >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Dominus regit me,
+<<<<<<<<<< new8.txt
+et nihil mihi deerit;
+
+
+
+
+In loco pascuae ibi me collocavit;
+super aquam refectionis educavit me.
+|||||||||| new5.txt
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+==========
+et nihil mihi deerit,
+
+
+
+
+In loco pascuae ibi me collocavit --
+super aquam refectionis educavit me,
+>>>>>>>>>> new9.txt
+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 'marker size' '
+ test_must_fail git merge-file -p --marker-size=10 \
+ new8.txt new5.txt new9.txt >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
new file mode 100755
index 0000000..b3fbf65
--- /dev/null
+++ b/t/t6024-recursive-merge.sh
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+test_description='Test merge without common ancestors'
+. ./test-lib.sh
+
+# This scenario is based on a real-world repository of Shawn Pearce.
+
+# 1 - A - D - F
+# \ X /
+# B X
+# X \
+# 2 - C - E - G
+
+GIT_COMMITTER_DATE="2006-12-12 23:28:00 +0100"
+export GIT_COMMITTER_DATE
+
+test_expect_success "setup tests" '
+echo 1 > a1 &&
+git add a1 &&
+GIT_AUTHOR_DATE="2006-12-12 23:00:00" git commit -m 1 a1 &&
+
+git checkout -b A master &&
+echo A > a1 &&
+GIT_AUTHOR_DATE="2006-12-12 23:00:01" git commit -m A a1 &&
+
+git checkout -b B master &&
+echo B > a1 &&
+GIT_AUTHOR_DATE="2006-12-12 23:00:02" git commit -m B a1 &&
+
+git checkout -b D A &&
+git rev-parse B > .git/MERGE_HEAD &&
+echo D > a1 &&
+git update-index a1 &&
+GIT_AUTHOR_DATE="2006-12-12 23:00:03" git commit -m D &&
+
+git symbolic-ref HEAD refs/heads/other &&
+echo 2 > a1 &&
+GIT_AUTHOR_DATE="2006-12-12 23:00:04" git commit -m 2 a1 &&
+
+git checkout -b C &&
+echo C > a1 &&
+GIT_AUTHOR_DATE="2006-12-12 23:00:05" git commit -m C a1 &&
+
+git checkout -b E C &&
+git rev-parse B > .git/MERGE_HEAD &&
+echo E > a1 &&
+git update-index a1 &&
+GIT_AUTHOR_DATE="2006-12-12 23:00:06" git commit -m E &&
+
+git checkout -b G E &&
+git rev-parse A > .git/MERGE_HEAD &&
+echo G > a1 &&
+git update-index a1 &&
+GIT_AUTHOR_DATE="2006-12-12 23:00:07" git commit -m G &&
+
+git checkout -b F D &&
+git rev-parse C > .git/MERGE_HEAD &&
+echo F > a1 &&
+git update-index a1 &&
+GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
+'
+
+test_expect_success "combined merge conflicts" "
+ test_must_fail git merge -m final G
+"
+
+cat > expect << EOF
+<<<<<<< HEAD
+F
+=======
+G
+>>>>>>> G
+EOF
+
+test_expect_success "result contains a conflict" "test_cmp expect a1"
+
+git ls-files --stage > out
+cat > expect << EOF
+100644 439cc46de773d8a83c77799b7cc9191c128bfcff 1 a1
+100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2 a1
+100644 fd7923529855d0b274795ae3349c5e0438333979 3 a1
+EOF
+
+test_expect_success "virtual trees were processed" "test_cmp expect out"
+
+test_expect_success 'refuse to merge binary files' '
+ git reset --hard &&
+ printf "\0" > binary-file &&
+ git add binary-file &&
+ git commit -m binary &&
+ git checkout G &&
+ printf "\0\0" > binary-file &&
+ git add binary-file &&
+ git commit -m binary2 &&
+ test_must_fail git merge F > merge.out 2> merge.err &&
+ grep "Cannot merge binary files: binary-file (HEAD vs. F)" merge.err
+'
+
+test_expect_success 'mark rename/delete as unmerged' '
+
+ git reset --hard &&
+ git checkout -b delete &&
+ git rm a1 &&
+ test_tick &&
+ git commit -m delete &&
+ git checkout -b rename HEAD^ &&
+ git mv a1 a2
+ test_tick &&
+ git commit -m rename &&
+ test_must_fail git merge delete &&
+ test 1 = $(git ls-files --unmerged | wc -l) &&
+ git rev-parse --verify :2:a2 &&
+ test_must_fail git rev-parse --verify :3:a2 &&
+ git checkout -f delete &&
+ test_must_fail git merge rename &&
+ test 1 = $(git ls-files --unmerged | wc -l) &&
+ test_must_fail git rev-parse --verify :2:a2 &&
+ git rev-parse --verify :3:a2
+
+'
+
+test_done
diff --git a/t/t6025-merge-symlinks.sh b/t/t6025-merge-symlinks.sh
new file mode 100755
index 0000000..433c4de
--- /dev/null
+++ b/t/t6025-merge-symlinks.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes Sixt
+#
+
+test_description='merging symlinks on filesystem w/o symlink support.
+
+This tests that git merge-recursive writes merge results as plain files
+if core.symlinks is false.'
+
+. ./test-lib.sh
+
+test_expect_success \
+'setup' '
+git config core.symlinks false &&
+> file &&
+git add file &&
+git commit -m initial &&
+git branch b-symlink &&
+git branch b-file &&
+l=$(printf file | git hash-object -t blob -w --stdin) &&
+echo "120000 $l symlink" | git update-index --index-info &&
+git commit -m master &&
+git checkout b-symlink &&
+l=$(printf file-different | git hash-object -t blob -w --stdin) &&
+echo "120000 $l symlink" | git update-index --index-info &&
+git commit -m b-symlink &&
+git checkout b-file &&
+echo plain-file > symlink &&
+git add symlink &&
+git commit -m b-file'
+
+test_expect_success \
+'merge master into b-symlink, which has a different symbolic link' '
+git checkout b-symlink &&
+test_must_fail git merge master'
+
+test_expect_success \
+'the merge result must be a file' '
+test -f symlink'
+
+test_expect_success \
+'merge master into b-file, which has a file instead of a symbolic link' '
+git reset --hard && git checkout b-file &&
+test_must_fail git merge master'
+
+test_expect_success \
+'the merge result must be a file' '
+test -f symlink'
+
+test_expect_success \
+'merge b-file, which has a file instead of a symbolic link, into master' '
+git reset --hard &&
+git checkout master &&
+test_must_fail git merge b-file'
+
+test_expect_success \
+'the merge result must be a file' '
+test -f symlink'
+
+test_done
diff --git a/t/t6026-merge-attr.sh b/t/t6026-merge-attr.sh
new file mode 100755
index 0000000..5e43997
--- /dev/null
+++ b/t/t6026-merge-attr.sh
@@ -0,0 +1,179 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Junio C Hamano
+#
+
+test_description='per path merge controlled by merge attribute'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ for f in text binary union
+ do
+ echo Initial >$f && git add $f || break
+ done &&
+ test_tick &&
+ git commit -m Initial &&
+
+ git branch side &&
+ for f in text binary union
+ do
+ echo Master >>$f && git add $f || break
+ done &&
+ test_tick &&
+ git commit -m Master &&
+
+ git checkout side &&
+ for f in text binary union
+ do
+ echo Side >>$f && git add $f || break
+ done &&
+ test_tick &&
+ git commit -m Side &&
+
+ git tag anchor
+'
+
+test_expect_success merge '
+
+ {
+ echo "binary -merge"
+ echo "union merge=union"
+ } >.gitattributes &&
+
+ if git merge master
+ then
+ echo Gaah, should have conflicted
+ false
+ else
+ echo Ok, conflicted.
+ fi
+'
+
+test_expect_success 'check merge result in index' '
+
+ git ls-files -u | grep binary &&
+ git ls-files -u | grep text &&
+ ! (git ls-files -u | grep union)
+
+'
+
+test_expect_success 'check merge result in working tree' '
+
+ git cat-file -p HEAD:binary >binary-orig &&
+ grep "<<<<<<<" text &&
+ cmp binary-orig binary &&
+ ! grep "<<<<<<<" union &&
+ grep Master union &&
+ grep Side union
+
+'
+
+test_expect_success 'retry the merge with longer context' '
+ echo text conflict-marker-size=32 >>.gitattributes &&
+ git checkout -m text &&
+ sed -ne "/^\([<=>]\)\1\1\1*/{
+ s/ .*$//
+ p
+ }" >actual text &&
+ grep ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" actual &&
+ grep "================================" actual &&
+ grep "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" actual
+'
+
+cat >./custom-merge <<\EOF
+#!/bin/sh
+
+orig="$1" ours="$2" theirs="$3" exit="$4"
+(
+ echo "orig is $orig"
+ echo "ours is $ours"
+ echo "theirs is $theirs"
+ echo "=== orig ==="
+ cat "$orig"
+ echo "=== ours ==="
+ cat "$ours"
+ echo "=== theirs ==="
+ cat "$theirs"
+) >"$ours+"
+cat "$ours+" >"$ours"
+rm -f "$ours+"
+exit "$exit"
+EOF
+chmod +x ./custom-merge
+
+test_expect_success 'custom merge backend' '
+
+ echo "* merge=union" >.gitattributes &&
+ echo "text merge=custom" >>.gitattributes &&
+
+ git reset --hard anchor &&
+ git config --replace-all \
+ merge.custom.driver "./custom-merge %O %A %B 0" &&
+ git config --replace-all \
+ merge.custom.name "custom merge driver for testing" &&
+
+ git merge master &&
+
+ cmp binary union &&
+ sed -e 1,3d text >check-1 &&
+ o=$(git unpack-file master^:text) &&
+ a=$(git unpack-file side^:text) &&
+ b=$(git unpack-file master:text) &&
+ sh -c "./custom-merge $o $a $b 0" &&
+ sed -e 1,3d $a >check-2 &&
+ cmp check-1 check-2 &&
+ rm -f $o $a $b
+'
+
+test_expect_success 'custom merge backend' '
+
+ git reset --hard anchor &&
+ git config --replace-all \
+ merge.custom.driver "./custom-merge %O %A %B 1" &&
+ git config --replace-all \
+ merge.custom.name "custom merge driver for testing" &&
+
+ if git merge master
+ then
+ echo "Eh? should have conflicted"
+ false
+ else
+ echo "Ok, conflicted"
+ fi &&
+
+ cmp binary union &&
+ sed -e 1,3d text >check-1 &&
+ o=$(git unpack-file master^:text) &&
+ a=$(git unpack-file anchor:text) &&
+ b=$(git unpack-file master:text) &&
+ sh -c "./custom-merge $o $a $b 0" &&
+ sed -e 1,3d $a >check-2 &&
+ cmp check-1 check-2 &&
+ rm -f $o $a $b
+'
+
+test_expect_success 'up-to-date merge without common ancestor' '
+ test_create_repo repo1 &&
+ test_create_repo repo2 &&
+ test_tick &&
+ (
+ cd repo1 &&
+ >a &&
+ git add a &&
+ git commit -m initial
+ ) &&
+ test_tick &&
+ (
+ cd repo2 &&
+ git commit --allow-empty -m initial
+ ) &&
+ test_tick &&
+ (
+ cd repo1 &&
+ git pull ../repo2 master
+ )
+'
+
+test_done
diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh
new file mode 100755
index 0000000..b519626
--- /dev/null
+++ b/t/t6027-merge-binary.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+test_description='ask merge-recursive to merge binary files'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ cat "$TEST_DIRECTORY"/test4012.png >m &&
+ git add m &&
+ git ls-files -s | sed -e "s/ 0 / 1 /" >E1 &&
+ test_tick &&
+ git commit -m "initial" &&
+
+ git branch side &&
+ echo frotz >a &&
+ git add a &&
+ echo nitfol >>m &&
+ git add a m &&
+ git ls-files -s a >E0 &&
+ git ls-files -s m | sed -e "s/ 0 / 3 /" >E3 &&
+ test_tick &&
+ git commit -m "master adds some" &&
+
+ git checkout side &&
+ echo rezrov >>m &&
+ git add m &&
+ git ls-files -s m | sed -e "s/ 0 / 2 /" >E2 &&
+ test_tick &&
+ git commit -m "side modifies" &&
+
+ git tag anchor &&
+
+ cat E0 E1 E2 E3 >expect
+'
+
+test_expect_success resolve '
+
+ rm -f a* m* &&
+ git reset --hard anchor &&
+
+ if git merge -s resolve master
+ then
+ echo Oops, should not have succeeded
+ false
+ else
+ git ls-files -s >current
+ test_cmp current expect
+ fi
+'
+
+test_expect_success recursive '
+
+ rm -f a* m* &&
+ git reset --hard anchor &&
+
+ if git merge -s recursive master
+ then
+ echo Oops, should not have succeeded
+ false
+ else
+ git ls-files -s >current
+ test_cmp current expect
+ fi
+'
+
+test_done
diff --git a/t/t6028-merge-up-to-date.sh b/t/t6028-merge-up-to-date.sh
new file mode 100755
index 0000000..a91644e
--- /dev/null
+++ b/t/t6028-merge-up-to-date.sh
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='merge fast-forward and up to date'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git tag c0 &&
+
+ echo second >file &&
+ git add file &&
+ test_tick &&
+ git commit -m second &&
+ git tag c1 &&
+ git branch test
+'
+
+test_expect_success 'merge -s recursive up-to-date' '
+
+ git reset --hard c1 &&
+ test_tick &&
+ git merge -s recursive c0 &&
+ expect=$(git rev-parse c1) &&
+ current=$(git rev-parse HEAD) &&
+ test "$expect" = "$current"
+
+'
+
+test_expect_success 'merge -s recursive fast-forward' '
+
+ git reset --hard c0 &&
+ test_tick &&
+ git merge -s recursive c1 &&
+ expect=$(git rev-parse c1) &&
+ current=$(git rev-parse HEAD) &&
+ test "$expect" = "$current"
+
+'
+
+test_expect_success 'merge -s ours up-to-date' '
+
+ git reset --hard c1 &&
+ test_tick &&
+ git merge -s ours c0 &&
+ expect=$(git rev-parse c1) &&
+ current=$(git rev-parse HEAD) &&
+ test "$expect" = "$current"
+
+'
+
+test_expect_success 'merge -s ours fast-forward' '
+
+ git reset --hard c0 &&
+ test_tick &&
+ git merge -s ours c1 &&
+ expect=$(git rev-parse c0^{tree}) &&
+ current=$(git rev-parse HEAD^{tree}) &&
+ test "$expect" = "$current"
+
+'
+
+test_expect_success 'merge -s subtree up-to-date' '
+
+ git reset --hard c1 &&
+ test_tick &&
+ git merge -s subtree c0 &&
+ expect=$(git rev-parse c1) &&
+ current=$(git rev-parse HEAD) &&
+ test "$expect" = "$current"
+
+'
+
+test_done
diff --git a/t/t6029-merge-subtree.sh b/t/t6029-merge-subtree.sh
new file mode 100755
index 0000000..3900d9f
--- /dev/null
+++ b/t/t6029-merge-subtree.sh
@@ -0,0 +1,124 @@
+#!/bin/sh
+
+test_description='subtree merge strategy'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ s="1 2 3 4 5 6 7 8"
+ for i in $s; do echo $i; done >hello &&
+ git add hello &&
+ git commit -m initial &&
+ git checkout -b side &&
+ echo >>hello world &&
+ git add hello &&
+ git commit -m second &&
+ git checkout master &&
+ for i in mundo $s; do echo $i; done >hello &&
+ git add hello &&
+ git commit -m master
+
+'
+
+test_expect_success 'subtree available and works like recursive' '
+
+ git merge -s subtree side &&
+ for i in mundo $s world; do echo $i; done >expect &&
+ test_cmp expect hello
+
+'
+
+test_expect_success 'setup' '
+ mkdir git-gui &&
+ cd git-gui &&
+ git init &&
+ echo git-gui > git-gui.sh &&
+ o1=$(git hash-object git-gui.sh) &&
+ git add git-gui.sh &&
+ git commit -m "initial git-gui" &&
+ cd .. &&
+ mkdir git &&
+ cd git &&
+ git init &&
+ echo git >git.c &&
+ o2=$(git hash-object git.c) &&
+ git add git.c &&
+ git commit -m "initial git"
+'
+
+test_expect_success 'initial merge' '
+ git remote add -f gui ../git-gui &&
+ git merge -s ours --no-commit gui/master &&
+ git read-tree --prefix=git-gui/ -u gui/master &&
+ git commit -m "Merge git-gui as our subdirectory" &&
+ git checkout -b work &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 git-gui/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge update' '
+ cd ../git-gui &&
+ echo git-gui2 > git-gui.sh &&
+ o3=$(git hash-object git-gui.sh) &&
+ git add git-gui.sh &&
+ git checkout -b master2 &&
+ git commit -m "update git-gui" &&
+ cd ../git &&
+ git pull -s subtree gui master2 &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o3 0 git-gui/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'initial ambiguous subtree' '
+ cd ../git &&
+ git reset --hard master &&
+ git checkout -b master2 &&
+ git merge -s ours --no-commit gui/master &&
+ git read-tree --prefix=git-gui2/ -u gui/master &&
+ git commit -m "Merge git-gui2 as our subdirectory" &&
+ git checkout -b work2 &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 git-gui/git-gui.sh"
+ echo "100644 $o1 0 git-gui2/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge using explicit' '
+ cd ../git &&
+ git reset --hard master2 &&
+ git pull -Xsubtree=git-gui gui master2 &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o3 0 git-gui/git-gui.sh"
+ echo "100644 $o1 0 git-gui2/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge2 using explicit' '
+ cd ../git &&
+ git reset --hard master2 &&
+ git pull -Xsubtree=git-gui2 gui master2 &&
+ git ls-files -s >actual &&
+ (
+ echo "100644 $o1 0 git-gui/git-gui.sh"
+ echo "100644 $o3 0 git-gui2/git-gui.sh"
+ echo "100644 $o2 0 git.c"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
new file mode 100755
index 0000000..3b042aa
--- /dev/null
+++ b/t/t6030-bisect-porcelain.sh
@@ -0,0 +1,577 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Christian Couder
+#
+test_description='Tests git bisect functionality'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_line_into_file()
+{
+ _line=$1
+ _file=$2
+
+ if [ -f "$_file" ]; then
+ echo "$_line" >> $_file || return $?
+ MSG="Add <$_line> into <$_file>."
+ else
+ echo "$_line" > $_file || return $?
+ git add $_file || return $?
+ MSG="Create file <$_file> with <$_line> inside."
+ fi
+
+ test_tick
+ git commit --quiet -m "$MSG" $_file
+}
+
+HASH1=
+HASH2=
+HASH3=
+HASH4=
+
+test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' '
+ add_line_into_file "1: Hello World" hello &&
+ HASH1=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "2: A new day for git" hello &&
+ HASH2=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "3: Another new day for git" hello &&
+ HASH3=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "4: Ciao for now" hello &&
+ HASH4=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'bisect starts with only one bad' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect bad $HASH4 &&
+ git bisect next
+'
+
+test_expect_success 'bisect does not start with only one good' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect good $HASH1 || return 1
+
+ if git bisect next
+ then
+ echo Oops, should have failed.
+ false
+ else
+ :
+ fi
+'
+
+test_expect_success 'bisect start with one bad and good' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect next
+'
+
+test_expect_success 'bisect fails if given any junk instead of revs' '
+ git bisect reset &&
+ test_must_fail git bisect start foo $HASH1 -- &&
+ test_must_fail git bisect start $HASH4 $HASH1 bar -- &&
+ test -z "$(git for-each-ref "refs/bisect/*")" &&
+ test -z "$(ls .git/BISECT_* 2>/dev/null)" &&
+ git bisect start &&
+ test_must_fail git bisect good foo $HASH1 &&
+ test_must_fail git bisect good $HASH1 bar &&
+ test_must_fail git bisect bad frotz &&
+ test_must_fail git bisect bad $HASH3 $HASH4 &&
+ test_must_fail git bisect skip bar $HASH3 &&
+ test_must_fail git bisect skip $HASH1 foo &&
+ test -z "$(git for-each-ref "refs/bisect/*")" &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4
+'
+
+test_expect_success 'bisect reset: back in the master branch' '
+ git bisect reset &&
+ echo "* master" > branch.expect &&
+ git branch > branch.output &&
+ cmp branch.expect branch.output
+'
+
+test_expect_success 'bisect reset: back in another branch' '
+ git checkout -b other &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH3 &&
+ git bisect reset &&
+ echo " master" > branch.expect &&
+ echo "* other" >> branch.expect &&
+ git branch > branch.output &&
+ cmp branch.expect branch.output
+'
+
+test_expect_success 'bisect reset when not bisecting' '
+ git bisect reset &&
+ git branch > branch.output &&
+ cmp branch.expect branch.output
+'
+
+test_expect_success 'bisect reset removes packed refs' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH3 &&
+ git pack-refs --all --prune &&
+ git bisect next &&
+ git bisect reset &&
+ test -z "$(git for-each-ref "refs/bisect/*")" &&
+ test -z "$(git for-each-ref "refs/heads/bisect")"
+'
+
+test_expect_success 'bisect start: back in good branch' '
+ git branch > branch.output &&
+ grep "* other" branch.output > /dev/null &&
+ git bisect start $HASH4 $HASH1 -- &&
+ git bisect good &&
+ git bisect start $HASH4 $HASH1 -- &&
+ git bisect bad &&
+ git bisect reset &&
+ git branch > branch.output &&
+ grep "* other" branch.output > /dev/null
+'
+
+test_expect_success 'bisect start: no ".git/BISECT_START" if junk rev' '
+ git bisect start $HASH4 $HASH1 -- &&
+ git bisect good &&
+ test_must_fail git bisect start $HASH4 foo -- &&
+ git branch > branch.output &&
+ grep "* other" branch.output > /dev/null &&
+ test_must_fail test -e .git/BISECT_START
+'
+
+test_expect_success 'bisect start: no ".git/BISECT_START" if mistaken rev' '
+ git bisect start $HASH4 $HASH1 -- &&
+ git bisect good &&
+ test_must_fail git bisect start $HASH1 $HASH4 -- &&
+ git branch > branch.output &&
+ grep "* other" branch.output > /dev/null &&
+ test_must_fail test -e .git/BISECT_START
+'
+
+test_expect_success 'bisect start: no ".git/BISECT_START" if checkout error' '
+ echo "temp stuff" > hello &&
+ test_must_fail git bisect start $HASH4 $HASH1 -- &&
+ git branch &&
+ git branch > branch.output &&
+ grep "* other" branch.output > /dev/null &&
+ test_must_fail test -e .git/BISECT_START &&
+ test -z "$(git for-each-ref "refs/bisect/*")" &&
+ git checkout HEAD hello
+'
+
+# $HASH1 is good, $HASH4 is bad, we skip $HASH3
+# but $HASH2 is bad,
+# so we should find $HASH2 as the first bad commit
+test_expect_success 'bisect skip: successfull result' '
+ git bisect reset &&
+ git bisect start $HASH4 $HASH1 &&
+ git bisect skip &&
+ git bisect bad > my_bisect_log.txt &&
+ grep "$HASH2 is the first bad commit" my_bisect_log.txt &&
+ git bisect reset
+'
+
+# $HASH1 is good, $HASH4 is bad, we skip $HASH3 and $HASH2
+# so we should not be able to tell the first bad commit
+# among $HASH2, $HASH3 and $HASH4
+test_expect_success 'bisect skip: cannot tell between 3 commits' '
+ git bisect start $HASH4 $HASH1 &&
+ git bisect skip || return 1
+
+ if git bisect skip > my_bisect_log.txt
+ then
+ echo Oops, should have failed.
+ false
+ else
+ test $? -eq 2 &&
+ grep "first bad commit could be any of" my_bisect_log.txt &&
+ ! grep $HASH1 my_bisect_log.txt &&
+ grep $HASH2 my_bisect_log.txt &&
+ grep $HASH3 my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ git bisect reset
+ fi
+'
+
+# $HASH1 is good, $HASH4 is bad, we skip $HASH3
+# but $HASH2 is good,
+# so we should not be able to tell the first bad commit
+# among $HASH3 and $HASH4
+test_expect_success 'bisect skip: cannot tell between 2 commits' '
+ git bisect start $HASH4 $HASH1 &&
+ git bisect skip || return 1
+
+ if git bisect good > my_bisect_log.txt
+ then
+ echo Oops, should have failed.
+ false
+ else
+ test $? -eq 2 &&
+ grep "first bad commit could be any of" my_bisect_log.txt &&
+ ! grep $HASH1 my_bisect_log.txt &&
+ ! grep $HASH2 my_bisect_log.txt &&
+ grep $HASH3 my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ git bisect reset
+ fi
+'
+
+# $HASH1 is good, $HASH4 is both skipped and bad, we skip $HASH3
+# and $HASH2 is good,
+# so we should not be able to tell the first bad commit
+# among $HASH3 and $HASH4
+test_expect_success 'bisect skip: with commit both bad and skipped' '
+ git bisect start &&
+ git bisect skip &&
+ git bisect bad &&
+ git bisect good $HASH1 &&
+ git bisect skip &&
+ if git bisect good > my_bisect_log.txt
+ then
+ echo Oops, should have failed.
+ false
+ else
+ test $? -eq 2 &&
+ grep "first bad commit could be any of" my_bisect_log.txt &&
+ ! grep $HASH1 my_bisect_log.txt &&
+ ! grep $HASH2 my_bisect_log.txt &&
+ grep $HASH3 my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ git bisect reset
+ fi
+'
+
+# We want to automatically find the commit that
+# introduced "Another" into hello.
+test_expect_success \
+ '"git bisect run" simple case' \
+ 'echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "grep Another hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./test_script.sh > my_bisect_log.txt &&
+ grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
+ git bisect reset'
+
+# We want to automatically find the commit that
+# introduced "Ciao" into hello.
+test_expect_success \
+ '"git bisect run" with more complex "git bisect start"' \
+ 'echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "grep Ciao hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start $HASH4 $HASH1 &&
+ git bisect run ./test_script.sh > my_bisect_log.txt &&
+ grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
+ git bisect reset'
+
+# $HASH1 is good, $HASH5 is bad, we skip $HASH3
+# but $HASH4 is good,
+# so we should find $HASH5 as the first bad commit
+HASH5=
+test_expect_success 'bisect skip: add line and then a new test' '
+ add_line_into_file "5: Another new line." hello &&
+ HASH5=$(git rev-parse --verify HEAD) &&
+ git bisect start $HASH5 $HASH1 &&
+ git bisect skip &&
+ git bisect good > my_bisect_log.txt &&
+ grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
+ git bisect log > log_to_replay.txt &&
+ git bisect reset
+'
+
+test_expect_success 'bisect skip and bisect replay' '
+ git bisect replay log_to_replay.txt > my_bisect_log.txt &&
+ grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
+ git bisect reset
+'
+
+HASH6=
+test_expect_success 'bisect run & skip: cannot tell between 2' '
+ add_line_into_file "6: Yet a line." hello &&
+ HASH6=$(git rev-parse --verify HEAD) &&
+ echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
+ echo "grep line hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start $HASH6 $HASH1 &&
+ if git bisect run ./test_script.sh > my_bisect_log.txt
+ then
+ echo Oops, should have failed.
+ false
+ else
+ test $? -eq 2 &&
+ grep "first bad commit could be any of" my_bisect_log.txt &&
+ ! grep $HASH3 my_bisect_log.txt &&
+ ! grep $HASH6 my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ grep $HASH5 my_bisect_log.txt
+ fi
+'
+
+HASH7=
+test_expect_success 'bisect run & skip: find first bad' '
+ git bisect reset &&
+ add_line_into_file "7: Should be the last line." hello &&
+ HASH7=$(git rev-parse --verify HEAD) &&
+ echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "sed -ne \\\$p hello | grep Ciao > /dev/null && exit 125" >> test_script.sh &&
+ echo "sed -ne \\\$p hello | grep day > /dev/null && exit 125" >> test_script.sh &&
+ echo "grep Yet hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start $HASH7 $HASH1 &&
+ git bisect run ./test_script.sh > my_bisect_log.txt &&
+ grep "$HASH6 is the first bad commit" my_bisect_log.txt
+'
+
+test_expect_success 'bisect skip only one range' '
+ git bisect reset &&
+ git bisect start $HASH7 $HASH1 &&
+ git bisect skip $HASH1..$HASH5 &&
+ test "$HASH6" = "$(git rev-parse --verify HEAD)" &&
+ test_must_fail git bisect bad > my_bisect_log.txt &&
+ grep "first bad commit could be any of" my_bisect_log.txt
+'
+
+test_expect_success 'bisect skip many ranges' '
+ git bisect start $HASH7 $HASH1 &&
+ test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+ git bisect skip $HASH2 $HASH2.. ..$HASH5 &&
+ test "$HASH6" = "$(git rev-parse --verify HEAD)" &&
+ test_must_fail git bisect bad > my_bisect_log.txt &&
+ grep "first bad commit could be any of" my_bisect_log.txt
+'
+
+test_expect_success 'bisect starting with a detached HEAD' '
+ git bisect reset &&
+ git checkout master^ &&
+ HEAD=$(git rev-parse --verify HEAD) &&
+ git bisect start &&
+ test $HEAD = $(cat .git/BISECT_START) &&
+ git bisect reset &&
+ test $HEAD = $(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'bisect errors out if bad and good are mistaken' '
+ git bisect reset &&
+ test_must_fail git bisect start $HASH2 $HASH4 2> rev_list_error &&
+ grep "mistake good and bad" rev_list_error &&
+ git bisect reset
+'
+
+test_expect_success 'bisect does not create a "bisect" branch' '
+ git bisect reset &&
+ git bisect start $HASH7 $HASH1 &&
+ git branch bisect &&
+ rev_hash4=$(git rev-parse --verify HEAD) &&
+ test "$rev_hash4" = "$HASH4" &&
+ git branch -D bisect &&
+ git bisect good &&
+ git branch bisect &&
+ rev_hash6=$(git rev-parse --verify HEAD) &&
+ test "$rev_hash6" = "$HASH6" &&
+ git bisect good > my_bisect_log.txt &&
+ grep "$HASH7 is the first bad commit" my_bisect_log.txt &&
+ git bisect reset &&
+ rev_hash6=$(git rev-parse --verify bisect) &&
+ test "$rev_hash6" = "$HASH6" &&
+ git branch -D bisect
+'
+
+# This creates a "side" branch to test "siblings" cases.
+#
+# H1-H2-H3-H4-H5-H6-H7 <--other
+# \
+# S5-S6-S7 <--side
+#
+test_expect_success 'side branch creation' '
+ git bisect reset &&
+ git checkout -b side $HASH4 &&
+ add_line_into_file "5(side): first line on a side branch" hello2 &&
+ SIDE_HASH5=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "6(side): second line on a side branch" hello2 &&
+ SIDE_HASH6=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "7(side): third line on a side branch" hello2 &&
+ SIDE_HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'good merge base when good and bad are siblings' '
+ git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
+ grep "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 $HASH6 my_bisect_log.txt &&
+ git bisect reset
+'
+test_expect_success 'skipped merge base when good and bad are siblings' '
+ git bisect start "$SIDE_HASH7" "$HASH7" > my_bisect_log.txt &&
+ grep "merge base must be tested" my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ git bisect skip > my_bisect_log.txt 2>&1 &&
+ grep "warning" my_bisect_log.txt &&
+ grep $SIDE_HASH6 my_bisect_log.txt &&
+ git bisect reset
+'
+
+test_expect_success 'bad merge base when good and bad are siblings' '
+ git bisect start "$HASH7" HEAD > my_bisect_log.txt &&
+ grep "merge base must be tested" my_bisect_log.txt &&
+ grep $HASH4 my_bisect_log.txt &&
+ test_must_fail git bisect bad > my_bisect_log.txt 2>&1 &&
+ grep "merge base $HASH4 is bad" my_bisect_log.txt &&
+ grep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
+ git bisect reset
+'
+
+# This creates a few more commits (A and B) to test "siblings" cases
+# when a good and a bad rev have many merge bases.
+#
+# We should have the following:
+#
+# H1-H2-H3-H4-H5-H6-H7
+# \ \ \
+# S5-A \
+# \ \
+# S6-S7----B
+#
+# And there A and B have 2 merge bases (S5 and H5) that should be
+# reported by "git merge-base --all A B".
+#
+test_expect_success 'many merge bases creation' '
+ git checkout "$SIDE_HASH5" &&
+ git merge -m "merge HASH5 and SIDE_HASH5" "$HASH5" &&
+ A_HASH=$(git rev-parse --verify HEAD) &&
+ git checkout side &&
+ git merge -m "merge HASH7 and SIDE_HASH7" "$HASH7" &&
+ B_HASH=$(git rev-parse --verify HEAD) &&
+ git merge-base --all "$A_HASH" "$B_HASH" > merge_bases.txt &&
+ test $(wc -l < merge_bases.txt) = "2" &&
+ grep "$HASH5" merge_bases.txt &&
+ grep "$SIDE_HASH5" merge_bases.txt
+'
+
+test_expect_success 'good merge bases when good and bad are siblings' '
+ git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
+ grep "merge base must be tested" my_bisect_log.txt &&
+ git bisect good > my_bisect_log2.txt &&
+ grep "merge base must be tested" my_bisect_log2.txt &&
+ {
+ {
+ grep "$SIDE_HASH5" my_bisect_log.txt &&
+ grep "$HASH5" my_bisect_log2.txt
+ } || {
+ grep "$SIDE_HASH5" my_bisect_log2.txt &&
+ grep "$HASH5" my_bisect_log.txt
+ }
+ } &&
+ git bisect reset
+'
+
+test_expect_success 'optimized merge base checks' '
+ git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
+ grep "merge base must be tested" my_bisect_log.txt &&
+ grep "$HASH4" my_bisect_log.txt &&
+ git bisect good > my_bisect_log2.txt &&
+ test -f ".git/BISECT_ANCESTORS_OK" &&
+ test "$HASH6" = $(git rev-parse --verify HEAD) &&
+ git bisect bad > my_bisect_log3.txt &&
+ git bisect good "$A_HASH" > my_bisect_log4.txt &&
+ grep "merge base must be tested" my_bisect_log4.txt &&
+ test_must_fail test -f ".git/BISECT_ANCESTORS_OK"
+'
+
+# This creates another side branch called "parallel" with some files
+# in some directories, to test bisecting with paths.
+#
+# We should have the following:
+#
+# P1-P2-P3-P4-P5-P6-P7
+# / / /
+# H1-H2-H3-H4-H5-H6-H7
+# \ \ \
+# S5-A \
+# \ \
+# S6-S7----B
+#
+test_expect_success '"parallel" side branch creation' '
+ git bisect reset &&
+ git checkout -b parallel $HASH1 &&
+ mkdir dir1 dir2 &&
+ add_line_into_file "1(para): line 1 on parallel branch" dir1/file1 &&
+ PARA_HASH1=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "2(para): line 2 on parallel branch" dir2/file2 &&
+ PARA_HASH2=$(git rev-parse --verify HEAD) &&
+ add_line_into_file "3(para): line 3 on parallel branch" dir2/file3 &&
+ PARA_HASH3=$(git rev-parse --verify HEAD)
+ git merge -m "merge HASH4 and PARA_HASH3" "$HASH4" &&
+ PARA_HASH4=$(git rev-parse --verify HEAD)
+ add_line_into_file "5(para): add line on parallel branch" dir1/file1 &&
+ PARA_HASH5=$(git rev-parse --verify HEAD)
+ add_line_into_file "6(para): add line on parallel branch" dir2/file2 &&
+ PARA_HASH6=$(git rev-parse --verify HEAD)
+ git merge -m "merge HASH7 and PARA_HASH6" "$HASH7" &&
+ PARA_HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'restricting bisection on one dir' '
+ git bisect reset &&
+ git bisect start HEAD $HASH1 -- dir1 &&
+ para1=$(git rev-parse --verify HEAD) &&
+ test "$para1" = "$PARA_HASH1" &&
+ git bisect bad > my_bisect_log.txt &&
+ grep "$PARA_HASH1 is the first bad commit" my_bisect_log.txt
+'
+
+test_expect_success 'restricting bisection on one dir and a file' '
+ git bisect reset &&
+ git bisect start HEAD $HASH1 -- dir1 hello &&
+ para4=$(git rev-parse --verify HEAD) &&
+ test "$para4" = "$PARA_HASH4" &&
+ git bisect bad &&
+ hash3=$(git rev-parse --verify HEAD) &&
+ test "$hash3" = "$HASH3" &&
+ git bisect good &&
+ hash4=$(git rev-parse --verify HEAD) &&
+ test "$hash4" = "$HASH4" &&
+ git bisect good &&
+ para1=$(git rev-parse --verify HEAD) &&
+ test "$para1" = "$PARA_HASH1" &&
+ git bisect good > my_bisect_log.txt &&
+ grep "$PARA_HASH4 is the first bad commit" my_bisect_log.txt
+'
+
+test_expect_success 'skipping away from skipped commit' '
+ git bisect start $PARA_HASH7 $HASH1 &&
+ para4=$(git rev-parse --verify HEAD) &&
+ test "$para4" = "$PARA_HASH4" &&
+ git bisect skip &&
+ hash7=$(git rev-parse --verify HEAD) &&
+ test "$hash7" = "$HASH7" &&
+ git bisect skip &&
+ para3=$(git rev-parse --verify HEAD) &&
+ test "$para3" = "$PARA_HASH3"
+'
+
+test_expect_success 'erroring out when using bad path parameters' '
+ test_must_fail git bisect start $PARA_HASH7 $HASH1 -- foobar 2> error.txt &&
+ grep "bad path parameters" error.txt
+'
+
+#
+#
+test_done
diff --git a/t/t6031-merge-recursive.sh b/t/t6031-merge-recursive.sh
new file mode 100755
index 0000000..8a3304f
--- /dev/null
+++ b/t/t6031-merge-recursive.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='merge-recursive: handle file mode'
+. ./test-lib.sh
+
+if ! test "$(git config --bool core.filemode)" = false
+then
+ test_set_prereq FILEMODE
+fi
+
+test_expect_success 'mode change in one branch: keep changed version' '
+ : >file1 &&
+ git add file1 &&
+ git commit -m initial &&
+ git checkout -b a1 master &&
+ : >dummy &&
+ git add dummy &&
+ git commit -m a &&
+ git checkout -b b1 master &&
+ test_chmod +x file1 &&
+ git commit -m b1 &&
+ git checkout a1 &&
+ git merge-recursive master -- a1 b1 &&
+ git ls-files -s file1 | grep ^100755
+'
+
+test_expect_success FILEMODE 'verify executable bit on file' '
+ test -x file1
+'
+
+test_expect_success 'mode change in both branches: expect conflict' '
+ git reset --hard HEAD &&
+ git checkout -b a2 master &&
+ : >file2 &&
+ H=$(git hash-object file2) &&
+ test_chmod +x file2 &&
+ git commit -m a2 &&
+ git checkout -b b2 master &&
+ : >file2 &&
+ git add file2 &&
+ git commit -m b2 &&
+ git checkout a2 &&
+ (
+ git merge-recursive master -- a2 b2
+ test $? = 1
+ ) &&
+ git ls-files -u >actual &&
+ (
+ echo "100755 $H 2 file2"
+ echo "100644 $H 3 file2"
+ ) >expect &&
+ test_cmp actual expect &&
+ git ls-files -s file2 | grep ^100755
+'
+
+test_expect_success FILEMODE 'verify executable bit on file' '
+ test -x file2
+'
+
+test_done
diff --git a/t/t6032-merge-large-rename.sh b/t/t6032-merge-large-rename.sh
new file mode 100755
index 0000000..eac5eba
--- /dev/null
+++ b/t/t6032-merge-large-rename.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='merging with large rename matrix'
+. ./test-lib.sh
+
+count() {
+ i=1
+ while test $i -le $1; do
+ echo $i
+ i=$(($i + 1))
+ done
+}
+
+test_expect_success 'setup (initial)' '
+ touch file &&
+ git add . &&
+ git commit -m initial &&
+ git tag initial
+'
+
+make_text() {
+ echo $1: $2
+ for i in `count 20`; do
+ echo $1: $i
+ done
+ echo $1: $3
+}
+
+test_rename() {
+ test_expect_success "rename ($1, $2)" '
+ n='$1'
+ expect='$2'
+ git checkout -f master &&
+ git branch -D test$n || true &&
+ git reset --hard initial &&
+ for i in $(count $n); do
+ make_text $i initial initial >$i
+ done &&
+ git add . &&
+ git commit -m add=$n &&
+ for i in $(count $n); do
+ make_text $i changed initial >$i
+ done &&
+ git commit -a -m change=$n &&
+ git checkout -b test$n HEAD^ &&
+ for i in $(count $n); do
+ git rm $i
+ make_text $i initial changed >$i.moved
+ done &&
+ git add . &&
+ git commit -m change+rename=$n &&
+ case "$expect" in
+ ok) git merge master ;;
+ *) test_must_fail git merge master ;;
+ esac
+ '
+}
+
+test_rename 5 ok
+
+test_expect_success 'set diff.renamelimit to 4' '
+ git config diff.renamelimit 4
+'
+test_rename 4 ok
+test_rename 5 fail
+
+test_expect_success 'set merge.renamelimit to 5' '
+ git config merge.renamelimit 5
+'
+test_rename 5 ok
+test_rename 6 fail
+
+test_done
diff --git a/t/t6033-merge-crlf.sh b/t/t6033-merge-crlf.sh
new file mode 100755
index 0000000..e8d65ee
--- /dev/null
+++ b/t/t6033-merge-crlf.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description='merge conflict in crlf repo
+
+ b---M
+ / /
+ initial---a
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ git config core.autocrlf true &&
+ echo foo | append_cr >file &&
+ git add file &&
+ git commit -m "Initial" &&
+ git tag initial &&
+ git branch side &&
+ echo line from a | append_cr >file &&
+ git commit -m "add line from a" file &&
+ git tag a &&
+ git checkout side &&
+ echo line from b | append_cr >file &&
+ git commit -m "add line from b" file &&
+ git tag b &&
+ git checkout master
+'
+
+test_expect_success 'Check "ours" is CRLF' '
+ git reset --hard initial &&
+ git merge side -s ours &&
+ cat file | remove_cr | append_cr >file.temp &&
+ test_cmp file file.temp
+'
+
+test_expect_success 'Check that conflict file is CRLF' '
+ git reset --hard a &&
+ test_must_fail git merge side &&
+ cat file | remove_cr | append_cr >file.temp &&
+ test_cmp file file.temp
+'
+
+test_done
diff --git a/t/t6034-merge-rename-nocruft.sh b/t/t6034-merge-rename-nocruft.sh
new file mode 100755
index 0000000..65be95f
--- /dev/null
+++ b/t/t6034-merge-rename-nocruft.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+test_description='Merge-recursive merging renames'
+. ./test-lib.sh
+
+test_expect_success setup \
+'
+cat >A <<\EOF &&
+a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+c cccccccccccccccccccccccccccccccccccccccccccccccc
+d dddddddddddddddddddddddddddddddddddddddddddddddd
+e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+f ffffffffffffffffffffffffffffffffffffffffffffffff
+g gggggggggggggggggggggggggggggggggggggggggggggggg
+h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
+i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
+k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
+l llllllllllllllllllllllllllllllllllllllllllllllll
+m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
+o oooooooooooooooooooooooooooooooooooooooooooooooo
+EOF
+
+cat >M <<\EOF &&
+A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+EOF
+
+git add A M &&
+git commit -m "initial has A and M" &&
+git branch white &&
+git branch red &&
+git branch blue &&
+
+git checkout white &&
+sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
+sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+rm -f A M &&
+git update-index --add --remove A B M N &&
+git commit -m "white renames A->B, M->N" &&
+
+git checkout red &&
+echo created by red >R &&
+git update-index --add R &&
+git commit -m "red creates R" &&
+
+git checkout blue &&
+sed -e "/^o /s/.*/g : blue changes a line/" <A >B &&
+rm -f A &&
+mv B A &&
+git update-index A &&
+git commit -m "blue modify A" &&
+
+git checkout master'
+
+# This test broke in 65ac6e9c3f47807cb603af07a6a9e1a43bc119ae
+test_expect_success 'merge white into red (A->B,M->N)' \
+'
+ git checkout -b red-white red &&
+ git merge white &&
+ git write-tree >/dev/null || {
+ echo "BAD: merge did not complete"
+ return 1
+ }
+
+ test -f B || {
+ echo "BAD: B does not exist in working directory"
+ return 1
+ }
+ test -f N || {
+ echo "BAD: N does not exist in working directory"
+ return 1
+ }
+ test -f R || {
+ echo "BAD: R does not exist in working directory"
+ return 1
+ }
+
+ test -f A && {
+ echo "BAD: A still exists in working directory"
+ return 1
+ }
+ test -f M && {
+ echo "BAD: M still exists in working directory"
+ return 1
+ }
+ return 0
+'
+
+# This test broke in 8371234ecaaf6e14fe3f2082a855eff1bbd79ae9
+test_expect_success 'merge blue into white (A->B, mod A, A untracked)' \
+'
+ git checkout -b white-blue white &&
+ echo dirty >A &&
+ git merge blue &&
+ git write-tree >/dev/null || {
+ echo "BAD: merge did not complete"
+ return 1
+ }
+
+ test -f A || {
+ echo "BAD: A does not exist in working directory"
+ return 1
+ }
+ test `cat A` = dirty || {
+ echo "BAD: A content is wrong"
+ return 1
+ }
+ test -f B || {
+ echo "BAD: B does not exist in working directory"
+ return 1
+ }
+ test -f N || {
+ echo "BAD: N does not exist in working directory"
+ return 1
+ }
+ test -f M && {
+ echo "BAD: M still exists in working directory"
+ return 1
+ }
+ return 0
+'
+
+test_done
diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh
new file mode 100755
index 0000000..cd3190c
--- /dev/null
+++ b/t/t6035-merge-dir-to-symlink.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='merging when a directory was replaced with a symlink'
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='Symbolic links not supported, skipping tests.'
+ test_done
+fi
+
+test_expect_success 'create a commit where dir a/b changed to symlink' '
+ mkdir -p a/b/c a/b-2/c &&
+ > a/b/c/d &&
+ > a/b-2/c/d &&
+ > a/x &&
+ git add -A &&
+ git commit -m base &&
+ git tag start &&
+ rm -rf a/b &&
+ ln -s b-2 a/b &&
+ git add -A &&
+ git commit -m "dir to symlink"
+'
+
+test_expect_success 'keep a/b-2/c/d across checkout' '
+ git checkout HEAD^0 &&
+ git reset --hard master &&
+ git rm --cached a/b &&
+ git commit -m "untracked symlink remains" &&
+ git checkout start^0 &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success 'checkout should not have deleted a/b-2/c/d' '
+ git checkout HEAD^0 &&
+ git reset --hard master &&
+ git checkout start^0 &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success 'setup for merge test' '
+ git reset --hard &&
+ test -f a/b-2/c/d &&
+ echo x > a/x &&
+ git add a/x &&
+ git commit -m x &&
+ git tag baseline
+'
+
+test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ git merge -s resolve master &&
+ test -h a/b &&
+ test -f a/b-2/c/d
+'
+
+test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ git merge -s recursive master &&
+ test -h a/b &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
+ git reset --hard &&
+ git checkout start^0 &&
+ rm -rf a/b-2 &&
+ ln -s b a/b-2 &&
+ git add -A &&
+ git commit -m "dir a/b-2 to symlink" &&
+ git tag test2
+'
+
+test_expect_success 'merge should not have conflicts (resolve)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ git merge -s resolve test2 &&
+ test -h a/b-2 &&
+ test -f a/b/c/d
+'
+
+test_expect_failure 'merge should not have conflicts (recursive)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ git merge -s recursive test2 &&
+ test -h a/b-2 &&
+ test -f a/b/c/d
+'
+
+test_done
diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
new file mode 100755
index 0000000..b874141
--- /dev/null
+++ b/t/t6036-recursive-corner-cases.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='recursive merge corner cases'
+
+. ./test-lib.sh
+
+#
+# L1 L2
+# o---o
+# / \ / \
+# o X ?
+# \ / \ /
+# o---o
+# R1 R2
+#
+
+test_expect_success setup '
+ ten="0 1 2 3 4 5 6 7 8 9"
+ for i in $ten
+ do
+ echo line $i in a sample file
+ done >one &&
+ for i in $ten
+ do
+ echo line $i in another sample file
+ done >two &&
+ git add one two &&
+ test_tick && git commit -m initial &&
+
+ git branch L1 &&
+ git checkout -b R1 &&
+ git mv one three &&
+ test_tick && git commit -m R1 &&
+
+ git checkout L1 &&
+ git mv two three &&
+ test_tick && git commit -m L1 &&
+
+ git checkout L1^0 &&
+ test_tick && git merge -s ours R1 &&
+ git tag L2 &&
+
+ git checkout R1^0 &&
+ test_tick && git merge -s ours L1 &&
+ git tag R2
+'
+
+test_expect_success merge '
+ git reset --hard &&
+ git checkout L2^0 &&
+
+ test_must_fail git merge -s recursive R2^0
+'
+
+test_done
diff --git a/t/t6037-merge-ours-theirs.sh b/t/t6037-merge-ours-theirs.sh
new file mode 100755
index 0000000..8ab3d61
--- /dev/null
+++ b/t/t6037-merge-ours-theirs.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test_description='Merge-recursive ours and theirs variants'
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in 1 2 3 4 5 6 7 8 9
+ do
+ echo "$i"
+ done >file &&
+ git add file &&
+ cp file elif &&
+ git commit -m initial &&
+
+ sed -e "s/1/one/" -e "s/9/nine/" >file <elif &&
+ git commit -a -m ours &&
+
+ git checkout -b side HEAD^ &&
+
+ sed -e "s/9/nueve/" >file <elif &&
+ git commit -a -m theirs &&
+
+ git checkout master^0
+'
+
+test_expect_success 'plain recursive - should conflict' '
+ git reset --hard master &&
+ test_must_fail git merge -s recursive side &&
+ grep nine file &&
+ grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_expect_success 'recursive favouring theirs' '
+ git reset --hard master &&
+ git merge -s recursive -Xtheirs side &&
+ ! grep nine file &&
+ grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_expect_success 'recursive favouring ours' '
+ git reset --hard master &&
+ git merge -s recursive -X ours side &&
+ grep nine file &&
+ ! grep nueve file &&
+ ! grep 9 file &&
+ grep one file &&
+ ! grep 1 file
+'
+
+test_expect_success 'pull with -X' '
+ git reset --hard master && git pull -s recursive -Xours . side &&
+ git reset --hard master && git pull -s recursive -X ours . side &&
+ git reset --hard master && git pull -s recursive -Xtheirs . side &&
+ git reset --hard master && git pull -s recursive -X theirs . side &&
+ git reset --hard master && ! git pull -s recursive -X bork . side
+'
+
+test_done
diff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh
new file mode 100755
index 0000000..1785e17
--- /dev/null
+++ b/t/t6040-tracking-info.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+test_description='remote tracking stats'
+
+. ./test-lib.sh
+
+advance () {
+ echo "$1" >"$1" &&
+ git add "$1" &&
+ test_tick &&
+ git commit -m "$1"
+}
+
+test_expect_success setup '
+ for i in a b c;
+ do
+ advance $i || break
+ done &&
+ git clone . test &&
+ (
+ cd test &&
+ git checkout -b b1 origin &&
+ git reset --hard HEAD^ &&
+ advance d &&
+ git checkout -b b2 origin &&
+ git reset --hard b1 &&
+ git checkout -b b3 origin &&
+ git reset --hard HEAD^ &&
+ git checkout -b b4 origin &&
+ advance e &&
+ advance f
+ ) &&
+ git checkout -b follower --track master &&
+ advance g
+'
+
+script='s/^..\(b.\)[ 0-9a-f]*\[\([^]]*\)\].*/\1 \2/p'
+cat >expect <<\EOF
+b1 ahead 1, behind 1
+b2 ahead 1, behind 1
+b3 behind 1
+b4 ahead 2
+EOF
+
+test_expect_success 'branch -v' '
+ (
+ cd test &&
+ git branch -v
+ ) |
+ sed -n -e "$script" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'checkout' '
+ (
+ cd test && git checkout b1
+ ) >actual &&
+ grep "have 1 and 1 different" actual
+'
+
+test_expect_success 'checkout with local tracked branch' '
+ git checkout master &&
+ git checkout follower >actual
+ grep "is ahead of" actual
+'
+
+test_expect_success 'status' '
+ (
+ cd test &&
+ git checkout b1 >/dev/null &&
+ # reports nothing to commit
+ test_must_fail git commit --dry-run
+ ) >actual &&
+ grep "have 1 and 1 different" actual
+'
+
+test_expect_success 'status when tracking lightweight tags' '
+ git checkout master &&
+ git tag light &&
+ git branch --track lighttrack light >actual &&
+ grep "set up to track" actual &&
+ git checkout lighttrack
+'
+
+test_expect_success 'status when tracking annotated tags' '
+ git checkout master &&
+ git tag -m heavy heavy &&
+ git branch --track heavytrack heavy >actual &&
+ grep "set up to track" actual &&
+ git checkout heavytrack
+'
+
+test_expect_success 'setup tracking with branch --set-upstream on existing branch' '
+ git branch from-master master &&
+ test_must_fail git config branch.from-master.merge > actual &&
+ git branch --set-upstream from-master master &&
+ git config branch.from-master.merge > actual &&
+ grep -q "^refs/heads/master$" actual
+'
+
+test_expect_success '--set-upstream does not change branch' '
+ git branch from-master2 master &&
+ test_must_fail git config branch.from-master2.merge > actual &&
+ git rev-list from-master2 &&
+ git update-ref refs/heads/from-master2 from-master2^ &&
+ git rev-parse from-master2 >expect2 &&
+ git branch --set-upstream from-master2 master &&
+ git config branch.from-master.merge > actual &&
+ git rev-parse from-master2 >actual2 &&
+ grep -q "^refs/heads/master$" actual &&
+ cmp expect2 actual2
+'
+test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
new file mode 100755
index 0000000..203ffdb
--- /dev/null
+++ b/t/t6050-replace.sh
@@ -0,0 +1,224 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Christian Couder
+#
+test_description='Tests replace refs functionality'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_and_commit_file()
+{
+ _file="$1"
+ _msg="$2"
+
+ git add $_file || return $?
+ test_tick || return $?
+ git commit --quiet -m "$_file: $_msg"
+}
+
+HASH1=
+HASH2=
+HASH3=
+HASH4=
+HASH5=
+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 &&
+ 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 &&
+ 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 &&
+ add_and_commit_file hello "2 more lines" &&
+ HASH3=$(git rev-parse --verify HEAD) &&
+ 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 &&
+ 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 &&
+ 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 &&
+ add_and_commit_file hello "again 3 more lines" &&
+ HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'replace the author' '
+ git cat-file commit $HASH2 | grep "author A U Thor" &&
+ R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+ git cat-file commit $R | grep "author O Thor" &&
+ git update-ref refs/replace/$HASH2 $R &&
+ git show HEAD~5 | grep "O Thor" &&
+ git show $HASH2 | grep "O Thor"
+'
+
+test_expect_success 'test --no-replace-objects option' '
+ git cat-file commit $HASH2 | grep "author O Thor" &&
+ git --no-replace-objects cat-file commit $HASH2 | grep "author A U Thor" &&
+ git show $HASH2 | grep "O Thor" &&
+ git --no-replace-objects show $HASH2 | grep "A U Thor"
+'
+
+test_expect_success 'test GIT_NO_REPLACE_OBJECTS env variable' '
+ GIT_NO_REPLACE_OBJECTS=1 git cat-file commit $HASH2 | grep "author A U Thor" &&
+ GIT_NO_REPLACE_OBJECTS=1 git show $HASH2 | grep "A U Thor"
+'
+
+cat >tag.sig <<EOF
+object $HASH2
+type commit
+tag mytag
+tagger T A Gger <> 0 +0000
+
+EOF
+
+test_expect_success 'tag replaced commit' '
+ git mktag <tag.sig >.git/refs/tags/mytag 2>message
+'
+
+test_expect_success '"git fsck" works' '
+ 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)"
+'
+
+test_expect_success 'repack, clone and fetch work' '
+ git repack -a -d &&
+ git clone --no-hardlinks . clone_dir &&
+ cd clone_dir &&
+ git show HEAD~5 | grep "A U Thor" &&
+ git show $HASH2 | grep "A U Thor" &&
+ git cat-file commit $R &&
+ git repack -a -d &&
+ test_must_fail git cat-file commit $R &&
+ git fetch ../ "refs/replace/*:refs/replace/*" &&
+ git show HEAD~5 | grep "O Thor" &&
+ git show $HASH2 | grep "O Thor" &&
+ git cat-file commit $R &&
+ cd ..
+'
+
+test_expect_success '"git replace" listing and deleting' '
+ test "$HASH2" = "$(git replace -l)" &&
+ test "$HASH2" = "$(git replace)" &&
+ aa=${HASH2%??????????????????????????????????????} &&
+ test "$HASH2" = "$(git replace -l "$aa*")" &&
+ test_must_fail git replace -d $R &&
+ test_must_fail git replace -d &&
+ test_must_fail git replace -l -d $HASH2 &&
+ git replace -d $HASH2 &&
+ git show $HASH2 | grep "A U Thor" &&
+ test -z "$(git replace -l)"
+'
+
+test_expect_success '"git replace" replacing' '
+ git replace $HASH2 $R &&
+ git show $HASH2 | grep "O Thor" &&
+ test_must_fail git replace $HASH2 $R &&
+ git replace -f $HASH2 $R &&
+ test_must_fail git replace -f &&
+ test "$HASH2" = "$(git replace)"
+'
+
+# This creates a side branch where the bug in H2
+# does not appear because P2 is created by applying
+# H2 and squashing H5 into it.
+# P3, P4 and P6 are created by cherry-picking H3, H4
+# and H6 respectively.
+#
+# At this point, we should have the following:
+#
+# P2--P3--P4--P6
+# /
+# H1-H2-H3-H4-H5-H6-H7
+#
+# Then we replace H6 with P6.
+#
+test_expect_success 'create parallel branch without the bug' '
+ git replace -d $HASH2 &&
+ git show $HASH2 | grep "A U Thor" &&
+ git checkout $HASH1 &&
+ git cherry-pick $HASH2 &&
+ git show $HASH5 | git apply &&
+ git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
+ PARA2=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH3 &&
+ PARA3=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH4 &&
+ PARA4=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH6 &&
+ PARA6=$(git rev-parse --verify HEAD) &&
+ git replace $HASH6 $PARA6 &&
+ git checkout master &&
+ cur=$(git rev-parse --verify HEAD) &&
+ test "$cur" = "$HASH7" &&
+ git log --pretty=oneline | grep $PARA2 &&
+ git remote add cloned ./clone_dir
+'
+
+test_expect_success 'push to cloned repo' '
+ git push cloned $HASH6^:refs/heads/parallel &&
+ cd clone_dir &&
+ git checkout parallel &&
+ git log --pretty=oneline | grep $PARA2 &&
+ cd ..
+'
+
+test_expect_success 'push branch with replacement' '
+ git cat-file commit $PARA3 | grep "author A U Thor" &&
+ S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+ git cat-file commit $S | grep "author O Thor" &&
+ git replace $PARA3 $S &&
+ git show $HASH6~2 | grep "O Thor" &&
+ git show $PARA3 | grep "O Thor" &&
+ git push cloned $HASH6^:refs/heads/parallel2 &&
+ cd clone_dir &&
+ git checkout parallel2 &&
+ git log --pretty=oneline | grep $PARA3 &&
+ git show $PARA3 | grep "A U Thor" &&
+ cd ..
+'
+
+test_expect_success 'fetch branch with replacement' '
+ git branch tofetch $HASH6 &&
+ cd clone_dir &&
+ git fetch origin refs/heads/tofetch:refs/heads/parallel3
+ git log --pretty=oneline parallel3 | grep $PARA3
+ git show $PARA3 | grep "A U Thor"
+ cd ..
+'
+
+test_expect_success 'bisect and replacements' '
+ git bisect start $HASH7 $HASH1 &&
+ test "$S" = "$(git rev-parse --verify HEAD)" &&
+ git bisect reset &&
+ GIT_NO_REPLACE_OBJECTS=1 git bisect start $HASH7 $HASH1 &&
+ test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+ git bisect reset &&
+ git --no-replace-objects bisect start $HASH7 $HASH1 &&
+ test "$HASH4" = "$(git rev-parse --verify HEAD)" &&
+ git bisect reset
+'
+
+#
+#
+test_done
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
new file mode 100755
index 0000000..e673c25
--- /dev/null
+++ b/t/t6101-rev-parse-parents.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Johannes Schindelin
+#
+
+test_description='Test git rev-parse with different parent options'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
+
+date >path0
+git update-index --add path0
+save_tag tree git write-tree
+hide_error save_tag start unique_commit "start" tree
+save_tag second unique_commit "second" tree -p start
+hide_error save_tag start2 unique_commit "start2" tree
+save_tag two_parents unique_commit "next" tree -p second -p start2
+save_tag final unique_commit "final" tree -p two_parents
+
+test_expect_success 'start is valid' 'git rev-parse start | grep "^[0-9a-f]\{40\}$"'
+test_expect_success 'start^0' "test $(cat .git/refs/tags/start) = $(git rev-parse start^0)"
+test_expect_success 'start^1 not valid' "if git rev-parse --verify start^1; then false; else :; fi"
+test_expect_success 'second^1 = second^' "test $(git rev-parse second^1) = $(git rev-parse second^)"
+test_expect_success 'final^1^1^1' "test $(git rev-parse start) = $(git rev-parse final^1^1^1)"
+test_expect_success 'final^1^1^1 = final^^^' "test $(git rev-parse final^1^1^1) = $(git rev-parse final^^^)"
+test_expect_success 'final^1^2' "test $(git rev-parse start2) = $(git rev-parse final^1^2)"
+test_expect_success 'final^1^2 != final^1^1' "test $(git rev-parse final^1^2) != $(git rev-parse final^1^1)"
+test_expect_success 'final^1^3 not valid' "if git rev-parse --verify final^1^3; then false; else :; fi"
+test_expect_success '--verify start2^1' 'test_must_fail git rev-parse --verify start2^1'
+test_expect_success '--verify start2^0' 'git rev-parse --verify start2^0'
+test_expect_success 'final^1^@ = final^1^1 final^1^2' "test \"$(git rev-parse final^1^@)\" = \"$(git rev-parse final^1^1 final^1^2)\""
+test_expect_success 'final^1^! = final^1 ^final^1^1 ^final^1^2' "test \"$(git rev-parse final^1^\!)\" = \"$(git rev-parse final^1 ^final^1^1 ^final^1^2)\""
+
+test_expect_success 'repack for next test' 'git repack -a -d'
+test_expect_success 'short SHA-1 works' '
+ start=`git rev-parse --verify start` &&
+ echo $start &&
+ abbrv=`echo $start | sed s/.\$//` &&
+ echo $abbrv &&
+ abbrv=`git rev-parse --verify $abbrv` &&
+ echo $abbrv &&
+ test $start = $abbrv'
+
+test_done
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
new file mode 100755
index 0000000..876d1ab
--- /dev/null
+++ b/t/t6120-describe.sh
@@ -0,0 +1,174 @@
+#!/bin/sh
+
+test_description='test describe
+
+ B
+ .--------------o----o----o----x
+ / / /
+ o----o----o----o----o----. /
+ \ A c /
+ .------------o---o---o
+ D,R e
+'
+. ./test-lib.sh
+
+check_describe () {
+ expect="$1"
+ shift
+ R=$(git describe "$@" 2>err.actual)
+ S=$?
+ cat err.actual >&3
+ test_expect_success "describe $*" '
+ test $S = 0 &&
+ case "$R" in
+ $expect) echo happy ;;
+ *) echo "Oops - $R is not $expect";
+ false ;;
+ esac
+ '
+}
+
+test_expect_success setup '
+
+ test_tick &&
+ echo one >file && git add file && git commit -m initial &&
+ one=$(git rev-parse HEAD) &&
+
+ git describe --always HEAD &&
+
+ test_tick &&
+ echo two >file && git add file && git commit -m second &&
+ two=$(git rev-parse HEAD) &&
+
+ test_tick &&
+ echo three >file && git add file && git commit -m third &&
+
+ test_tick &&
+ echo A >file && git add file && git commit -m A &&
+ test_tick &&
+ git tag -a -m A A &&
+
+ test_tick &&
+ echo c >file && git add file && git commit -m c &&
+ test_tick &&
+ git tag c &&
+
+ git reset --hard $two &&
+ test_tick &&
+ echo B >side && git add side && git commit -m B &&
+ test_tick &&
+ git tag -a -m B B &&
+
+ test_tick &&
+ git merge -m Merged c &&
+ merged=$(git rev-parse HEAD) &&
+
+ git reset --hard $two &&
+ test_tick &&
+ echo D >another && git add another && git commit -m D &&
+ test_tick &&
+ git tag -a -m D D &&
+ test_tick &&
+ git tag -a -m R R &&
+
+ test_tick &&
+ echo DD >another && git commit -a -m another &&
+
+ test_tick &&
+ git tag e &&
+
+ test_tick &&
+ echo DDD >another && git commit -a -m "yet another" &&
+
+ test_tick &&
+ git merge -m Merged $merged &&
+
+ test_tick &&
+ echo X >file && echo X >side && git add file side &&
+ git commit -m x
+
+'
+
+check_describe A-* HEAD
+check_describe A-* HEAD^
+check_describe R-* HEAD^^
+check_describe A-* HEAD^^2
+check_describe B HEAD^^2^
+check_describe R-* HEAD^^^
+
+check_describe c-* --tags HEAD
+check_describe c-* --tags HEAD^
+check_describe e-* --tags HEAD^^
+check_describe c-* --tags HEAD^^2
+check_describe B --tags HEAD^^2^
+check_describe e --tags HEAD^^^
+
+check_describe heads/master --all HEAD
+check_describe tags/c-* --all HEAD^
+check_describe tags/e --all HEAD^^^
+
+check_describe B-0-* --long HEAD^^2^
+check_describe A-3-* --long HEAD^^2
+
+: >err.expect
+check_describe A --all A^0
+test_expect_success 'no warning was displayed for A' '
+ test_cmp err.expect err.actual
+'
+
+test_expect_success 'rename tag A to Q locally' '
+ mv .git/refs/tags/A .git/refs/tags/Q
+'
+cat - >err.expect <<EOF
+warning: tag 'A' is really 'Q' here
+EOF
+check_describe A-* HEAD
+test_expect_success 'warning was displayed for Q' '
+ test_cmp err.expect err.actual
+'
+test_expect_success 'rename tag Q back to A' '
+ mv .git/refs/tags/Q .git/refs/tags/A
+'
+
+test_expect_success 'pack tag refs' 'git pack-refs'
+check_describe A-* HEAD
+
+check_describe "A-*[0-9a-f]" --dirty
+
+test_expect_success 'set-up dirty work tree' '
+ echo >>file
+'
+
+check_describe "A-*[0-9a-f]-dirty" --dirty
+
+check_describe "A-*[0-9a-f].mod" --dirty=.mod
+
+test_expect_success 'describe --dirty HEAD' '
+ test_must_fail git describe --dirty HEAD
+'
+
+test_expect_success 'set-up matching pattern tests' '
+ git tag -a -m test-annotated test-annotated &&
+ echo >>file &&
+ test_tick &&
+ git commit -a -m "one more" &&
+ git tag test1-lightweight &&
+ echo >>file &&
+ test_tick &&
+ git commit -a -m "yet another" &&
+ git tag test2-lightweight &&
+ echo >>file &&
+ test_tick &&
+ git commit -a -m "even more"
+
+'
+
+check_describe "test-annotated-*" --match="test-*"
+
+check_describe "test1-lightweight-*" --tags --match="test1-*"
+
+check_describe "test2-lightweight-*" --tags --match="test2-*"
+
+check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
+
+test_done
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
new file mode 100755
index 0000000..42f8ece
--- /dev/null
+++ b/t/t6200-fmt-merge-msg.sh
@@ -0,0 +1,371 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, Junio C Hamano
+#
+
+test_description='fmt-merge-msg test'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo one >one &&
+ git add one &&
+ test_tick &&
+ git commit -m "Initial" &&
+
+ git clone . remote &&
+
+ echo uno >one &&
+ echo dos >two &&
+ git add two &&
+ test_tick &&
+ git commit -a -m "Second" &&
+
+ git checkout -b left &&
+
+ echo "c1" >one &&
+ test_tick &&
+ git commit -a -m "Common #1" &&
+
+ echo "c2" >one &&
+ test_tick &&
+ git commit -a -m "Common #2" &&
+
+ git branch right &&
+
+ echo "l3" >two &&
+ test_tick &&
+ git commit -a -m "Left #3" &&
+
+ echo "l4" >two &&
+ test_tick &&
+ git commit -a -m "Left #4" &&
+
+ echo "l5" >two &&
+ test_tick &&
+ git commit -a -m "Left #5" &&
+ git tag tag-l5 &&
+
+ git checkout right &&
+
+ echo "r3" >three &&
+ git add three &&
+ test_tick &&
+ git commit -a -m "Right #3" &&
+ git tag tag-r3 &&
+
+ echo "r4" >three &&
+ test_tick &&
+ git commit -a -m "Right #4" &&
+
+ echo "r5" >three &&
+ test_tick &&
+ git commit -a -m "Right #5" &&
+
+ git checkout -b long &&
+ i=0 &&
+ while test $i -lt 30
+ do
+ test_commit $i one &&
+ i=$(($i+1))
+ done &&
+
+ git show-branch
+'
+
+cat >expected <<\EOF
+Merge branch 'left'
+EOF
+
+test_expect_success 'merge-msg test #1' '
+
+ git checkout master &&
+ git fetch . left &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+Merge branch 'left' of $(pwd)
+EOF
+
+test_expect_success 'merge-msg test #2' '
+
+ git checkout master &&
+ git fetch "$(pwd)" left &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+Merge branch 'left'
+
+* left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg test #3-1' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.log true &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . left &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge-msg test #3-2' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary true &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . left &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+Merge branches 'left' and 'right'
+
+* left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+
+* right:
+ Right #5
+ Right #4
+ Right #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg test #4-1' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.log true &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . left right &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge-msg test #4-2' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary true &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . left right &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge-msg test #5-1' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.log yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . left right &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge-msg test #5-2' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . left right &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge-msg -F' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . left right &&
+
+ git fmt-merge-msg -F .git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge-msg -F in subdirectory' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . left right &&
+ mkdir sub &&
+ cp .git/FETCH_HEAD sub/FETCH_HEAD &&
+ (
+ cd sub &&
+ git fmt-merge-msg -F FETCH_HEAD >../actual
+ ) &&
+ test_cmp expected actual
+'
+
+test_expect_success 'merge-msg with nothing to merge' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ (
+ cd remote &&
+ git checkout -b unrelated &&
+ test_tick &&
+ git fetch origin &&
+ git fmt-merge-msg <.git/FETCH_HEAD >../actual
+ ) &&
+
+ test_cmp /dev/null actual
+'
+
+cat >expected <<\EOF
+Merge tag 'tag-r3'
+
+* tag 'tag-r3':
+ Right #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg tag' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . tag tag-r3 &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+Merge tags 'tag-r3' and 'tag-l5'
+
+* tag 'tag-r3':
+ Right #3
+ Common #2
+ Common #1
+
+* tag 'tag-l5':
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg two tags' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . tag tag-r3 tag tag-l5 &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+Merge branch 'left', tag 'tag-r3'
+
+* tag 'tag-r3':
+ Right #3
+ Common #2
+ Common #1
+
+* left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg tag and branch' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . tag tag-r3 left &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+Merge branch 'long'
+
+* long: (35 commits)
+EOF
+
+test_expect_success 'merge-msg lots of commits' '
+
+ git checkout master &&
+ test_tick &&
+ git fetch . long &&
+
+ i=29 &&
+ while test $i -gt 9
+ do
+ echo " $i" &&
+ i=$(($i-1))
+ done >>expected &&
+ echo " ..." >>expected
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
new file mode 100755
index 0000000..7dc8a51
--- /dev/null
+++ b/t/t6300-for-each-ref.sh
@@ -0,0 +1,362 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Andy Parkins
+#
+
+test_description='for-each-ref test'
+
+. ./test-lib.sh
+
+# Mon Jul 3 15:18:43 2006 +0000
+datestamp=1151939923
+setdate_and_increment () {
+ GIT_COMMITTER_DATE="$datestamp +0200"
+ datestamp=$(expr "$datestamp" + 1)
+ GIT_AUTHOR_DATE="$datestamp +0200"
+ datestamp=$(expr "$datestamp" + 1)
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+test_expect_success 'Create sample commit with known timestamp' '
+ setdate_and_increment &&
+ echo "Using $datestamp" > one &&
+ git add one &&
+ git commit -m "Initial" &&
+ setdate_and_increment &&
+ git tag -a -m "Tagging at $datestamp" testtag
+'
+
+test_expect_success 'Create upstream config' '
+ git update-ref refs/remotes/origin/master master &&
+ git remote add origin nowhere &&
+ git config branch.master.remote origin &&
+ git config branch.master.merge refs/heads/master
+'
+
+test_atom() {
+ case "$1" in
+ head) ref=refs/heads/master ;;
+ tag) ref=refs/tags/testtag ;;
+ esac
+ printf '%s\n' "$3" >expected
+ test_expect_${4:-success} "basic atom: $1 $2" "
+ git for-each-ref --format='%($2)' $ref >actual &&
+ test_cmp expected actual
+ "
+}
+
+test_atom head refname refs/heads/master
+test_atom head upstream refs/remotes/origin/master
+test_atom head objecttype commit
+test_atom head objectsize 171
+test_atom head objectname 67a36f10722846e891fbada1ba48ed035de75581
+test_atom head tree 0e51c00fcb93dffc755546f27593d511e1bdb46f
+test_atom head parent ''
+test_atom head numparent 0
+test_atom head object ''
+test_atom head type ''
+test_atom head author 'A U Thor <author@example.com> 1151939924 +0200'
+test_atom head authorname 'A U Thor'
+test_atom head authoremail '<author@example.com>'
+test_atom head authordate 'Mon Jul 3 17:18:44 2006 +0200'
+test_atom head committer 'C O Mitter <committer@example.com> 1151939923 +0200'
+test_atom head committername 'C O Mitter'
+test_atom head committeremail '<committer@example.com>'
+test_atom head committerdate 'Mon Jul 3 17:18:43 2006 +0200'
+test_atom head tag ''
+test_atom head tagger ''
+test_atom head taggername ''
+test_atom head taggeremail ''
+test_atom head taggerdate ''
+test_atom head creator 'C O Mitter <committer@example.com> 1151939923 +0200'
+test_atom head creatordate 'Mon Jul 3 17:18:43 2006 +0200'
+test_atom head subject 'Initial'
+test_atom head body ''
+test_atom head contents 'Initial
+'
+
+test_atom tag refname refs/tags/testtag
+test_atom tag upstream ''
+test_atom tag objecttype tag
+test_atom tag objectsize 154
+test_atom tag objectname 98b46b1d36e5b07909de1b3886224e3e81e87322
+test_atom tag tree ''
+test_atom tag parent ''
+test_atom tag numparent ''
+test_atom tag object '67a36f10722846e891fbada1ba48ed035de75581'
+test_atom tag type 'commit'
+test_atom tag author ''
+test_atom tag authorname ''
+test_atom tag authoremail ''
+test_atom tag authordate ''
+test_atom tag committer ''
+test_atom tag committername ''
+test_atom tag committeremail ''
+test_atom tag committerdate ''
+test_atom tag tag 'testtag'
+test_atom tag tagger 'C O Mitter <committer@example.com> 1151939925 +0200'
+test_atom tag taggername 'C O Mitter'
+test_atom tag taggeremail '<committer@example.com>'
+test_atom tag taggerdate 'Mon Jul 3 17:18:45 2006 +0200'
+test_atom tag creator 'C O Mitter <committer@example.com> 1151939925 +0200'
+test_atom tag creatordate 'Mon Jul 3 17:18:45 2006 +0200'
+test_atom tag subject 'Tagging at 1151939927'
+test_atom tag body ''
+test_atom tag contents 'Tagging at 1151939927
+'
+
+test_expect_success 'Check invalid atoms names are errors' '
+ test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
+'
+
+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 &&
+ git for-each-ref --format="%(authordate) %(authordate:default)" refs/heads &&
+ git for-each-ref --format="%(authordate:default) %(authordate:default)" refs/heads
+'
+
+test_expect_success 'Check valid format specifiers for date fields' '
+ git for-each-ref --format="%(authordate:default)" refs/heads &&
+ git for-each-ref --format="%(authordate:relative)" refs/heads &&
+ git for-each-ref --format="%(authordate:short)" refs/heads &&
+ git for-each-ref --format="%(authordate:local)" refs/heads &&
+ git for-each-ref --format="%(authordate:iso8601)" refs/heads &&
+ git for-each-ref --format="%(authordate:rfc2822)" refs/heads
+'
+
+test_expect_success 'Check invalid format specifiers are errors' '
+ test_must_fail git for-each-ref --format="%(authordate:INVALID)" refs/heads
+'
+
+cat >expected <<\EOF
+'refs/heads/master' 'Mon Jul 3 17:18:43 2006 +0200' 'Mon Jul 3 17:18:44 2006 +0200'
+'refs/tags/testtag' 'Mon Jul 3 17:18:45 2006 +0200'
+EOF
+
+test_expect_success 'Check unformatted date fields output' '
+ (git for-each-ref --shell --format="%(refname) %(committerdate) %(authordate)" refs/heads &&
+ git for-each-ref --shell --format="%(refname) %(taggerdate)" refs/tags) >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Check format "default" formatted date fields output' '
+ f=default &&
+ (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+ git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+ test_cmp expected actual
+'
+
+# Don't know how to do relative check because I can't know when this script
+# is going to be run and can't fake the current time to git, and hence can't
+# provide expected output. Instead, I'll just make sure that "relative"
+# doesn't exit in error
+#
+#cat >expected <<\EOF
+#
+#EOF
+#
+test_expect_success 'Check format "relative" date fields output' '
+ f=relative &&
+ (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+ git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' '2006-07-03' '2006-07-03'
+'refs/tags/testtag' '2006-07-03'
+EOF
+
+test_expect_success 'Check format "short" date fields output' '
+ f=short &&
+ (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+ git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' 'Mon Jul 3 15:18:43 2006' 'Mon Jul 3 15:18:44 2006'
+'refs/tags/testtag' 'Mon Jul 3 15:18:45 2006'
+EOF
+
+test_expect_success 'Check format "local" date fields output' '
+ f=local &&
+ (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+ git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' '2006-07-03 17:18:43 +0200' '2006-07-03 17:18:44 +0200'
+'refs/tags/testtag' '2006-07-03 17:18:45 +0200'
+EOF
+
+test_expect_success 'Check format "iso8601" date fields output' '
+ f=iso8601 &&
+ (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+ git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' 'Mon, 3 Jul 2006 17:18:43 +0200' 'Mon, 3 Jul 2006 17:18:44 +0200'
+'refs/tags/testtag' 'Mon, 3 Jul 2006 17:18:45 +0200'
+EOF
+
+test_expect_success 'Check format "rfc2822" date fields output' '
+ f=rfc2822 &&
+ (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+ git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+refs/heads/master
+refs/remotes/origin/master
+refs/tags/testtag
+EOF
+
+test_expect_success 'Verify ascending sort' '
+ git for-each-ref --format="%(refname)" --sort=refname >actual &&
+ test_cmp expected actual
+'
+
+
+cat >expected <<\EOF
+refs/tags/testtag
+refs/remotes/origin/master
+refs/heads/master
+EOF
+
+test_expect_success 'Verify descending sort' '
+ git for-each-ref --format="%(refname)" --sort=-refname >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master'
+'refs/remotes/origin/master'
+'refs/tags/testtag'
+EOF
+
+test_expect_success 'Quoting style: shell' '
+ git for-each-ref --shell --format="%(refname)" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Quoting style: perl' '
+ git for-each-ref --perl --format="%(refname)" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Quoting style: python' '
+ git for-each-ref --python --format="%(refname)" >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+"refs/heads/master"
+"refs/remotes/origin/master"
+"refs/tags/testtag"
+EOF
+
+test_expect_success 'Quoting style: tcl' '
+ git for-each-ref --tcl --format="%(refname)" >actual &&
+ test_cmp expected actual
+'
+
+for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
+ test_expect_success "more than one quoting style: $i" "
+ git for-each-ref $i 2>&1 | (read line &&
+ case \$line in
+ \"error: more than one quoting style\"*) : happy;;
+ *) false
+ esac)
+ "
+done
+
+cat >expected <<\EOF
+master
+testtag
+EOF
+
+test_expect_success 'Check short refname format' '
+ (git for-each-ref --format="%(refname:short)" refs/heads &&
+ git for-each-ref --format="%(refname:short)" refs/tags) >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+origin/master
+EOF
+
+test_expect_success 'Check short upstream format' '
+ git for-each-ref --format="%(upstream:short)" refs/heads >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+67a36f1
+EOF
+
+test_expect_success 'Check short objectname format' '
+ git for-each-ref --format="%(objectname:short)" refs/heads >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'Check for invalid refname format' '
+ test_must_fail git for-each-ref --format="%(refname:INVALID)"
+'
+
+cat >expected <<\EOF
+heads/master
+tags/master
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs (strict)' '
+ git config --bool core.warnambiguousrefs true &&
+ git checkout -b newtag &&
+ echo "Using $datestamp" > one &&
+ git add one &&
+ git commit -m "Branch" &&
+ setdate_and_increment &&
+ git tag -m "Tagging at $datestamp" master &&
+ git for-each-ref --format "%(refname:short)" refs/heads/master refs/tags/master >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+heads/master
+master
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs (loose)' '
+ git config --bool core.warnambiguousrefs false &&
+ git for-each-ref --format "%(refname:short)" refs/heads/master refs/tags/master >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+heads/ambiguous
+ambiguous
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs II (loose)' '
+ git checkout master &&
+ git tag ambiguous testtag^0 &&
+ git branch ambiguous testtag^0 &&
+ git for-each-ref --format "%(refname:short)" refs/heads/ambiguous refs/tags/ambiguous >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'an unusual tag with an incomplete line' '
+
+ git tag -m "bogo" bogo &&
+ bogo=$(git cat-file tag bogo) &&
+ bogo=$(printf "%s" "$bogo" | git mktag) &&
+ git tag -f bogo "$bogo" &&
+ git for-each-ref --format "%(body)" refs/tags/bogo
+
+'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
new file mode 100755
index 0000000..65a35d9
--- /dev/null
+++ b/t/t7001-mv.sh
@@ -0,0 +1,258 @@
+#!/bin/sh
+
+test_description='git mv in subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+ 'prepare reference tree' \
+ 'mkdir path0 path1 &&
+ cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+ git add path0/COPYING &&
+ git commit -m add -a'
+
+test_expect_success \
+ 'moving the file out of subdirectory' \
+ 'cd path0 && git mv COPYING ../path1/COPYING'
+
+# in path0 currently
+test_expect_success \
+ 'commiting the change' \
+ 'cd .. && git commit -m move-out -a'
+
+test_expect_success \
+ 'checking the commit' \
+ 'git diff-tree -r -M --name-status HEAD^ HEAD | \
+ grep "^R100..*path0/COPYING..*path1/COPYING"'
+
+test_expect_success \
+ 'moving the file back into subdirectory' \
+ 'cd path0 && git mv ../path1/COPYING COPYING'
+
+# in path0 currently
+test_expect_success \
+ 'commiting the change' \
+ 'cd .. && git commit -m move-in -a'
+
+test_expect_success \
+ 'checking the commit' \
+ 'git diff-tree -r -M --name-status HEAD^ HEAD | \
+ grep "^R100..*path1/COPYING..*path0/COPYING"'
+
+test_expect_success \
+ 'checking -k on non-existing file' \
+ 'git mv -k idontexist path0'
+
+test_expect_success \
+ 'checking -k on untracked file' \
+ 'touch untracked1 &&
+ git mv -k untracked1 path0 &&
+ test -f untracked1 &&
+ test ! -f path0/untracked1'
+
+test_expect_success \
+ 'checking -k on multiple untracked files' \
+ 'touch untracked2 &&
+ git mv -k untracked1 untracked2 path0 &&
+ test -f untracked1 &&
+ test -f untracked2 &&
+ test ! -f path0/untracked1 &&
+ test ! -f path0/untracked2'
+
+test_expect_success \
+ 'checking -f on untracked file with existing target' \
+ 'touch path0/untracked1 &&
+ git mv -f untracked1 path0
+ test ! -f .git/index.lock &&
+ test -f untracked1 &&
+ test -f path0/untracked1'
+
+# clean up the mess in case bad things happen
+rm -f idontexist untracked1 untracked2 \
+ path0/idontexist path0/untracked1 path0/untracked2 \
+ .git/index.lock
+
+test_expect_success \
+ 'adding another file' \
+ 'cp "$TEST_DIRECTORY"/../README path0/README &&
+ git add path0/README &&
+ git commit -m add2 -a'
+
+test_expect_success \
+ 'moving whole subdirectory' \
+ 'git mv path0 path2'
+
+test_expect_success \
+ 'commiting the change' \
+ 'git commit -m dir-move -a'
+
+test_expect_success \
+ 'checking the commit' \
+ 'git diff-tree -r -M --name-status HEAD^ HEAD | \
+ grep "^R100..*path0/COPYING..*path2/COPYING" &&
+ git diff-tree -r -M --name-status HEAD^ HEAD | \
+ grep "^R100..*path0/README..*path2/README"'
+
+test_expect_success \
+ 'succeed when source is a prefix of destination' \
+ 'git mv path2/COPYING path2/COPYING-renamed'
+
+test_expect_success \
+ 'moving whole subdirectory into subdirectory' \
+ 'git mv path2 path1'
+
+test_expect_success \
+ 'commiting the change' \
+ 'git commit -m dir-move -a'
+
+test_expect_success \
+ 'checking the commit' \
+ 'git diff-tree -r -M --name-status HEAD^ HEAD | \
+ grep "^R100..*path2/COPYING..*path1/path2/COPYING" &&
+ git diff-tree -r -M --name-status HEAD^ HEAD | \
+ grep "^R100..*path2/README..*path1/path2/README"'
+
+test_expect_success \
+ 'do not move directory over existing directory' \
+ 'mkdir path0 && mkdir path0/path2 && test_must_fail git mv path2 path0'
+
+test_expect_success \
+ 'move into "."' \
+ 'git mv path1/path2/ .'
+
+test_expect_success "Michael Cassar's test case" '
+ rm -fr .git papers partA &&
+ git init &&
+ mkdir -p papers/unsorted papers/all-papers partA &&
+ echo a > papers/unsorted/Thesis.pdf &&
+ echo b > partA/outline.txt &&
+ echo c > papers/unsorted/_another &&
+ git add papers partA &&
+ T1=`git write-tree` &&
+
+ git mv papers/unsorted/Thesis.pdf papers/all-papers/moo-blah.pdf &&
+
+ T=`git write-tree` &&
+ git ls-tree -r $T | grep partA/outline.txt || {
+ git ls-tree -r $T
+ (exit 1)
+ }
+'
+
+rm -fr papers partA path?
+
+test_expect_success "Sergey Vlasov's test case" '
+ rm -fr .git &&
+ git init &&
+ mkdir ab &&
+ date >ab.c &&
+ date >ab/d &&
+ git add ab.c ab &&
+ git commit -m 'initial' &&
+ git mv ab a
+'
+
+test_expect_success 'absolute pathname' '(
+
+ rm -fr mine &&
+ mkdir mine &&
+ cd mine &&
+ test_create_repo one &&
+ cd one &&
+ mkdir sub &&
+ >sub/file &&
+ git add sub/file &&
+
+ git mv sub "$(pwd)/in" &&
+ ! test -d sub &&
+ test -d in &&
+ git ls-files --error-unmatch in/file
+
+
+)'
+
+test_expect_success 'absolute pathname outside should fail' '(
+
+ rm -fr mine &&
+ mkdir mine &&
+ cd mine &&
+ out=$(pwd) &&
+ test_create_repo one &&
+ cd one &&
+ mkdir sub &&
+ >sub/file &&
+ git add sub/file &&
+
+ test_must_fail git mv sub "$out/out" &&
+ test -d sub &&
+ ! test -d ../in &&
+ git ls-files --error-unmatch sub/file
+
+)'
+
+test_expect_success 'git mv to move multiple sources into a directory' '
+ rm -fr .git && git init &&
+ mkdir dir other &&
+ >dir/a.txt &&
+ >dir/b.txt &&
+ git add dir/?.txt &&
+ git mv dir/a.txt dir/b.txt other &&
+ git ls-files >actual &&
+ { echo other/a.txt; echo other/b.txt; } >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git mv should not change sha1 of moved cache entry' '
+
+ rm -fr .git &&
+ git init &&
+ echo 1 >dirty &&
+ git add dirty &&
+ entry="$(git ls-files --stage dirty | cut -f 1)"
+ git mv dirty dirty2 &&
+ [ "$entry" = "$(git ls-files --stage dirty2 | cut -f 1)" ] &&
+ echo 2 >dirty2 &&
+ git mv dirty2 dirty &&
+ [ "$entry" = "$(git ls-files --stage dirty | cut -f 1)" ]
+
+'
+
+rm -f dirty dirty2
+
+test_expect_success SYMLINKS 'git mv should overwrite symlink to a file' '
+
+ rm -fr .git &&
+ git init &&
+ echo 1 >moved &&
+ ln -s moved symlink &&
+ git add moved symlink &&
+ test_must_fail git mv moved symlink &&
+ git mv -f moved symlink &&
+ ! test -e moved &&
+ test -f symlink &&
+ test "$(cat symlink)" = 1 &&
+ git update-index --refresh &&
+ git diff-files --quiet
+
+'
+
+rm -f moved symlink
+
+test_expect_success SYMLINKS 'git mv should overwrite file with a symlink' '
+
+ rm -fr .git &&
+ git init &&
+ echo 1 >moved &&
+ ln -s moved symlink &&
+ git add moved symlink &&
+ test_must_fail git mv symlink moved &&
+ git mv -f symlink moved &&
+ ! test -e symlink &&
+ test -h moved &&
+ git update-index --refresh &&
+ git diff-files --quiet
+
+'
+
+rm -f moved symlink
+
+test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
new file mode 100755
index 0000000..0da13a8
--- /dev/null
+++ b/t/t7003-filter-branch.sh
@@ -0,0 +1,348 @@
+#!/bin/sh
+
+test_description='git filter-branch'
+. ./test-lib.sh
+
+make_commit () {
+ lower=$(echo $1 | tr '[A-Z]' '[a-z]')
+ echo $lower > $lower
+ git add $lower
+ test_tick
+ git commit -m $1
+ git tag $1
+}
+
+test_expect_success 'setup' '
+ make_commit A
+ make_commit B
+ git checkout -b branch B
+ make_commit D
+ mkdir dir
+ make_commit dir/D
+ make_commit E
+ git checkout master
+ make_commit C
+ git checkout branch
+ git merge C
+ git tag F
+ make_commit G
+ make_commit H
+'
+
+H=$(git rev-parse H)
+
+test_expect_success 'rewrite identically' '
+ git filter-branch branch
+'
+test_expect_success 'result is really identical' '
+ test $H = $(git rev-parse HEAD)
+'
+
+test_expect_success 'rewrite bare repository identically' '
+ (git config core.bare true && cd .git &&
+ git filter-branch branch > filter-output 2>&1 &&
+ ! fgrep fatal filter-output)
+'
+git config core.bare false
+test_expect_success 'result is really identical' '
+ test $H = $(git rev-parse HEAD)
+'
+
+TRASHDIR=$(pwd)
+test_expect_success 'correct GIT_DIR while using -d' '
+ mkdir drepo &&
+ ( cd drepo &&
+ git init &&
+ test_commit drepo &&
+ git filter-branch -d "$TRASHDIR/dfoo" \
+ --index-filter "cp \"$TRASHDIR\"/dfoo/backup-refs \"$TRASHDIR\"" \
+ ) &&
+ grep drepo "$TRASHDIR/backup-refs"
+'
+
+test_expect_success 'Fail if commit filter fails' '
+ test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD
+'
+
+test_expect_success 'rewrite, renaming a specific file' '
+ git filter-branch -f --tree-filter "mv d doh || :" HEAD
+'
+
+test_expect_success 'test that the file was renamed' '
+ test d = "$(git show HEAD:doh --)" &&
+ ! test -f d &&
+ test -f doh &&
+ test d = "$(cat doh)"
+'
+
+test_expect_success 'rewrite, renaming a specific directory' '
+ git filter-branch -f --tree-filter "mv dir diroh || :" HEAD
+'
+
+test_expect_success 'test that the directory was renamed' '
+ test dir/d = "$(git show HEAD:diroh/d --)" &&
+ ! test -d dir &&
+ test -d diroh &&
+ ! test -d diroh/dir &&
+ test -f diroh/d &&
+ test dir/d = "$(cat diroh/d)"
+'
+
+git tag oldD HEAD~4
+test_expect_success 'rewrite one branch, keeping a side branch' '
+ git branch modD oldD &&
+ git filter-branch -f --tree-filter "mv b boh || :" D..modD
+'
+
+test_expect_success 'common ancestor is still common (unchanged)' '
+ test "$(git merge-base modD D)" = "$(git rev-parse B)"
+'
+
+test_expect_success 'filter subdirectory only' '
+ mkdir subdir &&
+ touch subdir/new &&
+ git add subdir/new &&
+ test_tick &&
+ git commit -m "subdir" &&
+ echo H > a &&
+ test_tick &&
+ git commit -m "not subdir" a &&
+ echo A > subdir/new &&
+ test_tick &&
+ git commit -m "again subdir" subdir/new &&
+ git rm a &&
+ test_tick &&
+ git commit -m "again not subdir" &&
+ git branch sub &&
+ git branch sub-earlier HEAD~2 &&
+ git filter-branch -f --subdirectory-filter subdir \
+ refs/heads/sub refs/heads/sub-earlier
+'
+
+test_expect_success 'subdirectory filter result looks okay' '
+ test 2 = $(git rev-list sub | wc -l) &&
+ git show sub:new &&
+ test_must_fail git show sub:subdir &&
+ git show sub-earlier:new &&
+ test_must_fail git show sub-earlier:subdir
+'
+
+test_expect_success 'more setup' '
+ git checkout master &&
+ mkdir subdir &&
+ echo A > subdir/new &&
+ git add subdir/new &&
+ test_tick &&
+ git commit -m "subdir on master" subdir/new &&
+ git rm a &&
+ test_tick &&
+ git commit -m "again subdir on master" &&
+ git merge branch
+'
+
+test_expect_success 'use index-filter to move into a subdirectory' '
+ git branch directorymoved &&
+ git filter-branch -f --index-filter \
+ "git ls-files -s | sed \"s-\\t-&newsubdir/-\" |
+ GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
+ git update-index --index-info &&
+ mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved &&
+ test -z "$(git diff HEAD directorymoved:newsubdir)"'
+
+test_expect_success 'stops when msg filter fails' '
+ old=$(git rev-parse HEAD) &&
+ test_must_fail git filter-branch -f --msg-filter false HEAD &&
+ test $old = $(git rev-parse HEAD) &&
+ rm -rf .git-rewrite
+'
+
+test_expect_success 'author information is preserved' '
+ : > i &&
+ git add i &&
+ test_tick &&
+ GIT_AUTHOR_NAME="B V Uips" git commit -m bvuips &&
+ git branch preserved-author &&
+ git filter-branch -f --msg-filter "cat; \
+ test \$GIT_COMMIT != $(git rev-parse master) || \
+ echo Hallo" \
+ preserved-author &&
+ test 1 = $(git rev-list --author="B V Uips" preserved-author | wc -l)
+'
+
+test_expect_success "remove a certain author's commits" '
+ echo i > i &&
+ test_tick &&
+ git commit -m i i &&
+ git branch removed-author &&
+ git filter-branch -f --commit-filter "\
+ if [ \"\$GIT_AUTHOR_NAME\" = \"B V Uips\" ];\
+ then\
+ skip_commit \"\$@\";
+ else\
+ git commit-tree \"\$@\";\
+ fi" removed-author &&
+ cnt1=$(git rev-list master | wc -l) &&
+ cnt2=$(git rev-list removed-author | wc -l) &&
+ test $cnt1 -eq $(($cnt2 + 1)) &&
+ test 0 = $(git rev-list --author="B V Uips" removed-author | wc -l)
+'
+
+test_expect_success 'barf on invalid name' '
+ test_must_fail git filter-branch -f master xy-problem &&
+ test_must_fail git filter-branch -f HEAD^
+'
+
+test_expect_success '"map" works in commit filter' '
+ git filter-branch -f --commit-filter "\
+ parent=\$(git rev-parse \$GIT_COMMIT^) &&
+ mapped=\$(map \$parent) &&
+ actual=\$(echo \"\$@\" | sed \"s/^.*-p //\") &&
+ test \$mapped = \$actual &&
+ git commit-tree \"\$@\";" master~2..master &&
+ git rev-parse --verify master
+'
+
+test_expect_success 'Name needing quotes' '
+
+ git checkout -b rerere A &&
+ mkdir foo &&
+ name="れれれ" &&
+ >foo/$name &&
+ git add foo &&
+ git commit -m "Adding a file" &&
+ git filter-branch --tree-filter "rm -fr foo" &&
+ test_must_fail git ls-files --error-unmatch "foo/$name" &&
+ test $(git rev-parse --verify rerere) != $(git rev-parse --verify A)
+
+'
+
+test_expect_success 'Subdirectory filter with disappearing trees' '
+ git reset --hard &&
+ git checkout master &&
+
+ mkdir foo &&
+ touch foo/bar &&
+ git add foo &&
+ test_tick &&
+ git commit -m "Adding foo" &&
+
+ git rm -r foo &&
+ test_tick &&
+ git commit -m "Removing foo" &&
+
+ mkdir foo &&
+ touch foo/bar &&
+ git add foo &&
+ test_tick &&
+ git commit -m "Re-adding foo" &&
+
+ git filter-branch -f --subdirectory-filter foo &&
+ test $(git rev-list master | wc -l) = 3
+'
+
+test_expect_success 'Tag name filtering retains tag message' '
+ git tag -m atag T &&
+ git cat-file tag T > expect &&
+ git filter-branch -f --tag-name-filter cat &&
+ git cat-file tag T > actual &&
+ test_cmp expect actual
+'
+
+faux_gpg_tag='object XXXXXX
+type commit
+tag S
+tagger T A Gger <tagger@example.com> 1206026339 -0500
+
+This is a faux gpg signed tag.
+-----BEGIN PGP SIGNATURE-----
+Version: FauxGPG v0.0.0 (FAUX/Linux)
+
+gdsfoewhxu/6l06f1kxyxhKdZkrcbaiOMtkJUA9ITAc1mlamh0ooasxkH1XwMbYQ
+acmwXaWET20H0GeAGP+7vow=
+=agpO
+-----END PGP SIGNATURE-----
+'
+test_expect_success 'Tag name filtering strips gpg signature' '
+ sha1=$(git rev-parse HEAD) &&
+ sha1t=$(echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | git mktag) &&
+ git update-ref "refs/tags/S" "$sha1t" &&
+ echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | head -n 6 > expect &&
+ git filter-branch -f --tag-name-filter cat &&
+ git cat-file tag S > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Tag name filtering allows slashes in tag names' '
+ git tag -m tag-with-slash X/1 &&
+ git cat-file tag X/1 | sed -e s,X/1,X/2, > expect &&
+ git filter-branch -f --tag-name-filter "echo X/2" &&
+ git cat-file tag X/2 > actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Prune empty commits' '
+ git rev-list HEAD > expect &&
+ make_commit to_remove &&
+ git filter-branch -f --index-filter "git update-index --remove to_remove" --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 &&
+ test_commit add-foo foo 1 &&
+ git branch moved-foo &&
+ test_commit add-bar bar a &&
+ git branch invariant &&
+ orig_invariant=$(git rev-parse invariant) &&
+ git branch moved-bar &&
+ test_commit change-foo foo 2 &&
+ git filter-branch -f --remap-to-ancestor \
+ moved-foo moved-bar A..master \
+ -- -- foo &&
+ test $(git rev-parse moved-foo) = $(git rev-parse moved-bar) &&
+ test $(git rev-parse moved-foo) = $(git rev-parse master^) &&
+ test $orig_invariant = $(git rev-parse invariant)
+'
+
+test_expect_success 'setup submodule' '
+ rm -fr ?* .git &&
+ git init &&
+ test_commit file &&
+ mkdir submod &&
+ submodurl="$PWD/submod" &&
+ ( cd submod &&
+ git init &&
+ test_commit file-in-submod ) &&
+ git submodule add "$submodurl" &&
+ git commit -m "added submodule" &&
+ test_commit add-file &&
+ ( cd submod && test_commit add-in-submodule ) &&
+ git add submod &&
+ git commit -m "changed submodule" &&
+ git branch original HEAD
+'
+
+orig_head=`git show-ref --hash --head HEAD`
+
+test_expect_success 'rewrite submodule with another content' '
+ git filter-branch --tree-filter "test -d submod && {
+ rm -rf submod &&
+ git rm -rf --quiet submod &&
+ mkdir submod &&
+ : > submod/file
+ } || :" HEAD &&
+ test $orig_head != `git show-ref --hash --head HEAD`
+'
+
+test_expect_success 'replace submodule revision' '
+ git reset --hard original &&
+ git filter-branch -f --tree-filter \
+ "if git ls-files --error-unmatch -- submod > /dev/null 2>&1
+ then git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 submod
+ fi" HEAD &&
+ test $orig_head != `git show-ref --hash --head HEAD`
+'
+
+test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
new file mode 100755
index 0000000..ac943f5
--- /dev/null
+++ b/t/t7004-tag.sh
@@ -0,0 +1,1219 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carlos Rica
+#
+
+test_description='git tag
+
+Tests for operations with tags.'
+
+. ./test-lib.sh
+
+# creating and listing lightweight tags:
+
+tag_exists () {
+ git show-ref --quiet --verify refs/tags/"$1"
+}
+
+# todo: git tag -l now returns always zero, when fixed, change this test
+test_expect_success 'listing all tags in an empty tree should succeed' '
+ git tag -l &&
+ git tag
+'
+
+test_expect_success 'listing all tags in an empty tree should output nothing' '
+ test `git tag -l | wc -l` -eq 0 &&
+ test `git tag | wc -l` -eq 0
+'
+
+test_expect_success 'looking for a tag in an empty tree should fail' \
+ '! (tag_exists mytag)'
+
+test_expect_success 'creating a tag in an empty tree should fail' '
+ test_must_fail git tag mynotag &&
+ ! tag_exists mynotag
+'
+
+test_expect_success 'creating a tag for HEAD in an empty tree should fail' '
+ test_must_fail git tag mytaghead HEAD &&
+ ! tag_exists mytaghead
+'
+
+test_expect_success 'creating a tag for an unknown revision should fail' '
+ test_must_fail git tag mytagnorev aaaaaaaaaaa &&
+ ! tag_exists mytagnorev
+'
+
+# 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_tick &&
+ echo foo >foo &&
+ git add foo &&
+ git commit -m Foo &&
+ git tag mytag
+'
+
+test_expect_success 'listing all tags if one exists should succeed' '
+ git tag -l &&
+ git tag
+'
+
+test_expect_success 'listing all tags if one exists should output that tag' '
+ test `git tag -l` = mytag &&
+ test `git tag` = mytag
+'
+
+# pattern matching:
+
+test_expect_success 'listing a tag using a matching pattern should succeed' \
+ 'git tag -l mytag'
+
+test_expect_success \
+ 'listing a tag using a matching pattern should output that tag' \
+ 'test `git tag -l mytag` = mytag'
+
+# todo: git tag -l now returns always zero, when fixed, change this test
+test_expect_success \
+ 'listing tags using a non-matching pattern should suceed' \
+ 'git tag -l xxx'
+
+test_expect_success \
+ 'listing tags using a non-matching pattern should output nothing' \
+ 'test `git tag -l xxx | wc -l` -eq 0'
+
+# special cases for creating tags:
+
+test_expect_success \
+ 'trying to create a tag with the name of one existing should fail' \
+ 'test_must_fail git tag mytag'
+
+test_expect_success \
+ 'trying to create a tag with a non-valid name should fail' '
+ test `git tag -l | wc -l` -eq 1 &&
+ test_must_fail git tag "" &&
+ test_must_fail git tag .othertag &&
+ test_must_fail git tag "other tag" &&
+ test_must_fail git tag "othertag^" &&
+ test_must_fail git tag "other~tag" &&
+ test `git tag -l | wc -l` -eq 1
+'
+
+test_expect_success 'creating a tag using HEAD directly should succeed' '
+ git tag myhead HEAD &&
+ tag_exists myhead
+'
+
+# deleting tags:
+
+test_expect_success 'trying to delete an unknown tag should fail' '
+ ! tag_exists unknown-tag &&
+ test_must_fail git tag -d unknown-tag
+'
+
+cat >expect <<EOF
+myhead
+mytag
+EOF
+test_expect_success \
+ 'trying to delete tags without params should succeed and do nothing' '
+ git tag -l > actual && test_cmp expect actual &&
+ git tag -d &&
+ git tag -l > actual && test_cmp expect actual
+'
+
+test_expect_success \
+ 'deleting two existing tags in one command should succeed' '
+ tag_exists mytag &&
+ tag_exists myhead &&
+ git tag -d mytag myhead &&
+ ! tag_exists mytag &&
+ ! tag_exists myhead
+'
+
+test_expect_success \
+ 'creating a tag with the name of another deleted one should succeed' '
+ ! tag_exists mytag &&
+ git tag mytag &&
+ tag_exists mytag
+'
+
+test_expect_success \
+ 'trying to delete two tags, existing and not, should fail in the 2nd' '
+ tag_exists mytag &&
+ ! tag_exists myhead &&
+ test_must_fail git tag -d mytag anothertag &&
+ ! tag_exists mytag &&
+ ! tag_exists myhead
+'
+
+test_expect_success 'trying to delete an already deleted tag should fail' \
+ 'test_must_fail git tag -d mytag'
+
+# listing various tags with pattern matching:
+
+cat >expect <<EOF
+a1
+aa1
+cba
+t210
+t211
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+EOF
+test_expect_success 'listing all tags should print them ordered' '
+ git tag v1.0.1 &&
+ git tag t211 &&
+ git tag aa1 &&
+ git tag v0.2.1 &&
+ git tag v1.1.3 &&
+ git tag cba &&
+ git tag a1 &&
+ git tag v1.0 &&
+ git tag t210 &&
+ git tag -l > actual &&
+ test_cmp expect actual &&
+ git tag > actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+a1
+aa1
+cba
+EOF
+test_expect_success \
+ 'listing tags with substring as pattern must print those matching' '
+ rm *a* &&
+ git tag -l "*a*" > current &&
+ test_cmp expect current
+'
+
+cat >expect <<EOF
+v0.2.1
+v1.0.1
+EOF
+test_expect_success \
+ 'listing tags with a suffix as pattern must print those matching' '
+ git tag -l "*.1" > actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+t210
+t211
+EOF
+test_expect_success \
+ 'listing tags with a prefix as pattern must print those matching' '
+ git tag -l "t21*" > actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+a1
+EOF
+test_expect_success \
+ 'listing tags using a name as pattern must print that one matching' '
+ git tag -l a1 > actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+v1.0
+EOF
+test_expect_success \
+ 'listing tags using a name as pattern must print that one matching' '
+ git tag -l v1.0 > actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+v1.0.1
+v1.1.3
+EOF
+test_expect_success \
+ 'listing tags with ? in the pattern should print those matching' '
+ git tag -l "v1.?.?" > actual &&
+ test_cmp expect actual
+'
+
+>expect
+test_expect_success \
+ 'listing tags using v.* should print nothing because none have v.' '
+ git tag -l "v.*" > actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+EOF
+test_expect_success \
+ 'listing tags using v* should print only those having v' '
+ git tag -l "v*" > actual &&
+ test_cmp expect actual
+'
+
+# creating and verifying lightweight tags:
+
+test_expect_success \
+ 'a non-annotated tag created without parameters should point to HEAD' '
+ git tag non-annotated-tag &&
+ test $(git cat-file -t non-annotated-tag) = commit &&
+ test $(git rev-parse non-annotated-tag) = $(git rev-parse HEAD)
+'
+
+test_expect_success 'trying to verify an unknown tag should fail' \
+ 'test_must_fail git tag -v unknown-tag'
+
+test_expect_success \
+ 'trying to verify a non-annotated and non-signed tag should fail' \
+ 'test_must_fail git tag -v non-annotated-tag'
+
+test_expect_success \
+ 'trying to verify many non-annotated or unknown tags, should fail' \
+ 'test_must_fail git tag -v unknown-tag1 non-annotated-tag unknown-tag2'
+
+# creating annotated tags:
+
+get_tag_msg () {
+ git cat-file tag "$1" | sed -e "/BEGIN PGP/q"
+}
+
+# run test_tick before committing always gives the time in that timezone
+get_tag_header () {
+cat <<EOF
+object $2
+type $3
+tag $1
+tagger C O Mitter <committer@example.com> $4 -0700
+
+EOF
+}
+
+commit=$(git rev-parse HEAD)
+time=$test_tick
+
+get_tag_header annotated-tag $commit commit $time >expect
+echo "A message" >>expect
+test_expect_success \
+ 'creating an annotated tag with -m message should succeed' '
+ git tag -m "A message" annotated-tag &&
+ get_tag_msg annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+cat >msgfile <<EOF
+Another message
+in a file.
+EOF
+get_tag_header file-annotated-tag $commit commit $time >expect
+cat msgfile >>expect
+test_expect_success \
+ 'creating an annotated tag with -F messagefile should succeed' '
+ git tag -F msgfile file-annotated-tag &&
+ get_tag_msg file-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+cat >inputmsg <<EOF
+A message from the
+standard input
+EOF
+get_tag_header stdin-annotated-tag $commit commit $time >expect
+cat inputmsg >>expect
+test_expect_success 'creating an annotated tag with -F - should succeed' '
+ git tag -F - stdin-annotated-tag <inputmsg &&
+ get_tag_msg stdin-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'trying to create a tag with a non-existing -F file should fail' '
+ ! test -f nonexistingfile &&
+ ! tag_exists notag &&
+ test_must_fail git tag -F nonexistingfile notag &&
+ ! tag_exists notag
+'
+
+test_expect_success \
+ 'trying to create tags giving both -m or -F options should fail' '
+ echo "message file 1" >msgfile1 &&
+ echo "message file 2" >msgfile2 &&
+ ! tag_exists msgtag &&
+ test_must_fail git tag -m "message 1" -F msgfile1 msgtag &&
+ ! tag_exists msgtag &&
+ test_must_fail git tag -F msgfile1 -m "message 1" msgtag &&
+ ! tag_exists msgtag &&
+ test_must_fail git tag -m "message 1" -F msgfile1 \
+ -m "message 2" msgtag &&
+ ! tag_exists msgtag
+'
+
+# blank and empty messages:
+
+get_tag_header empty-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with an empty -m message should succeed' '
+ git tag -m "" empty-annotated-tag &&
+ get_tag_msg empty-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+>emptyfile
+get_tag_header emptyfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with an empty -F messagefile should succeed' '
+ git tag -F emptyfile emptyfile-annotated-tag &&
+ get_tag_msg emptyfile-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+printf '\n\n \n\t\nLeading blank lines\n' >blanksfile
+printf '\n\t \t \nRepeated blank lines\n' >>blanksfile
+printf '\n\n\nTrailing spaces \t \n' >>blanksfile
+printf '\nTrailing blank lines\n\n\t \n\n' >>blanksfile
+get_tag_header blanks-annotated-tag $commit commit $time >expect
+cat >>expect <<EOF
+Leading blank lines
+
+Repeated blank lines
+
+Trailing spaces
+
+Trailing blank lines
+EOF
+test_expect_success \
+ 'extra blanks in the message for an annotated tag should be removed' '
+ git tag -F blanksfile blanks-annotated-tag &&
+ get_tag_msg blanks-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+get_tag_header blank-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with blank -m message with spaces should succeed' '
+ git tag -m " " blank-annotated-tag &&
+ get_tag_msg blank-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+echo ' ' >blankfile
+echo '' >>blankfile
+echo ' ' >>blankfile
+get_tag_header blankfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with blank -F messagefile with spaces should succeed' '
+ git tag -F blankfile blankfile-annotated-tag &&
+ get_tag_msg blankfile-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+printf ' ' >blanknonlfile
+get_tag_header blanknonlfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with -F file of spaces and no newline should succeed' '
+ git tag -F blanknonlfile blanknonlfile-annotated-tag &&
+ get_tag_msg blanknonlfile-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+# messages with commented lines:
+
+cat >commentsfile <<EOF
+# A comment
+
+############
+The message.
+############
+One line.
+
+
+# commented lines
+# commented lines
+
+Another line.
+# comments
+
+Last line.
+EOF
+get_tag_header comments-annotated-tag $commit commit $time >expect
+cat >>expect <<EOF
+The message.
+One line.
+
+Another line.
+
+Last line.
+EOF
+test_expect_success \
+ 'creating a tag using a -F messagefile with #comments should succeed' '
+ git tag -F commentsfile comments-annotated-tag &&
+ get_tag_msg comments-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+get_tag_header comment-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with a #comment in the -m message should succeed' '
+ git tag -m "#comment" comment-annotated-tag &&
+ get_tag_msg comment-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+echo '#comment' >commentfile
+echo '' >>commentfile
+echo '####' >>commentfile
+get_tag_header commentfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with #comments in the -F messagefile should succeed' '
+ git tag -F commentfile commentfile-annotated-tag &&
+ get_tag_msg commentfile-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+printf '#comment' >commentnonlfile
+get_tag_header commentnonlfile-annotated-tag $commit commit $time >expect
+test_expect_success \
+ 'creating a tag with a file of #comment and no newline should succeed' '
+ git tag -F commentnonlfile commentnonlfile-annotated-tag &&
+ get_tag_msg commentnonlfile-annotated-tag >actual &&
+ test_cmp expect actual
+'
+
+# listing messages for annotated non-signed tags:
+
+test_expect_success \
+ 'listing the one-line message of a non-signed tag should succeed' '
+ git tag -m "A msg" tag-one-line &&
+
+ echo "tag-one-line" >expect &&
+ git tag -l | grep "^tag-one-line" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l | grep "^tag-one-line" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l tag-one-line >actual &&
+ test_cmp expect actual &&
+
+ echo "tag-one-line A msg" >expect &&
+ git tag -n1 -l | grep "^tag-one-line" >actual &&
+ test_cmp expect actual &&
+ git tag -n -l | grep "^tag-one-line" >actual &&
+ test_cmp expect actual &&
+ git tag -n1 -l tag-one-line >actual &&
+ test_cmp expect actual &&
+ git tag -n2 -l tag-one-line >actual &&
+ test_cmp expect actual &&
+ git tag -n999 -l tag-one-line >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success \
+ 'listing the zero-lines message of a non-signed tag should succeed' '
+ git tag -m "" tag-zero-lines &&
+
+ echo "tag-zero-lines" >expect &&
+ git tag -l | grep "^tag-zero-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l | grep "^tag-zero-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l tag-zero-lines >actual &&
+ test_cmp expect actual &&
+
+ echo "tag-zero-lines " >expect &&
+ git tag -n1 -l | grep "^tag-zero-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n -l | grep "^tag-zero-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n1 -l tag-zero-lines >actual &&
+ test_cmp expect actual &&
+ git tag -n2 -l tag-zero-lines >actual &&
+ test_cmp expect actual &&
+ git tag -n999 -l tag-zero-lines >actual &&
+ test_cmp expect actual
+'
+
+echo 'tag line one' >annotagmsg
+echo 'tag line two' >>annotagmsg
+echo 'tag line three' >>annotagmsg
+test_expect_success \
+ 'listing many message lines of a non-signed tag should succeed' '
+ git tag -F annotagmsg tag-lines &&
+
+ echo "tag-lines" >expect &&
+ git tag -l | grep "^tag-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l | grep "^tag-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l tag-lines >actual &&
+ test_cmp expect actual &&
+
+ echo "tag-lines tag line one" >expect &&
+ git tag -n1 -l | grep "^tag-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n -l | grep "^tag-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n1 -l tag-lines >actual &&
+ test_cmp expect actual &&
+
+ echo " tag line two" >>expect &&
+ git tag -n2 -l | grep "^ *tag.line" >actual &&
+ test_cmp expect actual &&
+ git tag -n2 -l tag-lines >actual &&
+ test_cmp expect actual &&
+
+ echo " tag line three" >>expect &&
+ git tag -n3 -l | grep "^ *tag.line" >actual &&
+ test_cmp expect actual &&
+ git tag -n3 -l tag-lines >actual &&
+ test_cmp expect actual &&
+ git tag -n4 -l | grep "^ *tag.line" >actual &&
+ test_cmp expect actual &&
+ git tag -n4 -l tag-lines >actual &&
+ test_cmp expect actual &&
+ git tag -n99 -l | grep "^ *tag.line" >actual &&
+ test_cmp expect actual &&
+ git tag -n99 -l tag-lines >actual &&
+ test_cmp expect actual
+'
+
+# subsequent tests require gpg; check if it is available
+gpg --version >/dev/null 2>/dev/null
+if [ $? -eq 127 ]; then
+ say "# gpg not found - skipping tag signing and verification tests"
+else
+ # As said here: http://www.gnupg.org/documentation/faqs.html#q6.19
+ # the gpg version 1.0.6 didn't parse trust packets correctly, so for
+ # that version, creation of signed tags using the generated key fails.
+ case "$(gpg --version)" in
+ 'gpg (GnuPG) 1.0.6'*)
+ say "Skipping signed tag tests, because a bug in 1.0.6 version"
+ ;;
+ *)
+ test_set_prereq GPG
+ ;;
+ esac
+fi
+
+# trying to verify annotated non-signed tags:
+
+test_expect_success GPG \
+ 'trying to verify an annotated non-signed tag should fail' '
+ tag_exists annotated-tag &&
+ test_must_fail git tag -v annotated-tag
+'
+
+test_expect_success GPG \
+ 'trying to verify a file-annotated non-signed tag should fail' '
+ tag_exists file-annotated-tag &&
+ test_must_fail git tag -v file-annotated-tag
+'
+
+test_expect_success GPG \
+ 'trying to verify two annotated non-signed tags should fail' '
+ tag_exists annotated-tag file-annotated-tag &&
+ test_must_fail git tag -v annotated-tag file-annotated-tag
+'
+
+# creating and verifying signed tags:
+
+# key generation info: gpg --homedir t/t7004 --gen-key
+# Type DSA and Elgamal, size 2048 bits, no expiration date.
+# Name and email: C O Mitter <committer@example.com>
+# No password given, to enable non-interactive operation.
+
+cp -R "$TEST_DIRECTORY"/t7004 ./gpghome
+chmod 0700 gpghome
+GNUPGHOME="$(pwd)/gpghome"
+export GNUPGHOME
+
+get_tag_header signed-tag $commit commit $time >expect
+echo 'A signed tag message' >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG 'creating a signed tag with -m message should succeed' '
+ git tag -s -m "A signed tag message" signed-tag &&
+ get_tag_msg signed-tag >actual &&
+ test_cmp expect actual
+'
+
+get_tag_header u-signed-tag $commit commit $time >expect
+echo 'Another message' >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG 'sign with a given key id' '
+
+ git tag -u committer@example.com -m "Another message" u-signed-tag &&
+ get_tag_msg u-signed-tag >actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success GPG 'sign with an unknown id (1)' '
+
+ test_must_fail git tag -u author@example.com \
+ -m "Another message" o-signed-tag
+
+'
+
+test_expect_success GPG 'sign with an unknown id (2)' '
+
+ test_must_fail git tag -u DEADBEEF -m "Another message" o-signed-tag
+
+'
+
+cat >fakeeditor <<'EOF'
+#!/bin/sh
+test -n "$1" && exec >"$1"
+echo A signed tag message
+echo from a fake editor.
+EOF
+chmod +x fakeeditor
+
+get_tag_header implied-sign $commit commit $time >expect
+./fakeeditor >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG '-u implies signed tag' '
+ GIT_EDITOR=./fakeeditor git tag -u CDDE430D implied-sign &&
+ get_tag_msg implied-sign >actual &&
+ test_cmp expect actual
+'
+
+cat >sigmsgfile <<EOF
+Another signed tag
+message in a file.
+EOF
+get_tag_header file-signed-tag $commit commit $time >expect
+cat sigmsgfile >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with -F messagefile should succeed' '
+ git tag -s -F sigmsgfile file-signed-tag &&
+ get_tag_msg file-signed-tag >actual &&
+ test_cmp expect actual
+'
+
+cat >siginputmsg <<EOF
+A signed tag message from
+the standard input
+EOF
+get_tag_header stdin-signed-tag $commit commit $time >expect
+cat siginputmsg >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG 'creating a signed tag with -F - should succeed' '
+ git tag -s -F - stdin-signed-tag <siginputmsg &&
+ get_tag_msg stdin-signed-tag >actual &&
+ test_cmp expect actual
+'
+
+get_tag_header implied-annotate $commit commit $time >expect
+./fakeeditor >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG '-s implies annotated tag' '
+ GIT_EDITOR=./fakeeditor git tag -s implied-annotate &&
+ get_tag_msg implied-annotate >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG \
+ 'trying to create a signed tag with non-existing -F file should fail' '
+ ! test -f nonexistingfile &&
+ ! tag_exists nosigtag &&
+ test_must_fail git tag -s -F nonexistingfile nosigtag &&
+ ! tag_exists nosigtag
+'
+
+test_expect_success GPG 'verifying a signed tag should succeed' \
+ 'git tag -v signed-tag'
+
+test_expect_success GPG 'verifying two signed tags in one command should succeed' \
+ 'git tag -v signed-tag file-signed-tag'
+
+test_expect_success GPG \
+ 'verifying many signed and non-signed tags should fail' '
+ test_must_fail git tag -v signed-tag annotated-tag &&
+ test_must_fail git tag -v file-annotated-tag file-signed-tag &&
+ test_must_fail git tag -v annotated-tag \
+ file-signed-tag file-annotated-tag &&
+ test_must_fail git tag -v signed-tag annotated-tag file-signed-tag
+'
+
+test_expect_success GPG 'verifying a forged tag should fail' '
+ forged=$(git cat-file tag signed-tag |
+ sed -e "s/signed-tag/forged-tag/" |
+ git mktag) &&
+ git tag forged-tag $forged &&
+ test_must_fail git tag -v forged-tag
+'
+
+# blank and empty messages for signed tags:
+
+get_tag_header empty-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with an empty -m message should succeed' '
+ git tag -s -m "" empty-signed-tag &&
+ get_tag_msg empty-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v empty-signed-tag
+'
+
+>sigemptyfile
+get_tag_header emptyfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with an empty -F messagefile should succeed' '
+ git tag -s -F sigemptyfile emptyfile-signed-tag &&
+ get_tag_msg emptyfile-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v emptyfile-signed-tag
+'
+
+printf '\n\n \n\t\nLeading blank lines\n' > sigblanksfile
+printf '\n\t \t \nRepeated blank lines\n' >>sigblanksfile
+printf '\n\n\nTrailing spaces \t \n' >>sigblanksfile
+printf '\nTrailing blank lines\n\n\t \n\n' >>sigblanksfile
+get_tag_header blanks-signed-tag $commit commit $time >expect
+cat >>expect <<EOF
+Leading blank lines
+
+Repeated blank lines
+
+Trailing spaces
+
+Trailing blank lines
+EOF
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'extra blanks in the message for a signed tag should be removed' '
+ git tag -s -F sigblanksfile blanks-signed-tag &&
+ get_tag_msg blanks-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v blanks-signed-tag
+'
+
+get_tag_header blank-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with a blank -m message should succeed' '
+ git tag -s -m " " blank-signed-tag &&
+ get_tag_msg blank-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v blank-signed-tag
+'
+
+echo ' ' >sigblankfile
+echo '' >>sigblankfile
+echo ' ' >>sigblankfile
+get_tag_header blankfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with blank -F file with spaces should succeed' '
+ git tag -s -F sigblankfile blankfile-signed-tag &&
+ get_tag_msg blankfile-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v blankfile-signed-tag
+'
+
+printf ' ' >sigblanknonlfile
+get_tag_header blanknonlfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with spaces and no newline should succeed' '
+ git tag -s -F sigblanknonlfile blanknonlfile-signed-tag &&
+ get_tag_msg blanknonlfile-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v signed-tag
+'
+
+# messages with commented lines for signed tags:
+
+cat >sigcommentsfile <<EOF
+# A comment
+
+############
+The message.
+############
+One line.
+
+
+# commented lines
+# commented lines
+
+Another line.
+# comments
+
+Last line.
+EOF
+get_tag_header comments-signed-tag $commit commit $time >expect
+cat >>expect <<EOF
+The message.
+One line.
+
+Another line.
+
+Last line.
+EOF
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with a -F file with #comments should succeed' '
+ git tag -s -F sigcommentsfile comments-signed-tag &&
+ get_tag_msg comments-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v comments-signed-tag
+'
+
+get_tag_header comment-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with #commented -m message should succeed' '
+ git tag -s -m "#comment" comment-signed-tag &&
+ get_tag_msg comment-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v comment-signed-tag
+'
+
+echo '#comment' >sigcommentfile
+echo '' >>sigcommentfile
+echo '####' >>sigcommentfile
+get_tag_header commentfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with #commented -F messagefile should succeed' '
+ git tag -s -F sigcommentfile commentfile-signed-tag &&
+ get_tag_msg commentfile-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v commentfile-signed-tag
+'
+
+printf '#comment' >sigcommentnonlfile
+get_tag_header commentnonlfile-signed-tag $commit commit $time >expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag with a #comment and no newline should succeed' '
+ git tag -s -F sigcommentnonlfile commentnonlfile-signed-tag &&
+ get_tag_msg commentnonlfile-signed-tag >actual &&
+ test_cmp expect actual &&
+ git tag -v commentnonlfile-signed-tag
+'
+
+# listing messages for signed tags:
+
+test_expect_success GPG \
+ 'listing the one-line message of a signed tag should succeed' '
+ git tag -s -m "A message line signed" stag-one-line &&
+
+ echo "stag-one-line" >expect &&
+ git tag -l | grep "^stag-one-line" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l | grep "^stag-one-line" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l stag-one-line >actual &&
+ test_cmp expect actual &&
+
+ echo "stag-one-line A message line signed" >expect &&
+ git tag -n1 -l | grep "^stag-one-line" >actual &&
+ test_cmp expect actual &&
+ git tag -n -l | grep "^stag-one-line" >actual &&
+ test_cmp expect actual &&
+ git tag -n1 -l stag-one-line >actual &&
+ test_cmp expect actual &&
+ git tag -n2 -l stag-one-line >actual &&
+ test_cmp expect actual &&
+ git tag -n999 -l stag-one-line >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success GPG \
+ 'listing the zero-lines message of a signed tag should succeed' '
+ git tag -s -m "" stag-zero-lines &&
+
+ echo "stag-zero-lines" >expect &&
+ git tag -l | grep "^stag-zero-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l | grep "^stag-zero-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l stag-zero-lines >actual &&
+ test_cmp expect actual &&
+
+ echo "stag-zero-lines " >expect &&
+ git tag -n1 -l | grep "^stag-zero-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n -l | grep "^stag-zero-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n1 -l stag-zero-lines >actual &&
+ test_cmp expect actual &&
+ git tag -n2 -l stag-zero-lines >actual &&
+ test_cmp expect actual &&
+ git tag -n999 -l stag-zero-lines >actual &&
+ test_cmp expect actual
+'
+
+echo 'stag line one' >sigtagmsg
+echo 'stag line two' >>sigtagmsg
+echo 'stag line three' >>sigtagmsg
+test_expect_success GPG \
+ 'listing many message lines of a signed tag should succeed' '
+ git tag -s -F sigtagmsg stag-lines &&
+
+ echo "stag-lines" >expect &&
+ git tag -l | grep "^stag-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l | grep "^stag-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n0 -l stag-lines >actual &&
+ test_cmp expect actual &&
+
+ echo "stag-lines stag line one" >expect &&
+ git tag -n1 -l | grep "^stag-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n -l | grep "^stag-lines" >actual &&
+ test_cmp expect actual &&
+ git tag -n1 -l stag-lines >actual &&
+ test_cmp expect actual &&
+
+ echo " stag line two" >>expect &&
+ git tag -n2 -l | grep "^ *stag.line" >actual &&
+ test_cmp expect actual &&
+ git tag -n2 -l stag-lines >actual &&
+ test_cmp expect actual &&
+
+ echo " stag line three" >>expect &&
+ git tag -n3 -l | grep "^ *stag.line" >actual &&
+ test_cmp expect actual &&
+ git tag -n3 -l stag-lines >actual &&
+ test_cmp expect actual &&
+ git tag -n4 -l | grep "^ *stag.line" >actual &&
+ test_cmp expect actual &&
+ git tag -n4 -l stag-lines >actual &&
+ test_cmp expect actual &&
+ git tag -n99 -l | grep "^ *stag.line" >actual &&
+ test_cmp expect actual &&
+ git tag -n99 -l stag-lines >actual &&
+ test_cmp expect actual
+'
+
+# tags pointing to objects different from commits:
+
+tree=$(git rev-parse HEAD^{tree})
+blob=$(git rev-parse HEAD:foo)
+tag=$(git rev-parse signed-tag 2>/dev/null)
+
+get_tag_header tree-signed-tag $tree tree $time >expect
+echo "A message for a tree" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag pointing to a tree should succeed' '
+ git tag -s -m "A message for a tree" tree-signed-tag HEAD^{tree} &&
+ get_tag_msg tree-signed-tag >actual &&
+ test_cmp expect actual
+'
+
+get_tag_header blob-signed-tag $blob blob $time >expect
+echo "A message for a blob" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag pointing to a blob should succeed' '
+ git tag -s -m "A message for a blob" blob-signed-tag HEAD:foo &&
+ get_tag_msg blob-signed-tag >actual &&
+ test_cmp expect actual
+'
+
+get_tag_header tag-signed-tag $tag tag $time >expect
+echo "A message for another tag" >>expect
+echo '-----BEGIN PGP SIGNATURE-----' >>expect
+test_expect_success GPG \
+ 'creating a signed tag pointing to another tag should succeed' '
+ git tag -s -m "A message for another tag" tag-signed-tag signed-tag &&
+ get_tag_msg tag-signed-tag >actual &&
+ test_cmp expect actual
+'
+
+# try to sign with bad user.signingkey
+git config user.signingkey BobTheMouse
+test_expect_success GPG \
+ 'git tag -s fails if gpg is misconfigured' \
+ 'test_must_fail git tag -s -m tail tag-gpg-failure'
+git config --unset user.signingkey
+
+# try to verify without gpg:
+
+rm -rf gpghome
+test_expect_success GPG \
+ 'verify signed tag fails when public key is not present' \
+ 'test_must_fail git tag -v signed-tag'
+
+test_expect_success \
+ 'git tag -a fails if tag annotation is empty' '
+ ! (GIT_EDITOR=cat git tag -a initial-comment)
+'
+
+test_expect_success \
+ 'message in editor has initial comment' '
+ GIT_EDITOR=cat git tag -a initial-comment > actual
+ # check the first line --- should be empty
+ first=$(sed -e 1q <actual) &&
+ test -z "$first" &&
+ # remove commented lines from the remainder -- should be empty
+ rest=$(sed -e 1d -e '/^#/d' <actual) &&
+ test -z "$rest"
+'
+
+get_tag_header reuse $commit commit $time >expect
+echo "An annotation to be reused" >> expect
+test_expect_success \
+ 'overwriting an annoted tag should use its previous body' '
+ git tag -a -m "An annotation to be reused" reuse &&
+ GIT_EDITOR=true git tag -f -a reuse &&
+ get_tag_msg reuse >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'filename for the message is relative to cwd' '
+ mkdir subdir &&
+ echo "Tag message in top directory" >msgfile-5 &&
+ echo "Tag message in sub directory" >subdir/msgfile-5 &&
+ (
+ cd subdir &&
+ git tag -a -F msgfile-5 tag-from-subdir
+ ) &&
+ git cat-file tag tag-from-subdir | grep "in sub directory"
+'
+
+test_expect_success 'filename for the message is relative to cwd' '
+ echo "Tag message in sub directory" >subdir/msgfile-6 &&
+ (
+ cd subdir &&
+ git tag -a -F msgfile-6 tag-from-subdir-2
+ ) &&
+ git cat-file tag tag-from-subdir-2 | grep "in sub directory"
+'
+
+# create a few more commits to test --contains
+
+hash1=$(git rev-parse HEAD)
+
+test_expect_success 'creating second commit and tag' '
+ echo foo-2.0 >foo &&
+ git add foo &&
+ git commit -m second
+ git tag v2.0
+'
+
+hash2=$(git rev-parse HEAD)
+
+test_expect_success 'creating third commit without tag' '
+ echo foo-dev >foo &&
+ git add foo &&
+ git commit -m third
+'
+
+hash3=$(git rev-parse HEAD)
+
+# simple linear checks of --continue
+
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+v2.0
+EOF
+
+test_expect_success 'checking that first commit is in all tags (hash)' "
+ git tag -l --contains $hash1 v* >actual
+ test_cmp expected actual
+"
+
+# other ways of specifying the commit
+test_expect_success 'checking that first commit is in all tags (tag)' "
+ git tag -l --contains v1.0 v* >actual
+ test_cmp expected actual
+"
+
+test_expect_success 'checking that first commit is in all tags (relative)' "
+ git tag -l --contains HEAD~2 v* >actual
+ test_cmp expected actual
+"
+
+cat > expected <<EOF
+v2.0
+EOF
+
+test_expect_success 'checking that second commit only has one tag' "
+ git tag -l --contains $hash2 v* >actual
+ test_cmp expected actual
+"
+
+
+cat > expected <<EOF
+EOF
+
+test_expect_success 'checking that third commit has no tags' "
+ git tag -l --contains $hash3 v* >actual
+ test_cmp expected actual
+"
+
+# how about a simple merge?
+
+test_expect_success 'creating simple branch' '
+ git branch stable v2.0 &&
+ git checkout stable &&
+ echo foo-3.0 > foo &&
+ git commit foo -m fourth
+ git tag v3.0
+'
+
+hash4=$(git rev-parse HEAD)
+
+cat > expected <<EOF
+v3.0
+EOF
+
+test_expect_success 'checking that branch head only has one tag' "
+ git tag -l --contains $hash4 v* >actual
+ test_cmp expected actual
+"
+
+test_expect_success 'merging original branch into this branch' '
+ git merge --strategy=ours master &&
+ git tag v4.0
+'
+
+cat > expected <<EOF
+v4.0
+EOF
+
+test_expect_success 'checking that original branch head has one tag now' "
+ git tag -l --contains $hash3 v* >actual
+ test_cmp expected actual
+"
+
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+v2.0
+v3.0
+v4.0
+EOF
+
+test_expect_success 'checking that initial commit is in all tags' "
+ git tag -l --contains $hash1 v* >actual
+ test_cmp expected actual
+"
+
+# mixing modes and options:
+
+test_expect_success 'mixing incompatibles modes and options is forbidden' '
+ test_must_fail git tag -a
+ test_must_fail git tag -l -v
+ test_must_fail git tag -n 100
+ test_must_fail git tag -l -m msg
+ test_must_fail git tag -l -F some file
+ test_must_fail git tag -v -s
+'
+
+test_done
diff --git a/t/t7004/pubring.gpg b/t/t7004/pubring.gpg
new file mode 100644
index 0000000..83855fa
--- /dev/null
+++ b/t/t7004/pubring.gpg
Binary files differ
diff --git a/t/t7004/random_seed b/t/t7004/random_seed
new file mode 100644
index 0000000..8fed133
--- /dev/null
+++ b/t/t7004/random_seed
Binary files differ
diff --git a/t/t7004/secring.gpg b/t/t7004/secring.gpg
new file mode 100644
index 0000000..d831cd9
--- /dev/null
+++ b/t/t7004/secring.gpg
Binary files differ
diff --git a/t/t7004/trustdb.gpg b/t/t7004/trustdb.gpg
new file mode 100644
index 0000000..abace96
--- /dev/null
+++ b/t/t7004/trustdb.gpg
Binary files differ
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
new file mode 100755
index 0000000..26ddf9d
--- /dev/null
+++ b/t/t7005-editor.sh
@@ -0,0 +1,137 @@
+#!/bin/sh
+
+test_description='GIT_EDITOR, core.editor, and stuff'
+
+. ./test-lib.sh
+
+unset EDITOR VISUAL GIT_EDITOR
+
+test_expect_success 'determine default editor' '
+
+ vi=$(TERM=vt100 git var GIT_EDITOR) &&
+ test -n "$vi"
+
+'
+
+if ! expr "$vi" : '[a-z]*$' >/dev/null
+then
+ vi=
+fi
+
+for i in GIT_EDITOR core_editor EDITOR VISUAL $vi
+do
+ cat >e-$i.sh <<-EOF
+ #!$SHELL_PATH
+ echo "Edited by $i" >"\$1"
+ EOF
+ chmod +x e-$i.sh
+done
+
+if ! test -z "$vi"
+then
+ mv e-$vi.sh $vi
+fi
+
+test_expect_success setup '
+
+ msg="Hand-edited" &&
+ test_commit "$msg" &&
+ echo "$msg" >expect &&
+ git show -s --format=%s > actual &&
+ test_cmp actual expect
+
+'
+
+TERM=dumb
+export TERM
+test_expect_success 'dumb should error out when falling back on vi' '
+
+ if git commit --amend
+ then
+ echo "Oops?"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success 'dumb should prefer EDITOR to VISUAL' '
+
+ EDITOR=./e-EDITOR.sh &&
+ VISUAL=./e-VISUAL.sh &&
+ export EDITOR VISUAL &&
+ git commit --amend &&
+ test "$(git show -s --format=%s)" = "Edited by EDITOR"
+
+'
+
+TERM=vt100
+export TERM
+for i in $vi EDITOR VISUAL core_editor GIT_EDITOR
+do
+ echo "Edited by $i" >expect
+ unset EDITOR VISUAL GIT_EDITOR
+ git config --unset-all core.editor
+ case "$i" in
+ core_editor)
+ git config core.editor ./e-core_editor.sh
+ ;;
+ [A-Z]*)
+ eval "$i=./e-$i.sh"
+ export $i
+ ;;
+ esac
+ test_expect_success "Using $i" '
+ git --exec-path=. commit --amend &&
+ git show -s --pretty=oneline |
+ sed -e "s/^[0-9a-f]* //" >actual &&
+ test_cmp actual expect
+ '
+done
+
+unset EDITOR VISUAL GIT_EDITOR
+git config --unset-all core.editor
+for i in $vi EDITOR VISUAL core_editor GIT_EDITOR
+do
+ echo "Edited by $i" >expect
+ case "$i" in
+ core_editor)
+ git config core.editor ./e-core_editor.sh
+ ;;
+ [A-Z]*)
+ eval "$i=./e-$i.sh"
+ export $i
+ ;;
+ esac
+ test_expect_success "Using $i (override)" '
+ git --exec-path=. commit --amend &&
+ git show -s --pretty=oneline |
+ sed -e "s/^[0-9a-f]* //" >actual &&
+ test_cmp actual expect
+ '
+done
+
+if ! echo 'echo space > "$1"' > "e space.sh"
+then
+ skip_all="Skipping; FS does not support spaces in filenames"
+ test_done
+fi
+
+test_expect_success 'editor with a space' '
+
+ chmod a+x "e space.sh" &&
+ GIT_EDITOR="./e\ space.sh" git commit --amend &&
+ test space = "$(git show -s --pretty=format:%s)"
+
+'
+
+unset GIT_EDITOR
+test_expect_success 'core.editor with a space' '
+
+ git config core.editor \"./e\ space.sh\" &&
+ git commit --amend &&
+ test space = "$(git show -s --pretty=format:%s)"
+
+'
+
+test_done
diff --git a/t/t7006-pager.sh b/t/t7006-pager.sh
new file mode 100755
index 0000000..71d3cef
--- /dev/null
+++ b/t/t7006-pager.sh
@@ -0,0 +1,372 @@
+#!/bin/sh
+
+test_description='Test automatic use of a pager.'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pager.sh
+
+cleanup_fail() {
+ echo >&2 cleanup failed
+ (exit 1)
+}
+
+test_expect_success 'set up terminal for tests' '
+ rm -f stdout_is_tty ||
+ cleanup_fail &&
+
+ if test -t 1
+ then
+ >stdout_is_tty
+ elif
+ test_have_prereq PERL &&
+ "$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl \
+ sh -c "test -t 1"
+ then
+ >test_terminal_works
+ fi
+'
+
+if test -e stdout_is_tty
+then
+ test_terminal() { "$@"; }
+ test_set_prereq TTY
+elif test -e test_terminal_works
+then
+ test_terminal() {
+ "$PERL_PATH" "$TEST_DIRECTORY"/t7006/test-terminal.perl "$@"
+ }
+ test_set_prereq TTY
+else
+ say "# no usable terminal, so skipping some tests"
+fi
+
+test_expect_success 'setup' '
+ unset GIT_PAGER GIT_PAGER_IN_USE;
+ test_might_fail git config --unset core.pager &&
+
+ PAGER="cat >paginated.out" &&
+ export PAGER &&
+
+ test_commit initial
+'
+
+test_expect_success TTY 'some commands use a pager' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
+ test_terminal git log &&
+ test -e paginated.out
+'
+
+test_expect_success TTY 'some commands do not use a pager' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
+ test_terminal git rev-list HEAD &&
+ ! test -e paginated.out
+'
+
+test_expect_success 'no pager when stdout is a pipe' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
+ git log | cat &&
+ ! test -e paginated.out
+'
+
+test_expect_success 'no pager when stdout is a regular file' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
+ git log >file &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'git --paginate rev-list uses a pager' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
+ test_terminal git --paginate rev-list HEAD &&
+ test -e paginated.out
+'
+
+test_expect_success 'no pager even with --paginate when stdout is a pipe' '
+ rm -f file paginated.out ||
+ cleanup_fail &&
+
+ git --paginate log | cat &&
+ ! test -e paginated.out
+'
+
+test_expect_success TTY 'no pager with --no-pager' '
+ rm -f paginated.out ||
+ cleanup_fail &&
+
+ test_terminal git --no-pager log &&
+ ! test -e paginated.out
+'
+
+# A colored commit log will begin with an appropriate ANSI escape
+# for the first color; the text "commit" comes later.
+colorful() {
+ read firstline <$1
+ ! expr "$firstline" : "[a-zA-Z]" >/dev/null
+}
+
+test_expect_success 'tests can detect color' '
+ rm -f colorful.log colorless.log ||
+ cleanup_fail &&
+
+ git log --no-color >colorless.log &&
+ git log --color >colorful.log &&
+ ! colorful colorless.log &&
+ colorful colorful.log
+'
+
+test_expect_success 'no color when stdout is a regular file' '
+ rm -f colorless.log &&
+ git config color.ui auto ||
+ cleanup_fail &&
+
+ git log >colorless.log &&
+ ! colorful colorless.log
+'
+
+test_expect_success TTY 'color when writing to a pager' '
+ rm -f paginated.out &&
+ git config color.ui auto ||
+ cleanup_fail &&
+
+ (
+ TERM=vt100 &&
+ export TERM &&
+ test_terminal git log
+ ) &&
+ colorful paginated.out
+'
+
+test_expect_success 'color when writing to a file intended for a pager' '
+ rm -f colorful.log &&
+ git config color.ui auto ||
+ cleanup_fail &&
+
+ (
+ TERM=vt100 &&
+ GIT_PAGER_IN_USE=true &&
+ export TERM GIT_PAGER_IN_USE &&
+ git log >colorful.log
+ ) &&
+ colorful colorful.log
+'
+
+if test_have_prereq SIMPLEPAGER && test_have_prereq TTY
+then
+ test_set_prereq SIMPLEPAGERTTY
+fi
+
+# Use this helper to make it easy for the caller of your
+# terminal-using function to specify whether it should fail.
+# If you write
+#
+# your_test() {
+# parse_args "$@"
+#
+# $test_expectation "$cmd - behaves well" "
+# ...
+# $full_command &&
+# ...
+# "
+# }
+#
+# then your test can be used like this:
+#
+# your_test expect_(success|failure) [test_must_fail] 'git foo'
+#
+parse_args() {
+ test_expectation="test_$1"
+ shift
+ if test "$1" = test_must_fail
+ then
+ full_command="test_must_fail test_terminal "
+ shift
+ else
+ full_command="test_terminal "
+ fi
+ cmd=$1
+ full_command="$full_command $1"
+}
+
+test_default_pager() {
+ parse_args "$@"
+
+ $test_expectation SIMPLEPAGERTTY "$cmd - default pager is used by default" "
+ unset PAGER GIT_PAGER;
+ test_might_fail git config --unset core.pager &&
+ rm -f default_pager_used ||
+ cleanup_fail &&
+
+ cat >\$less <<-\EOF &&
+ #!/bin/sh
+ wc >default_pager_used
+ EOF
+ chmod +x \$less &&
+ (
+ PATH=.:\$PATH &&
+ export PATH &&
+ $full_command
+ ) &&
+ test -e default_pager_used
+ "
+}
+
+test_PAGER_overrides() {
+ parse_args "$@"
+
+ $test_expectation TTY "$cmd - PAGER overrides default pager" "
+ unset GIT_PAGER;
+ test_might_fail git config --unset core.pager &&
+ rm -f PAGER_used ||
+ cleanup_fail &&
+
+ PAGER='wc >PAGER_used' &&
+ export PAGER &&
+ $full_command &&
+ test -e PAGER_used
+ "
+}
+
+test_core_pager_overrides() {
+ if_local_config=
+ used_if_wanted='overrides PAGER'
+ test_core_pager "$@"
+}
+
+test_local_config_ignored() {
+ if_local_config='! '
+ used_if_wanted='is not used'
+ test_core_pager "$@"
+}
+
+test_core_pager() {
+ parse_args "$@"
+
+ $test_expectation TTY "$cmd - repository-local core.pager setting $used_if_wanted" "
+ unset GIT_PAGER;
+ rm -f core.pager_used ||
+ cleanup_fail &&
+
+ PAGER=wc &&
+ export PAGER &&
+ git config core.pager 'wc >core.pager_used' &&
+ $full_command &&
+ ${if_local_config}test -e core.pager_used
+ "
+}
+
+test_core_pager_subdir() {
+ if_local_config=
+ used_if_wanted='overrides PAGER'
+ test_pager_subdir_helper "$@"
+}
+
+test_no_local_config_subdir() {
+ if_local_config='! '
+ used_if_wanted='is not used'
+ test_pager_subdir_helper "$@"
+}
+
+test_pager_subdir_helper() {
+ parse_args "$@"
+
+ $test_expectation TTY "$cmd - core.pager $used_if_wanted from subdirectory" "
+ unset GIT_PAGER;
+ rm -f core.pager_used &&
+ rm -fr sub ||
+ cleanup_fail &&
+
+ PAGER=wc &&
+ stampname=\$(pwd)/core.pager_used &&
+ export PAGER stampname &&
+ git config core.pager 'wc >\"\$stampname\"' &&
+ mkdir sub &&
+ (
+ cd sub &&
+ $full_command
+ ) &&
+ ${if_local_config}test -e core.pager_used
+ "
+}
+
+test_GIT_PAGER_overrides() {
+ parse_args "$@"
+
+ $test_expectation TTY "$cmd - GIT_PAGER overrides core.pager" "
+ rm -f GIT_PAGER_used ||
+ cleanup_fail &&
+
+ git config core.pager wc &&
+ GIT_PAGER='wc >GIT_PAGER_used' &&
+ export GIT_PAGER &&
+ $full_command &&
+ test -e GIT_PAGER_used
+ "
+}
+
+test_doesnt_paginate() {
+ parse_args "$@"
+
+ $test_expectation TTY "no pager for '$cmd'" "
+ rm -f GIT_PAGER_used ||
+ cleanup_fail &&
+
+ GIT_PAGER='wc >GIT_PAGER_used' &&
+ export GIT_PAGER &&
+ $full_command &&
+ ! test -e GIT_PAGER_used
+ "
+}
+
+test_pager_choices() {
+ test_default_pager expect_success "$@"
+ test_PAGER_overrides expect_success "$@"
+ test_core_pager_overrides expect_success "$@"
+ test_core_pager_subdir expect_success "$@"
+ test_GIT_PAGER_overrides expect_success "$@"
+}
+
+test_expect_success 'setup: some aliases' '
+ git config alias.aliasedlog log &&
+ git config alias.true "!true"
+'
+
+test_pager_choices 'git log'
+test_pager_choices 'git -p log'
+test_pager_choices 'git aliasedlog'
+
+test_default_pager expect_success 'git -p aliasedlog'
+test_PAGER_overrides expect_success 'git -p aliasedlog'
+test_core_pager_overrides expect_success 'git -p aliasedlog'
+test_core_pager_subdir expect_failure 'git -p aliasedlog'
+test_GIT_PAGER_overrides expect_success 'git -p aliasedlog'
+
+test_default_pager expect_success 'git -p true'
+test_PAGER_overrides expect_success 'git -p true'
+test_core_pager_overrides expect_success 'git -p true'
+test_core_pager_subdir expect_failure 'git -p true'
+test_GIT_PAGER_overrides expect_success 'git -p true'
+
+test_default_pager expect_success test_must_fail 'git -p request-pull'
+test_PAGER_overrides expect_success test_must_fail 'git -p request-pull'
+test_core_pager_overrides expect_success test_must_fail 'git -p request-pull'
+test_core_pager_subdir expect_failure test_must_fail 'git -p request-pull'
+test_GIT_PAGER_overrides expect_success test_must_fail 'git -p request-pull'
+
+test_default_pager expect_success test_must_fail 'git -p'
+test_PAGER_overrides expect_success test_must_fail 'git -p'
+test_local_config_ignored expect_failure test_must_fail 'git -p'
+test_no_local_config_subdir expect_success test_must_fail 'git -p'
+test_GIT_PAGER_overrides expect_success test_must_fail 'git -p'
+
+test_doesnt_paginate expect_failure test_must_fail 'git -p nonsense'
+
+test_done
diff --git a/t/t7006/test-terminal.perl b/t/t7006/test-terminal.perl
new file mode 100755
index 0000000..73ff809
--- /dev/null
+++ b/t/t7006/test-terminal.perl
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use IO::Pty;
+use File::Copy;
+
+# Run @$argv in the background with stdout redirected to $out.
+sub start_child {
+ my ($argv, $out) = @_;
+ my $pid = fork;
+ if (not defined $pid) {
+ die "fork failed: $!"
+ } elsif ($pid == 0) {
+ open STDOUT, ">&", $out;
+ close $out;
+ exec(@$argv) or die "cannot exec '$argv->[0]': $!"
+ }
+ return $pid;
+}
+
+# Wait for $pid to finish.
+sub finish_child {
+ # Simplified from wait_or_whine() in run-command.c.
+ my ($pid) = @_;
+
+ my $waiting = waitpid($pid, 0);
+ if ($waiting < 0) {
+ die "waitpid failed: $!";
+ } elsif ($? & 127) {
+ my $code = $? & 127;
+ warn "died of signal $code";
+ return $code - 128;
+ } else {
+ return $? >> 8;
+ }
+}
+
+sub xsendfile {
+ my ($out, $in) = @_;
+
+ # Note: the real sendfile() cannot read from a terminal.
+
+ # It is unspecified by POSIX whether reads
+ # from a disconnected terminal will return
+ # EIO (as in AIX 4.x, IRIX, and Linux) or
+ # end-of-file. Either is fine.
+ copy($in, $out, 4096) or $!{EIO} or die "cannot copy from child: $!";
+}
+
+if ($#ARGV < 1) {
+ die "usage: test-terminal program args";
+}
+my $master = new IO::Pty;
+my $slave = $master->slave;
+my $pid = start_child(\@ARGV, $slave);
+close $slave;
+xsendfile(\*STDOUT, $master);
+exit(finish_child($pid));
diff --git a/t/t7007-show.sh b/t/t7007-show.sh
new file mode 100755
index 0000000..cce222f
--- /dev/null
+++ b/t/t7007-show.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+test_description='git show'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo hello world >foo &&
+ H=$(git hash-object -w foo) &&
+ git tag -a foo-tag -m "Tags $H" $H &&
+ HH=$(expr "$H" : "\(..\)") &&
+ H38=$(expr "$H" : "..\(.*\)") &&
+ rm -f .git/objects/$HH/$H38
+'
+
+test_expect_success 'showing a tag that point at a missing object' '
+ test_must_fail git --no-pager show foo-tag
+'
+
+test_done
diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh
new file mode 100755
index 0000000..eb8ca88
--- /dev/null
+++ b/t/t7008-grep-binary.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='git grep in binary files'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' "
+ printf 'binary\000file\n' >a &&
+ git add a &&
+ git commit -m.
+"
+
+test_expect_success 'git grep ina a' '
+ echo Binary file a matches >expect &&
+ git grep ina a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git grep -ah ina a' '
+ git grep -ah ina a >actual &&
+ test_cmp a actual
+'
+
+test_expect_success 'git grep -I ina a' '
+ : >expect &&
+ test_must_fail git grep -I ina a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git grep -c ina a' '
+ echo a:1 >expect &&
+ git grep -c ina a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git grep -l ina a' '
+ echo a >expect &&
+ git grep -l ina a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git grep -L bar a' '
+ echo a >expect &&
+ git grep -L bar a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git grep -q ina a' '
+ : >expect &&
+ git grep -q ina a >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'git grep -F ile a' '
+ git grep -F ile a
+'
+
+test_expect_success 'git grep -Fi iLE a' '
+ git grep -Fi iLE a
+'
+
+# This test actually passes on platforms where regexec() supports the
+# flag REG_STARTEND.
+test_expect_failure 'git grep ile a' '
+ git grep ile a
+'
+
+test_expect_failure 'git grep .fi a' '
+ git grep .fi a
+'
+
+test_expect_success 'git grep -F y<NUL>f a' "
+ printf 'y\000f' >f &&
+ git grep -f f -F a
+"
+
+test_expect_success 'git grep -F y<NUL>x a' "
+ printf 'y\000x' >f &&
+ test_must_fail git grep -f f -F a
+"
+
+test_expect_success 'git grep -Fi Y<NUL>f a' "
+ printf 'Y\000f' >f &&
+ git grep -f f -Fi a
+"
+
+test_expect_failure 'git grep -Fi Y<NUL>x a' "
+ printf 'Y\000x' >f &&
+ test_must_fail git grep -f f -Fi a
+"
+
+test_expect_success 'git grep y<NUL>f a' "
+ printf 'y\000f' >f &&
+ git grep -f f a
+"
+
+test_expect_failure 'git grep y<NUL>x a' "
+ printf 'y\000x' >f &&
+ test_must_fail git grep -f f a
+"
+
+test_done
diff --git a/t/t7010-setup.sh b/t/t7010-setup.sh
new file mode 100755
index 0000000..0335a9a
--- /dev/null
+++ b/t/t7010-setup.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+test_description='setup taking and sanitizing funny paths'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ mkdir -p a/b/c a/e &&
+ D=$(pwd) &&
+ >a/b/c/d &&
+ >a/e/f
+
+'
+
+test_expect_success 'git add (absolute)' '
+
+ git add "$D/a/b/c/d" &&
+ git ls-files >current &&
+ echo a/b/c/d >expect &&
+ test_cmp expect current
+
+'
+
+
+test_expect_success 'git add (funny relative)' '
+
+ rm -f .git/index &&
+ (
+ cd a/b &&
+ git add "../e/./f"
+ ) &&
+ git ls-files >current &&
+ echo a/e/f >expect &&
+ test_cmp expect current
+
+'
+
+test_expect_success 'git rm (absolute)' '
+
+ rm -f .git/index &&
+ git add a &&
+ git rm -f --cached "$D/a/b/c/d" &&
+ git ls-files >current &&
+ echo a/e/f >expect &&
+ test_cmp expect current
+
+'
+
+test_expect_success 'git rm (funny relative)' '
+
+ rm -f .git/index &&
+ git add a &&
+ (
+ cd a/b &&
+ git rm -f --cached "../e/./f"
+ ) &&
+ git ls-files >current &&
+ echo a/b/c/d >expect &&
+ test_cmp expect current
+
+'
+
+test_expect_success 'git ls-files (absolute)' '
+
+ rm -f .git/index &&
+ git add a &&
+ git ls-files "$D/a/e/../b" >current &&
+ echo a/b/c/d >expect &&
+ test_cmp expect current
+
+'
+
+test_expect_success 'git ls-files (relative #1)' '
+
+ rm -f .git/index &&
+ git add a &&
+ (
+ cd a/b &&
+ git ls-files "../b/c"
+ ) >current &&
+ echo c/d >expect &&
+ test_cmp expect current
+
+'
+
+test_expect_success 'git ls-files (relative #2)' '
+
+ rm -f .git/index &&
+ git add a &&
+ (
+ cd a/b &&
+ git ls-files --full-name "../e/f"
+ ) >current &&
+ echo a/e/f >expect &&
+ test_cmp expect current
+
+'
+
+test_expect_success 'git ls-files (relative #3)' '
+
+ rm -f .git/index &&
+ git add a &&
+ (
+ cd a/b &&
+ git ls-files "../e/f"
+ ) >current &&
+ echo ../e/f >expect &&
+ test_cmp expect current
+
+'
+
+test_expect_success 'commit using absolute path names' '
+ git commit -m "foo" &&
+ echo aa >>a/b/c/d &&
+ git commit -m "aa" "$(pwd)/a/b/c/d"
+'
+
+test_expect_success 'log using absolute path names' '
+ echo bb >>a/b/c/d &&
+ git commit -m "bb" "$(pwd)/a/b/c/d" &&
+
+ git log a/b/c/d >f1.txt &&
+ git log "$(pwd)/a/b/c/d" >f2.txt &&
+ test_cmp f1.txt f2.txt
+'
+
+test_expect_success 'blame using absolute path names' '
+ git blame a/b/c/d >f1.txt &&
+ git blame "$(pwd)/a/b/c/d" >f2.txt &&
+ test_cmp f1.txt f2.txt
+'
+
+test_expect_success 'setup deeper work tree' '
+ test_create_repo tester
+'
+
+test_expect_success 'add a directory outside the work tree' '(
+ cd tester &&
+ d1="$(cd .. ; pwd)" &&
+ test_must_fail git add "$d1"
+)'
+
+
+test_expect_success 'add a file outside the work tree, nasty case 1' '(
+ cd tester &&
+ f="$(pwd)x" &&
+ echo "$f" &&
+ touch "$f" &&
+ test_must_fail git add "$f"
+)'
+
+test_expect_success 'add a file outside the work tree, nasty case 2' '(
+ cd tester &&
+ f="$(pwd | sed "s/.$//")x" &&
+ echo "$f" &&
+ touch "$f" &&
+ test_must_fail git add "$f"
+)'
+
+test_done
diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh
new file mode 100755
index 0000000..bb4066f
--- /dev/null
+++ b/t/t7011-skip-worktree-reading.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nguyá»…n Thái Ngá»c Duy
+#
+
+test_description='skip-worktree bit test'
+
+. ./test-lib.sh
+
+cat >expect.full <<EOF
+H 1
+H 2
+H init.t
+H sub/1
+H sub/2
+EOF
+
+cat >expect.skip <<EOF
+S 1
+H 2
+H init.t
+S sub/1
+H sub/2
+EOF
+
+NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+ZERO_SHA0=0000000000000000000000000000000000000000
+setup_absent() {
+ test -f 1 && rm 1
+ git update-index --remove 1 &&
+ git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+ git update-index --skip-worktree 1
+}
+
+test_absent() {
+ echo "100644 $NULL_SHA1 0 1" > expected &&
+ git ls-files --stage 1 > result &&
+ test_cmp expected result &&
+ test ! -f 1
+}
+
+setup_dirty() {
+ git update-index --force-remove 1 &&
+ echo dirty > 1 &&
+ git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+ git update-index --skip-worktree 1
+}
+
+test_dirty() {
+ echo "100644 $NULL_SHA1 0 1" > expected &&
+ git ls-files --stage 1 > result &&
+ test_cmp expected result &&
+ echo dirty > expected
+ test_cmp expected 1
+}
+
+test_expect_success 'setup' '
+ test_commit init &&
+ mkdir sub &&
+ touch ./1 ./2 sub/1 sub/2 &&
+ git add 1 2 sub/1 sub/2 &&
+ git update-index --skip-worktree 1 sub/1 &&
+ git ls-files -t > result &&
+ test_cmp expect.skip result
+'
+
+test_expect_success 'update-index' '
+ setup_absent &&
+ git update-index 1 &&
+ test_absent
+'
+
+test_expect_success 'update-index' '
+ setup_dirty &&
+ git update-index 1 &&
+ test_dirty
+'
+
+test_expect_success 'update-index --remove' '
+ setup_absent &&
+ git update-index --remove 1 &&
+ test -z "$(git ls-files 1)" &&
+ test ! -f 1
+'
+
+test_expect_success 'update-index --remove' '
+ setup_dirty &&
+ git update-index --remove 1 &&
+ test -z "$(git ls-files 1)" &&
+ echo dirty > expected &&
+ test_cmp expected 1
+'
+
+test_expect_success 'ls-files --delete' '
+ setup_absent &&
+ test -z "$(git ls-files -d)"
+'
+
+test_expect_success 'ls-files --delete' '
+ setup_dirty &&
+ test -z "$(git ls-files -d)"
+'
+
+test_expect_success 'ls-files --modified' '
+ setup_absent &&
+ test -z "$(git ls-files -m)"
+'
+
+test_expect_success 'ls-files --modified' '
+ setup_dirty &&
+ test -z "$(git ls-files -m)"
+'
+
+test_expect_success 'grep with skip-worktree file' '
+ git update-index --no-skip-worktree 1 &&
+ echo test > 1 &&
+ git update-index 1 &&
+ git update-index --skip-worktree 1 &&
+ rm 1 &&
+ test "$(git grep --no-ext-grep test)" = "1:test"
+'
+
+echo ":000000 100644 $ZERO_SHA0 $NULL_SHA1 A 1" > expected
+test_expect_success 'diff-index does not examine skip-worktree absent entries' '
+ setup_absent &&
+ git diff-index HEAD -- 1 > result &&
+ test_cmp expected result
+'
+
+test_expect_success 'diff-index does not examine skip-worktree dirty entries' '
+ setup_dirty &&
+ git diff-index HEAD -- 1 > result &&
+ test_cmp expected result
+'
+
+test_expect_success 'diff-files does not examine skip-worktree absent entries' '
+ setup_absent &&
+ test -z "$(git diff-files -- one)"
+'
+
+test_expect_success 'diff-files does not examine skip-worktree dirty entries' '
+ setup_dirty &&
+ test -z "$(git diff-files -- one)"
+'
+
+test_expect_success 'git-rm succeeds on skip-worktree absent entries' '
+ setup_absent &&
+ git rm 1
+'
+
+test_expect_success 'commit on skip-worktree absent entries' '
+ git reset &&
+ setup_absent &&
+ test_must_fail git commit -m null 1
+'
+
+test_expect_success 'commit on skip-worktree dirty entries' '
+ git reset &&
+ setup_dirty &&
+ test_must_fail git commit -m null 1
+'
+
+test_done
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
new file mode 100755
index 0000000..582d0b5
--- /dev/null
+++ b/t/t7012-skip-worktree-writing.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nguyá»…n Thái Ngá»c Duy
+#
+
+test_description='test worktree writing operations when skip-worktree is used'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit init &&
+ echo modified >> init.t &&
+ touch added &&
+ git add init.t added &&
+ git commit -m "modified and added" &&
+ git tag top
+'
+
+test_expect_success 'read-tree updates worktree, absent case' '
+ git checkout -f top &&
+ git update-index --skip-worktree init.t &&
+ rm init.t &&
+ git read-tree -m -u HEAD^ &&
+ echo init > expected &&
+ test_cmp expected init.t
+'
+
+test_expect_success 'read-tree updates worktree, dirty case' '
+ git checkout -f top &&
+ git update-index --skip-worktree init.t &&
+ echo dirty >> init.t &&
+ test_must_fail git read-tree -m -u HEAD^ &&
+ grep -q dirty init.t &&
+ test "$(git ls-files -t init.t)" = "S init.t" &&
+ git update-index --no-skip-worktree init.t
+'
+
+test_expect_success 'read-tree removes worktree, absent case' '
+ git checkout -f top &&
+ git update-index --skip-worktree added &&
+ rm added &&
+ git read-tree -m -u HEAD^ &&
+ test ! -f added
+'
+
+test_expect_success 'read-tree removes worktree, dirty case' '
+ git checkout -f top &&
+ git update-index --skip-worktree added &&
+ echo dirty >> added &&
+ test_must_fail git read-tree -m -u HEAD^ &&
+ grep -q dirty added &&
+ test "$(git ls-files -t added)" = "S added" &&
+ git update-index --no-skip-worktree added
+'
+
+NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+ZERO_SHA0=0000000000000000000000000000000000000000
+setup_absent() {
+ test -f 1 && rm 1
+ git update-index --remove 1 &&
+ git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+ git update-index --skip-worktree 1
+}
+
+test_absent() {
+ echo "100644 $NULL_SHA1 0 1" > expected &&
+ git ls-files --stage 1 > result &&
+ test_cmp expected result &&
+ test ! -f 1
+}
+
+setup_dirty() {
+ git update-index --force-remove 1 &&
+ echo dirty > 1 &&
+ git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+ git update-index --skip-worktree 1
+}
+
+test_dirty() {
+ echo "100644 $NULL_SHA1 0 1" > expected &&
+ git ls-files --stage 1 > result &&
+ test_cmp expected result &&
+ echo dirty > expected
+ test_cmp expected 1
+}
+
+cat >expected <<EOF
+S 1
+H 2
+H init.t
+S sub/1
+H sub/2
+EOF
+
+test_expect_success 'index setup' '
+ git checkout -f init &&
+ mkdir sub &&
+ touch ./1 ./2 sub/1 sub/2 &&
+ git add 1 2 sub/1 sub/2 &&
+ git update-index --skip-worktree 1 sub/1 &&
+ git ls-files -t > result &&
+ test_cmp expected result
+'
+
+test_expect_success 'git-add ignores worktree content' '
+ setup_absent &&
+ git add 1 &&
+ test_absent
+'
+
+test_expect_success 'git-add ignores worktree content' '
+ setup_dirty &&
+ git add 1 &&
+ test_dirty
+'
+
+test_expect_success 'git-rm fails if worktree is dirty' '
+ setup_dirty &&
+ test_must_fail git rm 1 &&
+ test_dirty
+'
+
+cat >expected <<EOF
+Would remove expected
+Would remove result
+EOF
+test_expect_success 'git-clean, absent case' '
+ setup_absent &&
+ git clean -n > result &&
+ test_cmp expected result
+'
+
+test_expect_success 'git-clean, dirty case' '
+ setup_dirty &&
+ git clean -n > result &&
+ test_cmp expected result
+'
+
+#TODO test_expect_failure 'git-apply adds file' false
+#TODO test_expect_failure 'git-apply updates file' false
+#TODO test_expect_failure 'git-apply removes file' false
+#TODO test_expect_failure 'git-mv to skip-worktree' false
+#TODO test_expect_failure 'git-mv from skip-worktree' false
+#TODO test_expect_failure 'git-checkout' false
+
+test_done
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
new file mode 100755
index 0000000..fcac472
--- /dev/null
+++ b/t/t7060-wtstatus.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='basic work tree status reporting'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit A &&
+ test_commit B oneside added &&
+ git checkout A^0 &&
+ test_commit C oneside created
+'
+
+test_expect_success 'A/A conflict' '
+ git checkout B^0 &&
+ test_must_fail git merge C
+'
+
+test_expect_success 'Report path with conflict' '
+ git diff --cached --name-status >actual &&
+ echo "U oneside" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Report new path with conflict' '
+ git diff --cached --name-status HEAD^ >actual &&
+ echo "U oneside" >expect &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+# On branch side
+# Unmerged paths:
+# (use "git add/rm <file>..." as appropriate to mark resolution)
+#
+# deleted by us: foo
+#
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success 'M/D conflict does not segfault' '
+ mkdir mdconflict &&
+ (
+ cd mdconflict &&
+ git init &&
+ test_commit initial foo "" &&
+ test_commit modify foo foo &&
+ git checkout -b side HEAD^ &&
+ git rm foo &&
+ git commit -m delete &&
+ test_must_fail git merge master &&
+ test_must_fail git commit --dry-run >../actual &&
+ test_cmp ../expect ../actual &&
+ git status >../actual &&
+ test_cmp ../expect ../actual
+ )
+'
+
+test_done
diff --git a/t/t7101-reset.sh b/t/t7101-reset.sh
new file mode 100755
index 0000000..96e163f
--- /dev/null
+++ b/t/t7101-reset.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Shawn Pearce
+#
+
+test_description='git reset should cull empty subdirs'
+. ./test-lib.sh
+
+test_expect_success \
+ 'creating initial files' \
+ 'mkdir path0 &&
+ cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
+ git add path0/COPYING &&
+ git commit -m add -a'
+
+test_expect_success \
+ 'creating second files' \
+ 'mkdir path1 &&
+ mkdir path1/path2 &&
+ cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
+ cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
+ cp "$TEST_DIRECTORY"/../COPYING COPYING &&
+ cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO &&
+ git add path1/path2/COPYING &&
+ git add path1/COPYING &&
+ git add COPYING &&
+ git add path0/COPYING-TOO &&
+ git commit -m change -a'
+
+test_expect_success \
+ 'resetting tree HEAD^' \
+ 'git reset --hard HEAD^'
+
+test_expect_success \
+ 'checking initial files exist after rewind' \
+ 'test -d path0 &&
+ test -f path0/COPYING'
+
+test_expect_success \
+ 'checking lack of path1/path2/COPYING' \
+ '! test -f path1/path2/COPYING'
+
+test_expect_success \
+ 'checking lack of path1/COPYING' \
+ '! test -f path1/COPYING'
+
+test_expect_success \
+ 'checking lack of COPYING' \
+ '! test -f COPYING'
+
+test_expect_success \
+ 'checking checking lack of path1/COPYING-TOO' \
+ '! test -f path0/COPYING-TOO'
+
+test_expect_success \
+ 'checking lack of path1/path2' \
+ '! test -d path1/path2'
+
+test_expect_success \
+ 'checking lack of path1' \
+ '! test -d path1'
+
+test_done
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
new file mode 100755
index 0000000..b8cf260
--- /dev/null
+++ b/t/t7102-reset.sh
@@ -0,0 +1,479 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carlos Rica
+#
+
+test_description='git reset
+
+Documented tests for git reset'
+
+. ./test-lib.sh
+
+test_expect_success 'creating initial files and commits' '
+ test_tick &&
+ echo "1st file" >first &&
+ git add first &&
+ git commit -m "create 1st file" &&
+
+ echo "2nd file" >second &&
+ git add second &&
+ git commit -m "create 2nd file" &&
+
+ echo "2nd line 1st file" >>first &&
+ git commit -a -m "modify 1st file" &&
+
+ git rm first &&
+ git mv second secondfile &&
+ git commit -a -m "remove 1st and rename 2nd" &&
+
+ echo "1st line 2nd file" >secondfile &&
+ echo "2nd line 2nd file" >>secondfile &&
+ git commit -a -m "modify 2nd file"
+'
+# git log --pretty=oneline # to see those SHA1 involved
+
+check_changes () {
+ test "$(git rev-parse HEAD)" = "$1" &&
+ git diff | test_cmp .diff_expect - &&
+ git diff --cached | test_cmp .cached_expect - &&
+ for FILE in *
+ do
+ echo $FILE':'
+ cat $FILE || return
+ done | test_cmp .cat_expect -
+}
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+
+test_expect_success 'giving a non existing revision should fail' '
+ test_must_fail git reset aaaaaa &&
+ test_must_fail git reset --mixed aaaaaa &&
+ test_must_fail git reset --soft aaaaaa &&
+ test_must_fail git reset --hard aaaaaa &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success 'reset --soft with unmerged index should fail' '
+ touch .git/MERGE_HEAD &&
+ echo "100644 44c5b5884550c17758737edcced463447b91d42b 1 un" |
+ git update-index --index-info &&
+ test_must_fail git reset --soft HEAD &&
+ rm .git/MERGE_HEAD &&
+ git rm --cached -- un
+'
+
+test_expect_success \
+ 'giving paths with options different than --mixed should fail' '
+ test_must_fail git reset --soft -- first &&
+ test_must_fail git reset --hard -- first &&
+ test_must_fail git reset --soft HEAD^ -- first &&
+ test_must_fail git reset --hard HEAD^ -- first &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success 'giving unrecognized options should fail' '
+ test_must_fail git reset --other &&
+ test_must_fail git reset -o &&
+ test_must_fail git reset --mixed --other &&
+ test_must_fail git reset --mixed -o &&
+ test_must_fail git reset --soft --other &&
+ test_must_fail git reset --soft -o &&
+ test_must_fail git reset --hard --other &&
+ test_must_fail git reset --hard -o &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+ 'trying to do reset --soft with pending merge should fail' '
+ git branch branch1 &&
+ git branch branch2 &&
+
+ git checkout branch1 &&
+ echo "3rd line in branch1" >>secondfile &&
+ git commit -a -m "change in branch1" &&
+
+ git checkout branch2 &&
+ echo "3rd line in branch2" >>secondfile &&
+ git commit -a -m "change in branch2" &&
+
+ test_must_fail git merge branch1 &&
+ test_must_fail git reset --soft &&
+
+ printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
+ git commit -a -m "the change in branch2" &&
+
+ git checkout master &&
+ git branch -D branch1 branch2 &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+ 'trying to do reset --soft with pending checkout merge should fail' '
+ git branch branch3 &&
+ git branch branch4 &&
+
+ git checkout branch3 &&
+ echo "3rd line in branch3" >>secondfile &&
+ git commit -a -m "line in branch3" &&
+
+ git checkout branch4 &&
+ echo "3rd line in branch4" >>secondfile &&
+
+ git checkout -m branch3 &&
+ test_must_fail git reset --soft &&
+
+ printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
+ git commit -a -m "the line in branch3" &&
+
+ git checkout master &&
+ git branch -D branch3 branch4 &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+ 'resetting to HEAD with no changes should succeed and do nothing' '
+ git reset --hard &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ git reset --hard HEAD &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ git reset --soft &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ git reset --soft HEAD &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ git reset --mixed &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ git reset --mixed HEAD &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ git reset &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+ git reset HEAD &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+cat >.cached_expect <<EOF
+diff --git a/secondfile b/secondfile
+index 1bbba79..44c5b58 100644
+--- a/secondfile
++++ b/secondfile
+@@ -1 +1,2 @@
+-2nd file
++1st line 2nd file
++2nd line 2nd file
+EOF
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success '--soft reset only should show changes in diff --cached' '
+ git reset --soft HEAD^ &&
+ check_changes d1a4bc3abce4829628ae2dcb0d60ef3d1a78b1c4 &&
+ test "$(git rev-parse ORIG_HEAD)" = \
+ 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+3rd line 2nd file
+EOF
+test_expect_success \
+ 'changing files and redo the last commit should succeed' '
+ echo "3rd line 2nd file" >>secondfile &&
+ git commit -a -C ORIG_HEAD &&
+ check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d &&
+ test "$(git rev-parse ORIG_HEAD)" = \
+ 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+first:
+1st file
+2nd line 1st file
+second:
+2nd file
+EOF
+test_expect_success \
+ '--hard reset should change the files and undo commits permanently' '
+ git reset --hard HEAD~2 &&
+ check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+ test "$(git rev-parse ORIG_HEAD)" = \
+ 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d
+'
+
+>.diff_expect
+cat >.cached_expect <<EOF
+diff --git a/first b/first
+deleted file mode 100644
+index 8206c22..0000000
+--- a/first
++++ /dev/null
+@@ -1,2 +0,0 @@
+-1st file
+-2nd line 1st file
+diff --git a/second b/second
+deleted file mode 100644
+index 1bbba79..0000000
+--- a/second
++++ /dev/null
+@@ -1 +0,0 @@
+-2nd file
+diff --git a/secondfile b/secondfile
+new file mode 100644
+index 0000000..44c5b58
+--- /dev/null
++++ b/secondfile
+@@ -0,0 +1,2 @@
++1st line 2nd file
++2nd line 2nd file
+EOF
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success \
+ 'redoing changes adding them without commit them should succeed' '
+ git rm first &&
+ git mv second secondfile &&
+
+ echo "1st line 2nd file" >secondfile &&
+ echo "2nd line 2nd file" >>secondfile &&
+ git add secondfile &&
+ check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e
+'
+
+cat >.diff_expect <<EOF
+diff --git a/first b/first
+deleted file mode 100644
+index 8206c22..0000000
+--- a/first
++++ /dev/null
+@@ -1,2 +0,0 @@
+-1st file
+-2nd line 1st file
+diff --git a/second b/second
+deleted file mode 100644
+index 1bbba79..0000000
+--- a/second
++++ /dev/null
+@@ -1 +0,0 @@
+-2nd file
+EOF
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success '--mixed reset to HEAD should unadd the files' '
+ git reset &&
+ check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+ test "$(git rev-parse ORIG_HEAD)" = \
+ ddaefe00f1da16864591c61fdc7adb5d7cd6b74e
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success 'redoing the last two commits should succeed' '
+ git add secondfile &&
+ git reset --hard ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+
+ git rm first &&
+ git mv second secondfile &&
+ git commit -a -m "remove 1st and rename 2nd" &&
+
+ echo "1st line 2nd file" >secondfile &&
+ echo "2nd line 2nd file" >>secondfile &&
+ git commit -a -m "modify 2nd file" &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+3rd line in branch2
+EOF
+test_expect_success '--hard reset to HEAD should clear a failed merge' '
+ git branch branch1 &&
+ git branch branch2 &&
+
+ git checkout branch1 &&
+ echo "3rd line in branch1" >>secondfile &&
+ git commit -a -m "change in branch1" &&
+
+ git checkout branch2 &&
+ echo "3rd line in branch2" >>secondfile &&
+ git commit -a -m "change in branch2" &&
+
+ test_must_fail git pull . branch1 &&
+ git reset --hard &&
+ check_changes 77abb337073fb4369a7ad69ff6f5ec0e4d6b54bb
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success \
+ '--hard reset to ORIG_HEAD should clear a fast-forward merge' '
+ git reset --hard HEAD^ &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+
+ git pull . branch1 &&
+ git reset --hard ORIG_HEAD &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+
+ git checkout master &&
+ git branch -D branch1 branch2 &&
+ check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+cat > expect << EOF
+diff --git a/file1 b/file1
+index d00491f..7ed6ff8 100644
+--- a/file1
++++ b/file1
+@@ -1 +1 @@
+-1
++5
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 0cfbf08..0000000
+--- a/file2
++++ /dev/null
+@@ -1 +0,0 @@
+-2
+EOF
+cat > cached_expect << EOF
+diff --git a/file4 b/file4
+new file mode 100644
+index 0000000..b8626c4
+--- /dev/null
++++ b/file4
+@@ -0,0 +1 @@
++4
+EOF
+test_expect_success 'test --mixed <paths>' '
+ echo 1 > file1 &&
+ echo 2 > file2 &&
+ git add file1 file2 &&
+ test_tick &&
+ git commit -m files &&
+ git rm file2 &&
+ echo 3 > file3 &&
+ echo 4 > file4 &&
+ echo 5 > file1 &&
+ git add file1 file3 file4 &&
+ test_must_fail git reset HEAD -- file1 file2 file3 &&
+ git diff > output &&
+ test_cmp output expect &&
+ git diff --cached > output &&
+ test_cmp output cached_expect
+'
+
+test_expect_success 'test resetting the index at give paths' '
+
+ mkdir sub &&
+ >sub/file1 &&
+ >sub/file2 &&
+ git update-index --add sub/file1 sub/file2 &&
+ T=$(git write-tree) &&
+ test_must_fail git reset HEAD sub/file2 &&
+ U=$(git write-tree) &&
+ echo "$T" &&
+ echo "$U" &&
+ test_must_fail git diff-index --cached --exit-code "$T" &&
+ test "$T" != "$U"
+
+'
+
+test_expect_success 'resetting an unmodified path is a no-op' '
+ git reset --hard &&
+ git reset -- file1 &&
+ git diff-files --exit-code &&
+ git diff-index --cached --exit-code HEAD
+'
+
+cat > expect << EOF
+Unstaged changes after reset:
+M file2
+EOF
+
+test_expect_success '--mixed refreshes the index' '
+ echo 123 >> file2 &&
+ git reset --mixed HEAD > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'disambiguation (1)' '
+
+ git reset --hard &&
+ >secondfile &&
+ git add secondfile &&
+ test_must_fail git reset secondfile &&
+ test -z "$(git diff --cached --name-only)" &&
+ test -f secondfile &&
+ test ! -s secondfile
+
+'
+
+test_expect_success 'disambiguation (2)' '
+
+ git reset --hard &&
+ >secondfile &&
+ git add secondfile &&
+ rm -f secondfile &&
+ test_must_fail git reset secondfile &&
+ test -n "$(git diff --cached --name-only -- secondfile)" &&
+ test ! -f secondfile
+
+'
+
+test_expect_success 'disambiguation (3)' '
+
+ git reset --hard &&
+ >secondfile &&
+ git add secondfile &&
+ rm -f secondfile &&
+ test_must_fail git reset HEAD secondfile &&
+ test -z "$(git diff --cached --name-only)" &&
+ test ! -f secondfile
+
+'
+
+test_expect_success 'disambiguation (4)' '
+
+ git reset --hard &&
+ >secondfile &&
+ git add secondfile &&
+ rm -f secondfile &&
+ test_must_fail git reset -- secondfile &&
+ test -z "$(git diff --cached --name-only)" &&
+ test ! -f secondfile
+'
+
+test_done
diff --git a/t/t7103-reset-bare.sh b/t/t7103-reset-bare.sh
new file mode 100755
index 0000000..1eef93c
--- /dev/null
+++ b/t/t7103-reset-bare.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git reset in a bare repository'
+. ./test-lib.sh
+
+test_expect_success 'setup non-bare' '
+ echo one >file &&
+ git add file &&
+ git commit -m one &&
+ echo two >file &&
+ git commit -a -m two
+'
+
+test_expect_success '"hard" reset requires a worktree' '
+ (cd .git &&
+ test_must_fail git reset --hard)
+'
+
+test_expect_success '"merge" reset requires a worktree' '
+ (cd .git &&
+ test_must_fail git reset --merge)
+'
+
+test_expect_success '"keep" reset requires a worktree' '
+ (cd .git &&
+ test_must_fail git reset --keep)
+'
+
+test_expect_success '"mixed" reset is ok' '
+ (cd .git && git reset)
+'
+
+test_expect_success '"soft" reset is ok' '
+ (cd .git && git reset --soft)
+'
+
+test_expect_success 'hard reset works with GIT_WORK_TREE' '
+ mkdir worktree &&
+ GIT_WORK_TREE=$PWD/worktree GIT_DIR=$PWD/.git git reset --hard &&
+ test_cmp file worktree/file
+'
+
+test_expect_success 'setup bare' '
+ git clone --bare . bare.git &&
+ cd bare.git
+'
+
+test_expect_success '"hard" reset is not allowed in bare' '
+ test_must_fail git reset --hard HEAD^
+'
+
+test_expect_success '"merge" reset is not allowed in bare' '
+ test_must_fail git reset --merge HEAD^
+'
+
+test_expect_success '"keep" reset is not allowed in bare' '
+ test_must_fail git reset --keep HEAD^
+'
+
+test_expect_success '"mixed" reset is not allowed in bare' '
+ test_must_fail git reset --mixed HEAD^
+'
+
+test_expect_success '"soft" reset is allowed in bare' '
+ git reset --soft HEAD^ &&
+ test "`git show --pretty=format:%s | head -n 1`" = "one"
+'
+
+test_done
diff --git a/t/t7104-reset.sh b/t/t7104-reset.sh
new file mode 100755
index 0000000..f136ee7
--- /dev/null
+++ b/t/t7104-reset.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='reset --hard unmerged'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ mkdir before later &&
+ >before/1 &&
+ >before/2 &&
+ >hello &&
+ >later/3 &&
+ git add before hello later &&
+ git commit -m world &&
+
+ H=$(git rev-parse :hello) &&
+ git rm --cached hello &&
+ echo "100644 $H 2 hello" | git update-index --index-info &&
+
+ rm -f hello &&
+ mkdir -p hello &&
+ >hello/world &&
+ test "$(git ls-files -o)" = hello/world
+
+'
+
+test_expect_success 'reset --hard should restore unmerged ones' '
+
+ git reset --hard &&
+ git ls-files --error-unmatch before/1 before/2 hello later/3 &&
+ test -f hello
+
+'
+
+test_expect_success 'reset --hard did not corrupt index nor cached-tree' '
+
+ T=$(git write-tree) &&
+ rm -f .git/index &&
+ git add before hello later &&
+ U=$(git write-tree) &&
+ test "$T" = "$U"
+
+'
+
+test_done
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
new file mode 100755
index 0000000..c1f4fc3
--- /dev/null
+++ b/t/t7105-reset-patch.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git reset --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+ mkdir dir &&
+ echo parent > dir/foo &&
+ echo dummy > bar &&
+ git add dir &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+# note: bar sorts before foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+ set_and_save_state dir/foo work work
+ (echo n; echo n) | git reset -p &&
+ verify_saved_state dir/foo &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p' '
+ (echo n; echo y) | git reset -p &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p HEAD^' '
+ (echo n; echo y) | git reset -p HEAD^ &&
+ verify_state dir/foo work parent &&
+ verify_saved_state bar
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success 'git reset -p dir' '
+ set_state dir/foo work work
+ (echo y; echo n) | git reset -p dir &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p -- foo (inside dir)' '
+ set_state dir/foo work work
+ (echo y; echo n) | (cd dir && git reset -p -- foo) &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p HEAD^ -- dir' '
+ (echo y; echo n) | git reset -p HEAD^ -- dir &&
+ verify_state dir/foo work parent &&
+ verify_saved_state bar
+'
+
+test_expect_success 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+
+test_done
diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh
new file mode 100755
index 0000000..70cdd8e
--- /dev/null
+++ b/t/t7110-reset-merge.sh
@@ -0,0 +1,295 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Christian Couder
+#
+
+test_description='Tests for "git reset" with "--merge" and "--keep" options'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ for i in 1 2 3; do echo line $i; done >file1 &&
+ cat file1 >file2 &&
+ git add file1 file2 &&
+ test_tick &&
+ git commit -m "Initial commit" &&
+ git tag initial &&
+ echo line 4 >>file1 &&
+ cat file1 >file2 &&
+ test_tick &&
+ git commit -m "add line 4 to file1" file1 &&
+ git tag second
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: C C C D --merge D D D
+# file2: C D D D --merge C D D
+test_expect_success 'reset --merge is ok with changes in file it does not touch' '
+ git reset --merge HEAD^ &&
+ ! grep 4 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --merge is ok when switching back' '
+ git reset --merge second &&
+ grep 4 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: C C C D --keep D D D
+# file2: C D D D --keep C D D
+test_expect_success 'reset --keep is ok with changes in file it does not touch' '
+ git reset --hard second &&
+ cat file1 >file2 &&
+ git reset --keep HEAD^ &&
+ ! grep 4 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --keep is ok when switching back' '
+ git reset --keep second &&
+ grep 4 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: B B C D --merge D D D
+# file2: C D D D --merge C D D
+test_expect_success 'reset --merge discards changes added to index (1)' '
+ git reset --hard second &&
+ cat file1 >file2 &&
+ echo "line 5" >> file1 &&
+ git add file1 &&
+ git reset --merge HEAD^ &&
+ ! grep 4 file1 &&
+ ! grep 5 file1 &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --merge is ok again when switching back (1)' '
+ git reset --hard initial &&
+ echo "line 5" >> file2 &&
+ git add file2 &&
+ git reset --merge second &&
+ ! grep 4 file2 &&
+ ! grep 5 file1 &&
+ grep 4 file1 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: B B C D --keep (disallowed)
+test_expect_success 'reset --keep fails with changes in index in files it touches' '
+ git reset --hard second &&
+ echo "line 5" >> file1 &&
+ git add file1 &&
+ test_must_fail git reset --keep HEAD^
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: C C C D --merge D D D
+# file2: C C D D --merge D D D
+test_expect_success 'reset --merge discards changes added to index (2)' '
+ git reset --hard second &&
+ echo "line 4" >> file2 &&
+ git add file2 &&
+ git reset --merge HEAD^ &&
+ ! grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff)" &&
+ test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --merge is ok again when switching back (2)' '
+ git reset --hard initial &&
+ git reset --merge second &&
+ ! grep 4 file2 &&
+ grep 4 file1 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: C C C D --keep D D D
+# file2: C C D D --keep C D D
+test_expect_success 'reset --keep keeps changes it does not touch' '
+ git reset --hard second &&
+ echo "line 4" >> file2 &&
+ git add file2 &&
+ git reset --keep HEAD^ &&
+ grep 4 file2 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+ test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --keep keeps changes when switching back' '
+ git reset --keep second &&
+ grep 4 file2 &&
+ grep 4 file1 &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: A B B C --merge (disallowed)
+test_expect_success 'reset --merge fails with changes in file it touches' '
+ git reset --hard second &&
+ echo "line 5" >> file1 &&
+ test_tick &&
+ git commit -m "add line 5" file1 &&
+ sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+ mv file3 file1 &&
+ test_must_fail git reset --merge HEAD^ 2>err.log &&
+ grep file1 err.log | grep "not uptodate"
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: A B B C --keep (disallowed)
+test_expect_success 'reset --keep fails with changes in file it touches' '
+ git reset --hard second &&
+ echo "line 5" >> file1 &&
+ test_tick &&
+ git commit -m "add line 5" file1 &&
+ sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+ mv file3 file1 &&
+ test_must_fail git reset --keep HEAD^ 2>err.log &&
+ grep file1 err.log | grep "not uptodate"
+'
+
+test_expect_success 'setup 3 different branches' '
+ git reset --hard second &&
+ git branch branch1 &&
+ git branch branch2 &&
+ git branch branch3 &&
+ git checkout branch1 &&
+ echo "line 5 in branch1" >> file1 &&
+ test_tick &&
+ git commit -a -m "change in branch1" &&
+ git checkout branch2 &&
+ echo "line 5 in branch2" >> file1 &&
+ test_tick &&
+ git commit -a -m "change in branch2" &&
+ git tag third &&
+ git checkout branch3 &&
+ echo a new file >file3 &&
+ rm -f file1 &&
+ git add file3 &&
+ test_tick &&
+ git commit -a -m "change in branch3"
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: X U B C --merge C C C
+test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
+ git checkout third &&
+ test_must_fail git merge branch1 &&
+ git reset --merge HEAD^ &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+ test -z "$(git diff --cached)" &&
+ test -z "$(git diff)"
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: X U B C --keep (disallowed)
+test_expect_success '"reset --keep HEAD^" fails with pending merge' '
+ git reset --hard third &&
+ test_must_fail git merge branch1 &&
+ test_must_fail git reset --keep HEAD^ 2>err.log &&
+ grep "middle of a merge" err.log
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: X U B B --merge B B B
+test_expect_success '"reset --merge HEAD" is ok with pending merge' '
+ git reset --hard third &&
+ test_must_fail git merge branch1 &&
+ git reset --merge HEAD &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
+ test -z "$(git diff --cached)" &&
+ test -z "$(git diff)"
+'
+
+# The next test will test the following:
+#
+# working index HEAD target working index HEAD
+# ----------------------------------------------------
+# file1: X U B B --keep (disallowed)
+test_expect_success '"reset --keep HEAD" fails with pending merge' '
+ git reset --hard third &&
+ test_must_fail git merge branch1 &&
+ test_must_fail git reset --keep HEAD 2>err.log &&
+ grep "middle of a merge" err.log
+'
+
+test_expect_success '--merge is ok with added/deleted merge' '
+ git reset --hard third &&
+ rm -f file2 &&
+ test_must_fail git merge branch3 &&
+ ! test -f file2 &&
+ test -f file3 &&
+ git diff --exit-code file3 &&
+ git diff --exit-code branch3 file3 &&
+ git reset --merge HEAD &&
+ ! test -f file3 &&
+ ! test -f file2 &&
+ git diff --exit-code --cached
+'
+
+test_expect_success '--keep fails with added/deleted merge' '
+ git reset --hard third &&
+ rm -f file2 &&
+ test_must_fail git merge branch3 &&
+ ! test -f file2 &&
+ test -f file3 &&
+ git diff --exit-code file3 &&
+ git diff --exit-code branch3 file3 &&
+ test_must_fail git reset --keep HEAD 2>err.log &&
+ grep "middle of a merge" err.log
+'
+
+test_done
diff --git a/t/t7111-reset-table.sh b/t/t7111-reset-table.sh
new file mode 100755
index 0000000..ce421ad
--- /dev/null
+++ b/t/t7111-reset-table.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Christian Couder
+#
+
+test_description='Tests to check that "reset" options follow a known table'
+
+. ./test-lib.sh
+
+
+test_expect_success 'creating initial commits' '
+ test_commit E file1 &&
+ test_commit D file1 &&
+ test_commit C file1
+'
+
+while read W1 I1 H1 T opt W2 I2 H2
+do
+ test_expect_success "check: $W1 $I1 $H1 $T --$opt $W2 $I2 $H2" '
+ git reset --hard C &&
+ if test "$I1" != "$H1"
+ then
+ echo "$I1" >file1 &&
+ git add file1
+ fi &&
+ if test "$W1" != "$I1"
+ then
+ echo "$W1" >file1
+ fi &&
+ if test "$W2" != "XXXXX"
+ then
+ git reset --$opt $T &&
+ test "$(cat file1)" = "$W2" &&
+ git checkout-index -f -- file1 &&
+ test "$(cat file1)" = "$I2" &&
+ git checkout -f HEAD -- file1 &&
+ test "$(cat file1)" = "$H2"
+ else
+ test_must_fail git reset --$opt $T
+ fi
+ '
+done <<\EOF
+A B C D soft A B D
+A B C D mixed A D D
+A B C D hard D D D
+A B C D merge XXXXX
+A B C D keep XXXXX
+A B C C soft A B C
+A B C C mixed A C C
+A B C C hard C C C
+A B C C merge XXXXX
+A B C C keep A C C
+B B C D soft B B D
+B B C D mixed B D D
+B B C D hard D D D
+B B C D merge D D D
+B B C D keep XXXXX
+B B C C soft B B C
+B B C C mixed B C C
+B B C C hard C C C
+B B C C merge C C C
+B B C C keep B C C
+B C C D soft B C D
+B C C D mixed B D D
+B C C D hard D D D
+B C C D merge XXXXX
+B C C D keep XXXXX
+B C C C soft B C C
+B C C C mixed B C C
+B C C C hard C C C
+B C C C merge B C C
+B C C C keep B C C
+EOF
+
+test_expect_success 'setting up branches to test with unmerged entries' '
+ git reset --hard C &&
+ git branch branch1 &&
+ git branch branch2 &&
+ git checkout branch1 &&
+ test_commit B1 file1 &&
+ git checkout branch2 &&
+ test_commit B file1
+'
+
+while read W1 I1 H1 T opt W2 I2 H2
+do
+ test_expect_success "check: $W1 $I1 $H1 $T --$opt $W2 $I2 $H2" '
+ git reset --hard B &&
+ test_must_fail git merge branch1 &&
+ cat file1 >X_file1 &&
+ if test "$W2" != "XXXXX"
+ then
+ git reset --$opt $T &&
+ if test "$W2" = "X"
+ then
+ test_cmp file1 X_file1
+ else
+ test "$(cat file1)" = "$W2"
+ fi &&
+ git checkout-index -f -- file1 &&
+ test "$(cat file1)" = "$I2" &&
+ git checkout -f HEAD -- file1 &&
+ test "$(cat file1)" = "$H2"
+ else
+ test_must_fail git reset --$opt $T
+ fi
+ '
+done <<\EOF
+X U B C soft XXXXX
+X U B C mixed X C C
+X U B C hard C C C
+X U B C merge C C C
+X U B C keep XXXXX
+X U B B soft XXXXX
+X U B B mixed X B B
+X U B B hard B B B
+X U B B merge B B B
+X U B B keep XXXXX
+EOF
+
+test_done
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
new file mode 100755
index 0000000..1337fa5
--- /dev/null
+++ b/t/t7201-co.sh
@@ -0,0 +1,671 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git checkout tests.
+
+Creates master, forks renamer and side branches from it.
+Test switching across them.
+
+ ! [master] Initial A one, A two
+ * [renamer] Renamer R one->uno, M two
+ ! [side] Side M one, D two, A three
+ ! [simple] Simple D one, M two
+ ----
+ + [simple] Simple D one, M two
+ + [side] Side M one, D two, A three
+ * [renamer] Renamer R one->uno, M two
+ +*++ [master] Initial A one, A two
+
+'
+
+. ./test-lib.sh
+
+test_tick
+
+fill () {
+ for i
+ do
+ echo "$i"
+ done
+}
+
+
+test_expect_success setup '
+
+ fill x y z > same &&
+ fill 1 2 3 4 5 6 7 8 >one &&
+ fill a b c d e >two &&
+ git add same one two &&
+ git commit -m "Initial A one, A two" &&
+
+ git checkout -b renamer &&
+ rm -f one &&
+ fill 1 3 4 5 6 7 8 >uno &&
+ git add uno &&
+ fill a b c d e f >two &&
+ git commit -a -m "Renamer R one->uno, M two" &&
+
+ git checkout -b side master &&
+ fill 1 2 3 4 5 6 7 >one &&
+ fill A B C D E >three &&
+ rm -f two &&
+ git update-index --add --remove one two three &&
+ git commit -m "Side M one, D two, A three" &&
+
+ git checkout -b simple master &&
+ rm -f one &&
+ fill a c e > two &&
+ git commit -a -m "Simple D one, M two" &&
+
+ git checkout master
+'
+
+test_expect_success "checkout from non-existing branch" '
+
+ git checkout -b delete-me master &&
+ rm .git/refs/heads/delete-me &&
+ test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
+ git checkout master &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success "checkout with dirty tree without -m" '
+
+ fill 0 1 2 3 4 5 6 7 8 >one &&
+ if git checkout side
+ then
+ echo Not happy
+ false
+ else
+ echo "happy - failed correctly"
+ fi
+
+'
+
+test_expect_success "checkout with unrelated dirty tree without -m" '
+
+ git checkout -f master &&
+ fill 0 1 2 3 4 5 6 7 8 >same &&
+ cp same kept
+ git checkout side >messages &&
+ test_cmp same kept
+ (cat > messages.expect <<EOF
+M same
+EOF
+) &&
+ touch messages.expect &&
+ test_cmp messages.expect messages
+'
+
+test_expect_success "checkout -m with dirty tree" '
+
+ git checkout -f master &&
+ git clean -f &&
+
+ fill 0 1 2 3 4 5 6 7 8 >one &&
+ git checkout -m side > messages &&
+
+ test "$(git symbolic-ref HEAD)" = "refs/heads/side" &&
+
+ (cat >expect.messages <<EOF
+M one
+EOF
+) &&
+ test_cmp expect.messages messages &&
+
+ fill "M one" "A three" "D two" >expect.master &&
+ git diff --name-status master >current.master &&
+ test_cmp expect.master current.master &&
+
+ fill "M one" >expect.side &&
+ git diff --name-status side >current.side &&
+ test_cmp expect.side current.side &&
+
+ : >expect.index &&
+ git diff --cached >current.index &&
+ test_cmp expect.index current.index
+'
+
+test_expect_success "checkout -m with dirty tree, renamed" '
+
+ git checkout -f master && git clean -f &&
+
+ fill 1 2 3 4 5 7 8 >one &&
+ if git checkout renamer
+ then
+ echo Not happy
+ false
+ else
+ echo "happy - failed correctly"
+ fi &&
+
+ git checkout -m renamer &&
+ fill 1 3 4 5 7 8 >expect &&
+ test_cmp expect uno &&
+ ! test -f one &&
+ git diff --cached >current &&
+ ! test -s current
+
+'
+
+test_expect_success 'checkout -m with merge conflict' '
+
+ git checkout -f master && git clean -f &&
+
+ fill 1 T 3 4 5 6 S 8 >one &&
+ if git checkout renamer
+ then
+ echo Not happy
+ false
+ else
+ echo "happy - failed correctly"
+ fi &&
+
+ git checkout -m renamer &&
+
+ git diff master:one :3:uno |
+ sed -e "1,/^@@/d" -e "/^ /d" -e "s/^-/d/" -e "s/^+/a/" >current &&
+ fill d2 aT d7 aS >expect &&
+ test_cmp current expect &&
+ git diff --cached two >current &&
+ ! test -s current
+'
+
+test_expect_success 'format of merge conflict from checkout -m' '
+
+ git checkout -f master && git clean -f &&
+
+ fill b d > two &&
+ git checkout -m simple &&
+
+ git ls-files >current &&
+ fill same two two two >expect &&
+ test_cmp current expect &&
+
+ cat <<-EOF >expect &&
+ <<<<<<< simple
+ a
+ c
+ e
+ =======
+ b
+ d
+ >>>>>>> local
+ EOF
+ test_cmp two expect
+'
+
+test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
+
+ git checkout -f master && git reset --hard && git clean -f &&
+
+ fill b d > two &&
+ git checkout --merge --conflict=diff3 simple &&
+
+ cat <<-EOF >expect &&
+ <<<<<<< simple
+ a
+ c
+ e
+ ||||||| master
+ a
+ b
+ c
+ d
+ e
+ =======
+ b
+ d
+ >>>>>>> local
+ EOF
+ test_cmp two expect
+'
+
+test_expect_success 'checkout to detach HEAD (with advice declined)' '
+
+ git config advice.detachedHead false &&
+ git checkout -f renamer && git clean -f &&
+ git checkout renamer^ 2>messages &&
+ grep "HEAD is now at 7329388" messages &&
+ test 1 -eq $(wc -l <messages) &&
+ H=$(git rev-parse --verify HEAD) &&
+ M=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$H" = "z$M" &&
+ if git symbolic-ref HEAD >/dev/null 2>&1
+ then
+ echo "OOPS, HEAD is still symbolic???"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success 'checkout to detach HEAD' '
+ git config advice.detachedHead true &&
+ git checkout -f renamer && git clean -f &&
+ git checkout renamer^ 2>messages &&
+ grep "HEAD is now at 7329388" messages &&
+ test 1 -lt $(wc -l <messages) &&
+ H=$(git rev-parse --verify HEAD) &&
+ M=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$H" = "z$M" &&
+ if git symbolic-ref HEAD >/dev/null 2>&1
+ then
+ echo "OOPS, HEAD is still symbolic???"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success 'checkout to detach HEAD with branchname^' '
+
+ git checkout -f master && git clean -f &&
+ git checkout renamer^ &&
+ H=$(git rev-parse --verify HEAD) &&
+ M=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$H" = "z$M" &&
+ if git symbolic-ref HEAD >/dev/null 2>&1
+ then
+ echo "OOPS, HEAD is still symbolic???"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success 'checkout to detach HEAD with :/message' '
+
+ git checkout -f master && git clean -f &&
+ git checkout ":/Initial" &&
+ H=$(git rev-parse --verify HEAD) &&
+ M=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$H" = "z$M" &&
+ if git symbolic-ref HEAD >/dev/null 2>&1
+ then
+ echo "OOPS, HEAD is still symbolic???"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success 'checkout to detach HEAD with HEAD^0' '
+
+ git checkout -f master && git clean -f &&
+ git checkout HEAD^0 &&
+ H=$(git rev-parse --verify HEAD) &&
+ M=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$H" = "z$M" &&
+ if git symbolic-ref HEAD >/dev/null 2>&1
+ then
+ echo "OOPS, HEAD is still symbolic???"
+ false
+ else
+ : happy
+ fi
+'
+
+test_expect_success 'checkout with ambiguous tag/branch names' '
+
+ git tag both side &&
+ git branch both master &&
+ git reset --hard &&
+ git checkout master &&
+
+ git checkout both &&
+ H=$(git rev-parse --verify HEAD) &&
+ M=$(git show-ref -s --verify refs/heads/master) &&
+ test "z$H" = "z$M" &&
+ name=$(git symbolic-ref HEAD 2>/dev/null) &&
+ test "z$name" = zrefs/heads/both
+
+'
+
+test_expect_success 'checkout with ambiguous tag/branch names' '
+
+ git reset --hard &&
+ git checkout master &&
+
+ git tag frotz side &&
+ git branch frotz master &&
+ git reset --hard &&
+ git checkout master &&
+
+ git checkout tags/frotz &&
+ H=$(git rev-parse --verify HEAD) &&
+ S=$(git show-ref -s --verify refs/heads/side) &&
+ test "z$H" = "z$S" &&
+ if name=$(git symbolic-ref HEAD 2>/dev/null)
+ then
+ echo "Bad -- should have detached"
+ false
+ else
+ : happy
+ fi
+
+'
+
+test_expect_success 'switch branches while in subdirectory' '
+
+ git reset --hard &&
+ git checkout master &&
+
+ mkdir subs &&
+ (
+ cd subs &&
+ git checkout side
+ ) &&
+ ! test -f subs/one &&
+ rm -fr subs
+
+'
+
+test_expect_success 'checkout specific path while in subdirectory' '
+
+ git reset --hard &&
+ git checkout side &&
+ mkdir subs &&
+ >subs/bero &&
+ git add subs/bero &&
+ git commit -m "add subs/bero" &&
+
+ git checkout master &&
+ mkdir -p subs &&
+ (
+ cd subs &&
+ git checkout side -- bero
+ ) &&
+ test -f subs/bero
+
+'
+
+test_expect_success \
+ 'checkout w/--track sets up tracking' '
+ git config branch.autosetupmerge false &&
+ git checkout master &&
+ git checkout --track -b track1 &&
+ test "$(git config branch.track1.remote)" &&
+ test "$(git config branch.track1.merge)"'
+
+test_expect_success \
+ 'checkout w/autosetupmerge=always sets up tracking' '
+ git config branch.autosetupmerge always &&
+ git checkout master &&
+ git checkout -b track2 &&
+ test "$(git config branch.track2.remote)" &&
+ test "$(git config branch.track2.merge)"
+ git config branch.autosetupmerge false'
+
+test_expect_success 'checkout w/--track from non-branch HEAD fails' '
+ git checkout master^0 &&
+ test_must_fail git symbolic-ref HEAD &&
+ test_must_fail git checkout --track -b track &&
+ test_must_fail git rev-parse --verify track &&
+ test_must_fail git symbolic-ref HEAD &&
+ test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)"
+'
+
+test_expect_success 'detach a symbolic link HEAD' '
+ git checkout master &&
+ git config --bool core.prefersymlinkrefs yes &&
+ git checkout side &&
+ git checkout master &&
+ it=$(git symbolic-ref HEAD) &&
+ test "z$it" = zrefs/heads/master &&
+ here=$(git rev-parse --verify refs/heads/master) &&
+ git checkout side^ &&
+ test "z$(git rev-parse --verify refs/heads/master)" = "z$here"
+'
+
+test_expect_success \
+ 'checkout with --track fakes a sensible -b <name>' '
+ git update-ref refs/remotes/origin/koala/bear renamer &&
+ git update-ref refs/new/koala/bear renamer &&
+
+ git checkout --track origin/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+ git checkout master && git branch -D koala/bear &&
+
+ git checkout --track refs/remotes/origin/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+ git checkout master && git branch -D koala/bear &&
+
+ git checkout --track remotes/origin/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+ git checkout master && git branch -D koala/bear &&
+
+ git checkout --track refs/new/koala/bear &&
+ test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
+'
+
+test_expect_success \
+ 'checkout with --track, but without -b, fails with too short tracked name' '
+ test_must_fail git checkout --track renamer'
+
+setup_conflicting_index () {
+ rm -f .git/index &&
+ O=$(echo original | git hash-object -w --stdin) &&
+ A=$(echo ourside | git hash-object -w --stdin) &&
+ B=$(echo theirside | git hash-object -w --stdin) &&
+ (
+ echo "100644 $A 0 fild" &&
+ echo "100644 $O 1 file" &&
+ echo "100644 $A 2 file" &&
+ echo "100644 $B 3 file" &&
+ echo "100644 $A 0 filf"
+ ) | git update-index --index-info
+}
+
+test_expect_success 'checkout an unmerged path should fail' '
+ setup_conflicting_index &&
+ echo "none of the above" >sample &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ test_must_fail git checkout fild file filf &&
+ test_cmp sample fild &&
+ test_cmp sample filf &&
+ test_cmp sample file
+'
+
+test_expect_success 'checkout with an unmerged path can be ignored' '
+ setup_conflicting_index &&
+ echo "none of the above" >sample &&
+ echo ourside >expect &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ git checkout -f fild file filf &&
+ test_cmp expect fild &&
+ test_cmp expect filf &&
+ test_cmp sample file
+'
+
+test_expect_success 'checkout unmerged stage' '
+ setup_conflicting_index &&
+ echo "none of the above" >sample &&
+ echo ourside >expect &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ git checkout --ours . &&
+ test_cmp expect fild &&
+ test_cmp expect filf &&
+ test_cmp expect file &&
+ git checkout --theirs file &&
+ test ztheirside = "z$(cat file)"
+'
+
+test_expect_success 'checkout with --merge' '
+ setup_conflicting_index &&
+ echo "none of the above" >sample &&
+ echo ourside >expect &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ git checkout -m -- fild file filf &&
+ (
+ echo "<<<<<<< ours"
+ echo ourside
+ echo "======="
+ echo theirside
+ echo ">>>>>>> theirs"
+ ) >merged &&
+ test_cmp expect fild &&
+ test_cmp expect filf &&
+ test_cmp merged file
+'
+
+test_expect_success 'checkout with --merge, in diff3 -m style' '
+ git config merge.conflictstyle diff3 &&
+ setup_conflicting_index &&
+ echo "none of the above" >sample &&
+ echo ourside >expect &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ git checkout -m -- fild file filf &&
+ (
+ echo "<<<<<<< ours"
+ echo ourside
+ echo "||||||| base"
+ echo original
+ echo "======="
+ echo theirside
+ echo ">>>>>>> theirs"
+ ) >merged &&
+ test_cmp expect fild &&
+ test_cmp expect filf &&
+ test_cmp merged file
+'
+
+test_expect_success 'checkout --conflict=merge, overriding config' '
+ git config merge.conflictstyle diff3 &&
+ setup_conflicting_index &&
+ echo "none of the above" >sample &&
+ echo ourside >expect &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ git checkout --conflict=merge -- fild file filf &&
+ (
+ echo "<<<<<<< ours"
+ echo ourside
+ echo "======="
+ echo theirside
+ echo ">>>>>>> theirs"
+ ) >merged &&
+ test_cmp expect fild &&
+ test_cmp expect filf &&
+ test_cmp merged file
+'
+
+test_expect_success 'checkout --conflict=diff3' '
+ git config --unset merge.conflictstyle
+ setup_conflicting_index &&
+ echo "none of the above" >sample &&
+ echo ourside >expect &&
+ cat sample >fild &&
+ cat sample >file &&
+ cat sample >filf &&
+ git checkout --conflict=diff3 -- fild file filf &&
+ (
+ echo "<<<<<<< ours"
+ echo ourside
+ echo "||||||| base"
+ echo original
+ echo "======="
+ echo theirside
+ echo ">>>>>>> theirs"
+ ) >merged &&
+ test_cmp expect fild &&
+ test_cmp expect filf &&
+ test_cmp merged file
+'
+
+test_expect_success 'failing checkout -b should not break working tree' '
+ git reset --hard master &&
+ git symbolic-ref HEAD refs/heads/master &&
+ test_must_fail git checkout -b renamer side^ &&
+ test $(git symbolic-ref HEAD) = refs/heads/master &&
+ git diff --exit-code &&
+ git diff --cached --exit-code
+
+'
+
+test_expect_success 'switch out of non-branch' '
+ git reset --hard master &&
+ git checkout master^0 &&
+ echo modified >one &&
+ test_must_fail git checkout renamer 2>error.log &&
+ ! grep "^Previous HEAD" error.log
+'
+
+(
+ echo "#!$SHELL_PATH"
+ cat <<\EOF
+O=$1 A=$2 B=$3
+cat "$A" >.tmp
+exec >"$A"
+echo '<<<<<<< filfre-theirs'
+cat "$B"
+echo '||||||| filfre-common'
+cat "$O"
+echo '======='
+cat ".tmp"
+echo '>>>>>>> filfre-ours'
+rm -f .tmp
+exit 1
+EOF
+) >filfre.sh
+chmod +x filfre.sh
+
+test_expect_success 'custom merge driver with checkout -m' '
+ git reset --hard &&
+
+ git config merge.filfre.driver "./filfre.sh %O %A %B" &&
+ git config merge.filfre.name "Feel-free merge driver" &&
+ git config merge.filfre.recursive binary &&
+ echo "arm merge=filfre" >.gitattributes &&
+
+ git checkout -b left &&
+ echo neutral >arm &&
+ git add arm .gitattributes &&
+ test_tick &&
+ git commit -m neutral &&
+ git branch right &&
+
+ echo left >arm &&
+ test_tick &&
+ git commit -a -m left &&
+ git checkout right &&
+
+ echo right >arm &&
+ test_tick &&
+ git commit -a -m right &&
+
+ test_must_fail git merge left &&
+ (
+ for t in filfre-common left right
+ do
+ grep $t arm || exit 1
+ done
+ exit 0
+ ) &&
+
+ mv arm expect &&
+ git checkout -m arm &&
+ test_cmp expect arm
+'
+
+test_done
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
new file mode 100755
index 0000000..7d8ed68
--- /dev/null
+++ b/t/t7300-clean.sh
@@ -0,0 +1,441 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Michael Spang
+#
+
+test_description='git clean basic tests'
+
+. ./test-lib.sh
+
+git config clean.requireForce no
+
+test_expect_success 'setup' '
+
+ mkdir -p src &&
+ touch src/part1.c Makefile &&
+ echo build >.gitignore &&
+ echo \*.o >>.gitignore &&
+ git add . &&
+ git commit -m setup &&
+ touch src/part2.c README &&
+ git add .
+
+'
+
+test_expect_success 'git clean with skip-worktree .gitignore' '
+ git update-index --skip-worktree .gitignore &&
+ rm .gitignore &&
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so &&
+ git update-index --no-skip-worktree .gitignore &&
+ git checkout .gitignore
+'
+
+test_expect_success 'git clean' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean src/' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean src/ &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean src/ src/' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean src/ src/ &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean with prefix' '
+
+ mkdir -p build docs src/test &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so src/test/1.c &&
+ (cd src/ && git clean) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test -f src/test/1.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean with relative prefix' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ would_clean=$(
+ cd docs &&
+ git clean -n ../src |
+ sed -n -e "s|^Would remove ||p"
+ ) &&
+ test "$would_clean" = ../src/part3.c || {
+ echo "OOps <$would_clean>"
+ false
+ }
+'
+
+test_expect_success 'git clean with absolute path' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ would_clean=$(
+ cd docs &&
+ git clean -n "$(pwd)/../src" |
+ sed -n -e "s|^Would remove ||p"
+ ) &&
+ test "$would_clean" = ../src/part3.c || {
+ echo "OOps <$would_clean>"
+ false
+ }
+'
+
+test_expect_success 'git clean with out of work tree relative path' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ (
+ cd docs &&
+ test_must_fail git clean -n ../..
+ )
+'
+
+test_expect_success 'git clean with out of work tree absolute path' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ dd=$(cd .. && pwd) &&
+ (
+ cd docs &&
+ test_must_fail git clean -n $dd
+ )
+'
+
+test_expect_success 'git clean -d with prefix and path' '
+
+ mkdir -p build docs src/feature &&
+ touch a.out src/part3.c src/feature/file.c docs/manual.txt obj.o build/lib.so &&
+ (cd src/ && git clean -d feature/) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f src/part3.c &&
+ test ! -f src/feature/file.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean symbolic link' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ ln -s docs/manual.txt src/part4.c
+ git clean &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -f src/part4.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean with wildcard' '
+
+ touch a.clean b.clean other.c &&
+ git clean "*.clean" &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.clean &&
+ test ! -f b.clean &&
+ test -f other.c
+
+'
+
+test_expect_success 'git clean -n' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean -n &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -d' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean -d &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -d docs &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -d src/ examples/' '
+
+ mkdir -p build docs examples &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so examples/1.c &&
+ git clean -d src/ examples/ &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -f examples/1.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -x' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean -x &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test ! -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -d -x' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean -d -x &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -d docs &&
+ test ! -f obj.o &&
+ test ! -d build
+
+'
+
+test_expect_success 'git clean -X' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean -X &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test ! -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -d -X' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean -d -X &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test ! -f obj.o &&
+ test ! -d build
+
+'
+
+test_expect_success 'clean.requireForce defaults to true' '
+
+ git config --unset clean.requireForce &&
+ test_must_fail git clean
+
+'
+
+test_expect_success 'clean.requireForce' '
+
+ git config clean.requireForce true &&
+ test_must_fail git clean
+
+'
+
+test_expect_success 'clean.requireForce and -n' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+ git clean -n &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'clean.requireForce and -f' '
+
+ git clean -f &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'core.excludesfile' '
+
+ echo excludes >excludes &&
+ echo included >included &&
+ git config core.excludesfile excludes &&
+ output=$(git clean -n excludes included 2>&1) &&
+ expr "$output" : ".*included" >/dev/null &&
+ ! expr "$output" : ".*excludes" >/dev/null
+
+'
+
+test_expect_success 'removal failure' '
+
+ mkdir foo &&
+ touch foo/bar &&
+ (exec <foo/bar &&
+ chmod 0 foo &&
+ test_must_fail git clean -f -d)
+
+'
+chmod 755 foo
+
+test_expect_success 'nested git work tree' '
+ rm -fr foo bar &&
+ mkdir foo bar &&
+ (
+ cd foo &&
+ git init &&
+ >hello.world
+ git add . &&
+ git commit -a -m nested
+ ) &&
+ (
+ cd bar &&
+ >goodbye.people
+ ) &&
+ git clean -f -d &&
+ test -f foo/.git/index &&
+ test -f foo/hello.world &&
+ ! test -d bar
+'
+
+test_expect_success 'force removal of nested git work tree' '
+ rm -fr foo bar &&
+ mkdir foo bar &&
+ (
+ cd foo &&
+ git init &&
+ >hello.world
+ git add . &&
+ git commit -a -m nested
+ ) &&
+ (
+ cd bar &&
+ >goodbye.people
+ ) &&
+ git clean -f -f -d &&
+ ! test -d foo &&
+ ! test -d bar
+'
+
+test_done
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
new file mode 100755
index 0000000..9bda970
--- /dev/null
+++ b/t/t7400-submodule-basic.sh
@@ -0,0 +1,430 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Lars Hjemli
+#
+
+test_description='Basic porcelain support for submodules
+
+This test tries to verify basic sanity of the init, update and status
+subcommands of git submodule.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup - initial commit' '
+ >t &&
+ git add t &&
+ git commit -m "initial commit" &&
+ git branch initial
+'
+
+test_expect_success 'setup - repository in init subdirectory' '
+ mkdir init &&
+ (
+ cd init &&
+ git init &&
+ echo a >a &&
+ git add a &&
+ git commit -m "submodule commit 1" &&
+ git tag -a -m "rev-1" rev-1
+ )
+'
+
+test_expect_success 'setup - commit with gitlink' '
+ echo a >a &&
+ echo z >z &&
+ git add a init z &&
+ git commit -m "super commit 1"
+'
+
+test_expect_success 'setup - hide init subdirectory' '
+ mv init .subrepo
+'
+
+test_expect_success 'setup - repository to add submodules to' '
+ git init addtest &&
+ git init addtest-ignore
+'
+
+# The 'submodule add' tests need some repository to add as a submodule.
+# The trash directory is a good one as any.
+submodurl=$TRASH_DIRECTORY
+
+listbranches() {
+ git for-each-ref --format='%(refname)' 'refs/heads/*'
+}
+
+inspect() {
+ dir=$1 &&
+ dotdot="${2:-..}" &&
+
+ (
+ cd "$dir" &&
+ listbranches >"$dotdot/heads" &&
+ { git symbolic-ref HEAD || :; } >"$dotdot/head" &&
+ git rev-parse HEAD >"$dotdot/head-sha1" &&
+ git update-index --refresh &&
+ git diff-files --exit-code &&
+ git clean -n -d -x >"$dotdot/untracked"
+ )
+}
+
+test_expect_success 'submodule add' '
+ echo "refs/heads/master" >expect &&
+ >empty &&
+
+ (
+ cd addtest &&
+ git submodule add "$submodurl" submod &&
+ git submodule init
+ ) &&
+
+ rm -f heads head untracked &&
+ inspect addtest/submod ../.. &&
+ test_cmp expect heads &&
+ test_cmp expect head &&
+ test_cmp empty untracked
+'
+
+test_expect_success 'submodule add to .gitignored path fails' '
+ (
+ cd addtest-ignore &&
+ cat <<-\EOF >expect &&
+ The following path is ignored by one of your .gitignore files:
+ submod
+ Use -f if you really want to add it.
+ EOF
+ # Does not use test_commit due to the ignore
+ echo "*" > .gitignore &&
+ git add --force .gitignore &&
+ git commit -m"Ignore everything" &&
+ ! git submodule add "$submodurl" submod >actual 2>&1 &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'submodule add to .gitignored path with --force' '
+ (
+ cd addtest-ignore &&
+ git submodule add --force "$submodurl" submod
+ )
+'
+
+test_expect_success 'submodule add --branch' '
+ echo "refs/heads/initial" >expect-head &&
+ cat <<-\EOF >expect-heads &&
+ refs/heads/initial
+ refs/heads/master
+ EOF
+ >empty &&
+
+ (
+ cd addtest &&
+ git submodule add -b initial "$submodurl" submod-branch &&
+ git submodule init
+ ) &&
+
+ rm -f heads head untracked &&
+ inspect addtest/submod-branch ../.. &&
+ test_cmp expect-heads heads &&
+ test_cmp expect-head head &&
+ test_cmp empty untracked
+'
+
+test_expect_success 'submodule add with ./ in path' '
+ echo "refs/heads/master" >expect &&
+ >empty &&
+
+ (
+ cd addtest &&
+ git submodule add "$submodurl" ././dotsubmod/./frotz/./ &&
+ git submodule init
+ ) &&
+
+ rm -f heads head untracked &&
+ inspect addtest/dotsubmod/frotz ../../.. &&
+ test_cmp expect heads &&
+ test_cmp expect head &&
+ test_cmp empty untracked
+'
+
+test_expect_success 'submodule add with // in path' '
+ echo "refs/heads/master" >expect &&
+ >empty &&
+
+ (
+ cd addtest &&
+ git submodule add "$submodurl" slashslashsubmod///frotz// &&
+ git submodule init
+ ) &&
+
+ rm -f heads head untracked &&
+ inspect addtest/slashslashsubmod/frotz ../../.. &&
+ test_cmp expect heads &&
+ test_cmp expect head &&
+ test_cmp empty untracked
+'
+
+test_expect_success 'submodule add with /.. in path' '
+ echo "refs/heads/master" >expect &&
+ >empty &&
+
+ (
+ cd addtest &&
+ git submodule add "$submodurl" dotdotsubmod/../realsubmod/frotz/.. &&
+ git submodule init
+ ) &&
+
+ rm -f heads head untracked &&
+ inspect addtest/realsubmod ../.. &&
+ test_cmp expect heads &&
+ test_cmp expect head &&
+ test_cmp empty untracked
+'
+
+test_expect_success 'submodule add with ./, /.. and // in path' '
+ echo "refs/heads/master" >expect &&
+ >empty &&
+
+ (
+ cd addtest &&
+ git submodule add "$submodurl" dot/dotslashsubmod/./../..////realsubmod2/a/b/c/d/../../../../frotz//.. &&
+ git submodule init
+ ) &&
+
+ rm -f heads head untracked &&
+ inspect addtest/realsubmod2 ../.. &&
+ test_cmp expect heads &&
+ test_cmp expect head &&
+ test_cmp empty untracked
+'
+
+test_expect_success 'setup - add an example entry to .gitmodules' '
+ GIT_CONFIG=.gitmodules \
+ git config submodule.example.url git://example.com/init.git
+'
+
+test_expect_success 'status should fail for unmapped paths' '
+ test_must_fail git submodule status
+'
+
+test_expect_success 'setup - map path in .gitmodules' '
+ cat <<\EOF >expect &&
+[submodule "example"]
+ url = git://example.com/init.git
+ path = init
+EOF
+
+ GIT_CONFIG=.gitmodules git config submodule.example.path init &&
+
+ test_cmp expect .gitmodules
+'
+
+test_expect_success 'status should only print one line' '
+ git submodule status >lines &&
+ test $(wc -l <lines) = 1
+'
+
+test_expect_success 'setup - fetch commit name from submodule' '
+ rev1=$(cd .subrepo && git rev-parse HEAD) &&
+ printf "rev1: %s\n" "$rev1" &&
+ test -n "$rev1"
+'
+
+test_expect_success 'status should initially be "missing"' '
+ git submodule status >lines &&
+ grep "^-$rev1" lines
+'
+
+test_expect_success 'init should register submodule url in .git/config' '
+ echo git://example.com/init.git >expect &&
+
+ git submodule init &&
+ git config submodule.example.url >url &&
+ git config submodule.example.url ./.subrepo &&
+
+ test_cmp expect url
+'
+
+test_expect_success 'update should fail when path is used by a file' '
+ echo hello >expect &&
+
+ echo "hello" >init &&
+ test_must_fail git submodule update &&
+
+ test_cmp expect init
+'
+
+test_expect_success 'update should fail when path is used by a nonempty directory' '
+ echo hello >expect &&
+
+ rm -fr init &&
+ mkdir init &&
+ echo "hello" >init/a &&
+
+ test_must_fail git submodule update &&
+
+ test_cmp expect init/a
+'
+
+test_expect_success 'update should work when path is an empty dir' '
+ rm -fr init &&
+ rm -f head-sha1 &&
+ echo "$rev1" >expect &&
+
+ mkdir init &&
+ git submodule update &&
+
+ inspect init &&
+ test_cmp expect head-sha1
+'
+
+test_expect_success 'status should be "up-to-date" after update' '
+ git submodule status >list &&
+ grep "^ $rev1" list
+'
+
+test_expect_success 'status should be "modified" after submodule commit' '
+ (
+ cd init &&
+ echo b >b &&
+ git add b &&
+ git commit -m "submodule commit 2"
+ ) &&
+
+ rev2=$(cd init && git rev-parse HEAD) &&
+ test -n "$rev2" &&
+ git submodule status >list &&
+
+ grep "^+$rev2" list
+'
+
+test_expect_success 'the --cached sha1 should be rev1' '
+ git submodule --cached status >list &&
+ grep "^+$rev1" list
+'
+
+test_expect_success 'git diff should report the SHA1 of the new submodule commit' '
+ git diff >diff &&
+ grep "^+Subproject commit $rev2" diff
+'
+
+test_expect_success 'update should checkout rev1' '
+ rm -f head-sha1 &&
+ echo "$rev1" >expect &&
+
+ git submodule update init &&
+ inspect init &&
+
+ test_cmp expect head-sha1
+'
+
+test_expect_success 'status should be "up-to-date" after update' '
+ git submodule status >list &&
+ grep "^ $rev1" list
+'
+
+test_expect_success 'checkout superproject with subproject already present' '
+ git checkout initial &&
+ git checkout master
+'
+
+test_expect_success 'apply submodule diff' '
+ >empty &&
+
+ git branch second &&
+ (
+ cd init &&
+ echo s >s &&
+ git add s &&
+ git commit -m "change subproject"
+ ) &&
+ git update-index --add init &&
+ git commit -m "change init" &&
+ git format-patch -1 --stdout >P.diff &&
+ git checkout second &&
+ git apply --index P.diff &&
+
+ git diff --cached master >staged &&
+ test_cmp empty staged
+'
+
+test_expect_success 'update --init' '
+ mv init init2 &&
+ git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
+ git config --remove-section submodule.example &&
+ test_must_fail git config submodule.example.url &&
+
+ git submodule update init > update.out &&
+ cat update.out &&
+ grep "not initialized" update.out &&
+ ! test -d init/.git &&
+
+ git submodule update --init init &&
+ test -d init/.git
+'
+
+test_expect_success 'do not add files from a submodule' '
+
+ git reset --hard &&
+ test_must_fail git add init/a
+
+'
+
+test_expect_success 'gracefully add submodule with a trailing slash' '
+
+ git reset --hard &&
+ git commit -m "commit subproject" init &&
+ (cd init &&
+ echo b > a) &&
+ git add init/ &&
+ git diff --exit-code --cached init &&
+ commit=$(cd init &&
+ git commit -m update a >/dev/null &&
+ git rev-parse HEAD) &&
+ git add init/ &&
+ test_must_fail git diff --exit-code --cached init &&
+ test $commit = $(git ls-files --stage |
+ sed -n "s/^160000 \([^ ]*\).*/\1/p")
+
+'
+
+test_expect_success 'ls-files gracefully handles trailing slash' '
+
+ test "init" = "$(git ls-files init/)"
+
+'
+
+test_expect_success 'moving to a commit without submodule does not leave empty dir' '
+ rm -rf init &&
+ mkdir init &&
+ git reset --hard &&
+ git checkout initial &&
+ test ! -d init &&
+ git checkout second
+'
+
+test_expect_success 'submodule <invalid-path> warns' '
+
+ git submodule no-such-submodule 2> output.err &&
+ grep "^error: .*no-such-submodule" output.err
+
+'
+
+test_expect_success 'add submodules without specifying an explicit path' '
+ mkdir repo &&
+ cd repo &&
+ git init &&
+ echo r >r &&
+ git add r &&
+ git commit -m "repo commit 1" &&
+ cd .. &&
+ git clone --bare repo/ bare.git &&
+ cd addtest &&
+ git submodule add "$submodurl/repo" &&
+ git config -f .gitmodules submodule.repo.path repo &&
+ git submodule add "$submodurl/bare.git" &&
+ git config -f .gitmodules submodule.bare.path bare
+'
+
+test_done
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
new file mode 100755
index 0000000..cee319d
--- /dev/null
+++ b/t/t7401-submodule-summary.sh
@@ -0,0 +1,237 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Ping Yin
+#
+
+test_description='Summary support for submodules
+
+This test tries to verify the sanity of summary subcommand of git submodule.
+'
+
+. ./test-lib.sh
+
+add_file () {
+ sm=$1
+ shift
+ owd=$(pwd)
+ cd "$sm"
+ for name; do
+ echo "$name" > "$name" &&
+ git add "$name" &&
+ test_tick &&
+ git commit -m "Add $name"
+ done >/dev/null
+ git rev-parse --verify HEAD | cut -c1-7
+ cd "$owd"
+}
+commit_file () {
+ test_tick &&
+ git commit "$@" -m "Commit $*" >/dev/null
+}
+
+test_create_repo sm1 &&
+add_file . foo >/dev/null
+
+head1=$(add_file sm1 foo1 foo2)
+
+test_expect_success 'added submodule' "
+ git add sm1 &&
+ git submodule summary >actual &&
+ diff actual - <<-EOF
+* sm1 0000000...$head1 (2):
+ > Add foo2
+
+EOF
+"
+
+commit_file sm1 &&
+head2=$(add_file sm1 foo3)
+
+test_expect_success 'modified submodule(forward)' "
+ git submodule summary >actual &&
+ diff actual - <<-EOF
+* sm1 $head1...$head2 (1):
+ > Add foo3
+
+EOF
+"
+
+test_expect_success 'modified submodule(forward), --files' "
+ git submodule summary --files >actual &&
+ diff actual - <<-EOF
+* sm1 $head1...$head2 (1):
+ > Add foo3
+
+EOF
+"
+
+commit_file sm1 &&
+cd sm1 &&
+git reset --hard HEAD~2 >/dev/null &&
+head3=$(git rev-parse --verify HEAD | cut -c1-7) &&
+cd ..
+
+test_expect_success 'modified submodule(backward)' "
+ git submodule summary >actual &&
+ diff actual - <<-EOF
+* sm1 $head2...$head3 (2):
+ < Add foo3
+ < Add foo2
+
+EOF
+"
+
+head4=$(add_file sm1 foo4 foo5) &&
+head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
+test_expect_success 'modified submodule(backward and forward)' "
+ git submodule summary >actual &&
+ diff actual - <<-EOF
+* sm1 $head2...$head4 (4):
+ > Add foo5
+ > Add foo4
+ < Add foo3
+ < Add foo2
+
+EOF
+"
+
+test_expect_success '--summary-limit' "
+ git submodule summary -n 3 >actual &&
+ diff actual - <<-EOF
+* sm1 $head2...$head4 (4):
+ > Add foo5
+ > Add foo4
+ < Add foo3
+
+EOF
+"
+
+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 submodule summary --cached >actual &&
+ diff actual - <<-EOF
+* sm1 $head4(submodule)->$head5(blob) (3):
+ < Add foo5
+
+EOF
+"
+
+test_expect_success 'typechanged submodule(submodule->blob), --files' "
+ git submodule summary --files >actual &&
+ diff actual - <<-EOF
+* sm1 $head5(blob)->$head4(submodule) (3):
+ > Add foo5
+
+EOF
+"
+
+rm -rf sm1 &&
+git checkout-index sm1
+test_expect_success 'typechanged submodule(submodule->blob)' "
+ git submodule summary >actual &&
+ diff actual - <<-EOF
+* sm1 $head4(submodule)->$head5(blob):
+
+EOF
+"
+
+rm -f sm1 &&
+test_create_repo sm1 &&
+head6=$(add_file sm1 foo6 foo7)
+test_expect_success 'nonexistent commit' "
+ git submodule summary >actual &&
+ diff actual - <<-EOF
+* sm1 $head4...$head6:
+ Warn: sm1 doesn't contain commit $head4_full
+
+EOF
+"
+
+commit_file
+test_expect_success 'typechanged submodule(blob->submodule)' "
+ git submodule summary >actual &&
+ diff actual - <<-EOF
+* sm1 $head5(blob)->$head6(submodule) (2):
+ > Add foo7
+
+EOF
+"
+
+commit_file sm1 &&
+rm -rf sm1
+test_expect_success 'deleted submodule' "
+ git submodule summary >actual &&
+ diff actual - <<-EOF
+* sm1 $head6...0000000:
+
+EOF
+"
+
+test_create_repo sm2 &&
+head7=$(add_file sm2 foo8 foo9) &&
+git add sm2
+
+test_expect_success 'multiple submodules' "
+ git submodule summary >actual &&
+ diff actual - <<-EOF
+* sm1 $head6...0000000:
+
+* sm2 0000000...$head7 (2):
+ > Add foo9
+
+EOF
+"
+
+test_expect_success 'path filter' "
+ git submodule summary sm2 >actual &&
+ diff actual - <<-EOF
+* sm2 0000000...$head7 (2):
+ > Add foo9
+
+EOF
+"
+
+commit_file sm2
+test_expect_success 'given commit' "
+ git submodule summary HEAD^ >actual &&
+ diff actual - <<-EOF
+* sm1 $head6...0000000:
+
+* sm2 0000000...$head7 (2):
+ > Add foo9
+
+EOF
+"
+
+test_expect_success '--for-status' "
+ git submodule summary --for-status HEAD^ >actual &&
+ test_cmp actual - <<EOF
+# Submodule changes to be committed:
+#
+# * sm1 $head6...0000000:
+#
+# * sm2 0000000...$head7 (2):
+# > Add foo9
+#
+EOF
+"
+
+test_expect_success 'fail when using --files together with --cached' "
+ test_must_fail git submodule summary --files --cached
+"
+
+test_expect_success 'should not fail in an empty repo' "
+ git init xyzzy &&
+ cd xyzzy &&
+ git submodule summary >output 2>&1 &&
+ test_cmp output /dev/null
+"
+
+test_done
diff --git a/t/t7402-submodule-rebase.sh b/t/t7402-submodule-rebase.sh
new file mode 100755
index 0000000..f919c8d
--- /dev/null
+++ b/t/t7402-submodule-rebase.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Johannes Schindelin
+#
+
+test_description='Test rebasing and stashing with dirty submodules'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ echo file > file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git clone . submodule &&
+ git add submodule &&
+ test_tick &&
+ git commit -m submodule &&
+ echo second line >> file &&
+ (cd submodule && git pull) &&
+ test_tick &&
+ git commit -m file-and-submodule -a
+
+'
+
+test_expect_success 'rebase with a dirty submodule' '
+
+ (cd submodule &&
+ echo 3rd line >> file &&
+ test_tick &&
+ git commit -m fork -a) &&
+ echo unrelated >> file2 &&
+ git add file2 &&
+ test_tick &&
+ git commit -m unrelated file2 &&
+ echo other line >> file &&
+ test_tick &&
+ git commit -m update file &&
+ CURRENT=$(cd submodule && git rev-parse HEAD) &&
+ EXPECTED=$(git rev-parse HEAD~2:submodule) &&
+ GIT_TRACE=1 git rebase --onto HEAD~2 HEAD^ &&
+ STORED=$(git rev-parse HEAD:submodule) &&
+ test $EXPECTED = $STORED &&
+ test $CURRENT = $(cd submodule && git rev-parse HEAD)
+
+'
+
+cat > fake-editor.sh << \EOF
+#!/bin/sh
+echo $EDITOR_TEXT
+EOF
+chmod a+x fake-editor.sh
+
+test_expect_success 'interactive rebase with a dirty submodule' '
+
+ test submodule = $(git diff --name-only) &&
+ HEAD=$(git rev-parse HEAD) &&
+ GIT_EDITOR="\"$(pwd)/fake-editor.sh\"" EDITOR_TEXT="pick $HEAD" \
+ git rebase -i HEAD^ &&
+ test submodule = $(git diff --name-only)
+
+'
+
+test_expect_success 'rebase with dirty file and submodule fails' '
+
+ echo yet another line >> file &&
+ test_tick &&
+ git commit -m next file &&
+ echo rewrite > file &&
+ test_tick &&
+ git commit -m rewrite file &&
+ echo dirty > file &&
+ test_must_fail git rebase --onto HEAD~2 HEAD^
+
+'
+
+test_expect_success 'stash with a dirty submodule' '
+
+ echo new > file &&
+ CURRENT=$(cd submodule && git rev-parse HEAD) &&
+ git stash &&
+ test new != $(cat file) &&
+ test submodule = $(git diff --name-only) &&
+ test $CURRENT = $(cd submodule && git rev-parse HEAD) &&
+ git stash apply &&
+ test new = $(cat file) &&
+ test $CURRENT = $(cd submodule && git rev-parse HEAD)
+
+'
+
+test_done
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
new file mode 100755
index 0000000..7538756
--- /dev/null
+++ b/t/t7403-submodule-sync.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 David Aguilar
+#
+
+test_description='git submodule sync
+
+These tests exercise the "git submodule sync" subcommand.
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo file > file &&
+ git add file &&
+ test_tick &&
+ git commit -m upstream
+ git clone . super &&
+ git clone super submodule &&
+ (cd super &&
+ git submodule add ../submodule submodule &&
+ test_tick &&
+ git commit -m "submodule"
+ ) &&
+ git clone super super-clone &&
+ (cd super-clone && git submodule update --init)
+'
+
+test_expect_success 'change submodule' '
+ (cd submodule &&
+ echo second line >> file &&
+ test_tick &&
+ git commit -a -m "change submodule"
+ )
+'
+
+test_expect_success 'change submodule url' '
+ (cd super &&
+ cd submodule &&
+ git checkout master &&
+ git pull
+ ) &&
+ mv submodule moved-submodule &&
+ (cd super &&
+ git config -f .gitmodules submodule.submodule.url ../moved-submodule
+ test_tick &&
+ git commit -a -m moved-submodule
+ )
+'
+
+test_expect_success '"git submodule sync" should update submodule URLs' '
+ (cd super-clone &&
+ git pull &&
+ git submodule sync
+ ) &&
+ test -d "$(git config -f super-clone/submodule/.git/config \
+ remote.origin.url)" &&
+ (cd super-clone/submodule &&
+ git checkout master &&
+ git pull
+ )
+'
+
+test_done
diff --git a/t/t7405-submodule-merge.sh b/t/t7405-submodule-merge.sh
new file mode 100755
index 0000000..4a7b893
--- /dev/null
+++ b/t/t7405-submodule-merge.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+test_description='merging with submodules'
+
+. ./test-lib.sh
+
+#
+# history
+#
+# a --- c
+# / \ /
+# root X
+# \ / \
+# b --- d
+#
+
+test_expect_success setup '
+
+ mkdir sub &&
+ (cd sub &&
+ git init &&
+ echo original > file &&
+ git add file &&
+ test_tick &&
+ git commit -m sub-root) &&
+ git add sub &&
+ test_tick &&
+ git commit -m root &&
+
+ git checkout -b a master &&
+ (cd sub &&
+ echo A > file &&
+ git add file &&
+ test_tick &&
+ git commit -m sub-a) &&
+ git add sub &&
+ test_tick &&
+ git commit -m a &&
+
+ git checkout -b b master &&
+ (cd sub &&
+ echo B > file &&
+ git add file &&
+ test_tick &&
+ git commit -m sub-b) &&
+ git add sub &&
+ test_tick &&
+ git commit -m b &&
+
+ git checkout -b c a &&
+ git merge -s ours b &&
+
+ git checkout -b d b &&
+ git merge -s ours a
+'
+
+test_expect_success 'merging with modify/modify conflict' '
+
+ git checkout -b test1 a &&
+ test_must_fail git merge b &&
+ test -f .git/MERGE_MSG &&
+ git diff &&
+ test -n "$(git ls-files -u)"
+'
+
+test_expect_success 'merging with a modify/modify conflict between merge bases' '
+
+ git reset --hard HEAD &&
+ git checkout -b test2 c &&
+ git merge d
+
+'
+
+test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
new file mode 100755
index 0000000..1382a8e
--- /dev/null
+++ b/t/t7406-submodule-update.sh
@@ -0,0 +1,206 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Red Hat, Inc.
+#
+
+test_description='Test updating submodules
+
+This test verifies that "git submodule update" detaches the HEAD of the
+submodule and "git submodule update --rebase/--merge" does not detach the HEAD.
+'
+
+. ./test-lib.sh
+
+
+compare_head()
+{
+ sha_master=`git rev-list --max-count=1 master`
+ sha_head=`git rev-list --max-count=1 HEAD`
+
+ test "$sha_master" = "$sha_head"
+}
+
+
+test_expect_success 'setup a submodule tree' '
+ echo file > file &&
+ git add file &&
+ test_tick &&
+ git commit -m upstream
+ git clone . super &&
+ git clone super submodule &&
+ git clone super rebasing &&
+ git clone super merging &&
+ (cd super &&
+ git submodule add ../submodule submodule &&
+ test_tick &&
+ git commit -m "submodule" &&
+ git submodule init submodule
+ ) &&
+ (cd submodule &&
+ echo "line2" > file &&
+ git add file &&
+ git commit -m "Commit 2"
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ git pull --rebase origin
+ ) &&
+ git add submodule &&
+ git commit -m "submodule update"
+ ) &&
+ (cd super &&
+ git submodule add ../rebasing rebasing &&
+ test_tick &&
+ git commit -m "rebasing"
+ ) &&
+ (cd super &&
+ git submodule add ../merging merging &&
+ test_tick &&
+ git commit -m "rebasing"
+ )
+'
+
+test_expect_success 'submodule update detaching the HEAD ' '
+ (cd super/submodule &&
+ git reset --hard HEAD~1
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update submodule &&
+ cd submodule &&
+ ! compare_head
+ )
+'
+
+test_expect_success 'submodule update --rebase staying on master' '
+ (cd super/submodule &&
+ git checkout master
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update --rebase submodule &&
+ cd submodule &&
+ compare_head
+ )
+'
+
+test_expect_success 'submodule update --merge staying on master' '
+ (cd super/submodule &&
+ git reset --hard HEAD~1
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update --merge submodule &&
+ cd submodule &&
+ compare_head
+ )
+'
+
+test_expect_success 'submodule update - rebase in .git/config' '
+ (cd super &&
+ git config submodule.submodule.update rebase
+ ) &&
+ (cd super/submodule &&
+ git reset --hard HEAD~1
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update submodule &&
+ cd submodule &&
+ compare_head
+ )
+'
+
+test_expect_success 'submodule update - checkout in .git/config but --rebase given' '
+ (cd super &&
+ git config submodule.submodule.update checkout
+ ) &&
+ (cd super/submodule &&
+ git reset --hard HEAD~1
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update --rebase submodule &&
+ cd submodule &&
+ compare_head
+ )
+'
+
+test_expect_success 'submodule update - merge in .git/config' '
+ (cd super &&
+ git config submodule.submodule.update merge
+ ) &&
+ (cd super/submodule &&
+ git reset --hard HEAD~1
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update submodule &&
+ cd submodule &&
+ compare_head
+ )
+'
+
+test_expect_success 'submodule update - checkout in .git/config but --merge given' '
+ (cd super &&
+ git config submodule.submodule.update checkout
+ ) &&
+ (cd super/submodule &&
+ git reset --hard HEAD~1
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update --merge submodule &&
+ cd submodule &&
+ compare_head
+ )
+'
+
+test_expect_success 'submodule update - checkout in .git/config' '
+ (cd super &&
+ git config submodule.submodule.update checkout
+ ) &&
+ (cd super/submodule &&
+ git reset --hard HEAD^
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update submodule &&
+ cd submodule &&
+ ! compare_head
+ )
+'
+
+test_expect_success 'submodule init picks up rebase' '
+ (cd super &&
+ git config -f .gitmodules submodule.rebasing.update rebase &&
+ git submodule init rebasing &&
+ test "rebase" = "$(git config submodule.rebasing.update)"
+ )
+'
+
+test_expect_success 'submodule init picks up merge' '
+ (cd super &&
+ git config -f .gitmodules submodule.merging.update merge &&
+ git submodule init merging &&
+ test "merge" = "$(git config submodule.merging.update)"
+ )
+'
+
+test_done
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
new file mode 100755
index 0000000..db9365b
--- /dev/null
+++ b/t/t7407-submodule-foreach.sh
@@ -0,0 +1,241 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Johan Herland
+#
+
+test_description='Test "git submodule foreach"
+
+This test verifies that "git submodule foreach" correctly visits all submodules
+that are currently checked out.
+'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup a submodule tree' '
+ echo file > file &&
+ git add file &&
+ test_tick &&
+ git commit -m upstream
+ git clone . super &&
+ git clone super submodule &&
+ (
+ cd super &&
+ git submodule add ../submodule sub1 &&
+ git submodule add ../submodule sub2 &&
+ git submodule add ../submodule sub3 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub1 submodule.foo1 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub2 submodule.foo2 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub3 submodule.foo3 &&
+ git add .gitmodules
+ test_tick &&
+ git commit -m "submodules" &&
+ git submodule init sub1 &&
+ git submodule init sub2 &&
+ git submodule init sub3
+ ) &&
+ (
+ cd submodule &&
+ echo different > file &&
+ git add file &&
+ test_tick &&
+ git commit -m "different"
+ ) &&
+ (
+ cd super &&
+ (
+ cd sub3 &&
+ git pull
+ ) &&
+ git add sub3 &&
+ test_tick &&
+ git commit -m "update sub3"
+ )
+'
+
+sub1sha1=$(cd super/sub1 && git rev-parse HEAD)
+sub3sha1=$(cd super/sub3 && git rev-parse HEAD)
+
+pwd=$(pwd)
+
+cat > expect <<EOF
+Entering 'sub1'
+$pwd/clone-foo1-sub1-$sub1sha1
+Entering 'sub3'
+$pwd/clone-foo3-sub3-$sub3sha1
+EOF
+
+test_expect_success 'test basic "submodule foreach" usage' '
+ git clone super clone &&
+ (
+ cd clone &&
+ git submodule update --init -- sub1 sub3 &&
+ git submodule foreach "echo \$toplevel-\$name-\$path-\$sha1" > ../actual &&
+ git config foo.bar zar &&
+ git submodule foreach "git config --file \"\$toplevel/.git/config\" foo.bar"
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup nested submodules' '
+ git clone submodule nested1 &&
+ git clone submodule nested2 &&
+ git clone submodule nested3 &&
+ (
+ cd nested3 &&
+ git submodule add ../submodule submodule &&
+ test_tick &&
+ git commit -m "submodule" &&
+ git submodule init submodule
+ ) &&
+ (
+ cd nested2 &&
+ git submodule add ../nested3 nested3 &&
+ test_tick &&
+ git commit -m "nested3" &&
+ git submodule init nested3
+ ) &&
+ (
+ cd nested1 &&
+ git submodule add ../nested2 nested2 &&
+ test_tick &&
+ git commit -m "nested2" &&
+ git submodule init nested2
+ ) &&
+ (
+ cd super &&
+ git submodule add ../nested1 nested1 &&
+ test_tick &&
+ git commit -m "nested1" &&
+ git submodule init nested1
+ )
+'
+
+test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' '
+ git clone super clone2 &&
+ (
+ cd clone2 &&
+ test ! -d sub1/.git &&
+ test ! -d sub2/.git &&
+ test ! -d sub3/.git &&
+ test ! -d nested1/.git &&
+ git submodule update --init &&
+ test -d sub1/.git &&
+ test -d sub2/.git &&
+ test -d sub3/.git &&
+ test -d nested1/.git &&
+ test ! -d nested1/nested2/.git &&
+ git submodule foreach "git submodule update --init" &&
+ test -d nested1/nested2/.git &&
+ test ! -d nested1/nested2/nested3/.git
+ )
+'
+
+test_expect_success 'use "foreach --recursive" to checkout all submodules' '
+ (
+ cd clone2 &&
+ git submodule foreach --recursive "git submodule update --init" &&
+ test -d nested1/nested2/nested3/.git &&
+ test -d nested1/nested2/nested3/submodule/.git
+ )
+'
+
+cat > expect <<EOF
+Entering 'nested1'
+Entering 'nested1/nested2'
+Entering 'nested1/nested2/nested3'
+Entering 'nested1/nested2/nested3/submodule'
+Entering 'sub1'
+Entering 'sub2'
+Entering 'sub3'
+EOF
+
+test_expect_success 'test messages from "foreach --recursive"' '
+ (
+ cd clone2 &&
+ git submodule foreach --recursive "true" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+cat > expect <<EOF
+nested1-nested1
+nested2-nested2
+nested3-nested3
+submodule-submodule
+foo1-sub1
+foo2-sub2
+foo3-sub3
+EOF
+
+test_expect_success 'test "foreach --quiet --recursive"' '
+ (
+ cd clone2 &&
+ git submodule foreach -q --recursive "echo \$name-\$path" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'use "update --recursive" to checkout all submodules' '
+ git clone super clone3 &&
+ (
+ cd clone3 &&
+ test ! -d sub1/.git &&
+ test ! -d sub2/.git &&
+ test ! -d sub3/.git &&
+ test ! -d nested1/.git &&
+ git submodule update --init --recursive &&
+ test -d sub1/.git &&
+ test -d sub2/.git &&
+ test -d sub3/.git &&
+ test -d nested1/.git &&
+ test -d nested1/nested2/.git &&
+ test -d nested1/nested2/nested3/.git &&
+ test -d nested1/nested2/nested3/submodule/.git
+ )
+'
+
+nested1sha1=$(cd clone3/nested1 && git rev-parse HEAD)
+nested2sha1=$(cd clone3/nested1/nested2 && git rev-parse HEAD)
+nested3sha1=$(cd clone3/nested1/nested2/nested3 && git rev-parse HEAD)
+submodulesha1=$(cd clone3/nested1/nested2/nested3/submodule && git rev-parse HEAD)
+sub1sha1=$(cd clone3/sub1 && git rev-parse HEAD)
+sub2sha1=$(cd clone3/sub2 && git rev-parse HEAD)
+sub3sha1=$(cd clone3/sub3 && git rev-parse HEAD)
+sub1sha1_short=$(cd clone3/sub1 && git rev-parse --short HEAD)
+sub2sha1_short=$(cd clone3/sub2 && git rev-parse --short HEAD)
+
+cat > expect <<EOF
+ $nested1sha1 nested1 (heads/master)
+ $nested2sha1 nested1/nested2 (heads/master)
+ $nested3sha1 nested1/nested2/nested3 (heads/master)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+ $sub1sha1 sub1 ($sub1sha1_short)
+ $sub2sha1 sub2 ($sub2sha1_short)
+ $sub3sha1 sub3 (heads/master)
+EOF
+
+test_expect_success 'test "status --recursive"' '
+ (
+ cd clone3 &&
+ git submodule status --recursive > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'use "git clone --recursive" to checkout all submodules' '
+ git clone --recursive super clone4 &&
+ test -d clone4/.git &&
+ test -d clone4/sub1/.git &&
+ test -d clone4/sub2/.git &&
+ test -d clone4/sub3/.git &&
+ test -d clone4/nested1/.git &&
+ test -d clone4/nested1/nested2/.git &&
+ test -d clone4/nested1/nested2/nested3/.git &&
+ test -d clone4/nested1/nested2/nested3/submodule/.git
+'
+
+test_done
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
new file mode 100755
index 0000000..cc16d3f
--- /dev/null
+++ b/t/t7408-submodule-reference.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, Red Hat Inc, Author: Michael S. Tsirkin (mst@redhat.com)
+#
+
+test_description='test clone --reference'
+. ./test-lib.sh
+
+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 supermodule' \
+'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 `wc -l <super/sub/.git/objects/info/alternates` = 1'
+
+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 supermodule' \
+'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 `wc -l <super-clone/sub/.git/objects/info/alternates` = 1'
+
+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_done
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
new file mode 100755
index 0000000..aa9c577
--- /dev/null
+++ b/t/t7500-commit.sh
@@ -0,0 +1,218 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Steven Grimm
+#
+
+test_description='git commit
+
+Tests for selected commit options.'
+
+. ./test-lib.sh
+
+commit_msg_is () {
+ test "`git log --pretty=format:%s%b -1`" = "$1"
+}
+
+# A sanity check to see if commit is working at all.
+test_expect_success 'a basic commit in an empty tree should succeed' '
+ echo content > foo &&
+ git add foo &&
+ git commit -m "initial commit"
+'
+
+test_expect_success 'nonexistent template file should return error' '
+ echo changes >> foo &&
+ git add foo &&
+ test_must_fail git commit --template "$PWD"/notexist
+'
+
+test_expect_success 'nonexistent template file in config should return error' '
+ git config commit.template "$PWD"/notexist &&
+ test_must_fail git commit &&
+ git config --unset commit.template
+'
+
+# From now on we'll use a template file that exists.
+TEMPLATE="$PWD"/template
+
+test_expect_success 'unedited template should not commit' '
+ echo "template line" > "$TEMPLATE" &&
+ test_must_fail git commit --template "$TEMPLATE"
+'
+
+test_expect_success 'unedited template with comments should not commit' '
+ echo "# comment in template" >> "$TEMPLATE" &&
+ test_must_fail git commit --template "$TEMPLATE"
+'
+
+test_expect_success 'a Signed-off-by line by itself should not commit' '
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-signed-off &&
+ test_must_fail git commit --template "$TEMPLATE"
+ )
+'
+
+test_expect_success 'adding comments to a template should not commit' '
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-comments &&
+ test_must_fail git commit --template "$TEMPLATE"
+ )
+'
+
+test_expect_success 'adding real content to a template should commit' '
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+ git commit --template "$TEMPLATE"
+ ) &&
+ commit_msg_is "template linecommit message"
+'
+
+test_expect_success '-t option should be short for --template' '
+ echo "short template" > "$TEMPLATE" &&
+ echo "new content" >> foo &&
+ git add foo &&
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+ git commit -t "$TEMPLATE"
+ ) &&
+ commit_msg_is "short templatecommit message"
+'
+
+test_expect_success 'config-specified template should commit' '
+ echo "new template" > "$TEMPLATE" &&
+ git config commit.template "$TEMPLATE" &&
+ echo "more content" >> foo &&
+ git add foo &&
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+ git commit
+ ) &&
+ git config --unset commit.template &&
+ commit_msg_is "new templatecommit message"
+'
+
+test_expect_success 'explicit commit message should override template' '
+ echo "still more content" >> foo &&
+ git add foo &&
+ GIT_EDITOR="$TEST_DIRECTORY"/t7500/add-content git commit --template "$TEMPLATE" \
+ -m "command line msg" &&
+ commit_msg_is "command line msg"
+'
+
+test_expect_success 'commit message from file should override template' '
+ echo "content galore" >> foo &&
+ git add foo &&
+ echo "standard input msg" |
+ (
+ test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+ git commit --template "$TEMPLATE" --file -
+ ) &&
+ commit_msg_is "standard input msg"
+'
+
+test_expect_success 'using alternate GIT_INDEX_FILE (1)' '
+
+ cp .git/index saved-index &&
+ (
+ echo some new content >file &&
+ GIT_INDEX_FILE=.git/another_index &&
+ export GIT_INDEX_FILE &&
+ git add file &&
+ git commit -m "commit using another index" &&
+ git diff-index --exit-code HEAD &&
+ git diff-files --exit-code
+ ) &&
+ cmp .git/index saved-index >/dev/null
+
+'
+
+test_expect_success 'using alternate GIT_INDEX_FILE (2)' '
+
+ cp .git/index saved-index &&
+ (
+ rm -f .git/no-such-index &&
+ GIT_INDEX_FILE=.git/no-such-index &&
+ export GIT_INDEX_FILE &&
+ git commit -m "commit using nonexistent index" &&
+ test -z "$(git ls-files)" &&
+ test -z "$(git ls-tree HEAD)"
+
+ ) &&
+ cmp .git/index saved-index >/dev/null
+'
+
+cat > expect << EOF
+zort
+
+Signed-off-by: C O Mitter <committer@example.com>
+EOF
+
+test_expect_success '--signoff' '
+ echo "yet another content *narf*" >> foo &&
+ echo "zort" | git commit -s -F - foo &&
+ git cat-file commit HEAD | sed "1,/^\$/d" > output &&
+ test_cmp expect output
+'
+
+test_expect_success 'commit message from file (1)' '
+ mkdir subdir &&
+ echo "Log in top directory" >log &&
+ echo "Log in sub directory" >subdir/log &&
+ (
+ cd subdir &&
+ git commit --allow-empty -F log
+ ) &&
+ commit_msg_is "Log in sub directory"
+'
+
+test_expect_success 'commit message from file (2)' '
+ rm -f log &&
+ echo "Log in sub directory" >subdir/log &&
+ (
+ cd subdir &&
+ git commit --allow-empty -F log
+ ) &&
+ commit_msg_is "Log in sub directory"
+'
+
+test_expect_success 'commit message from stdin' '
+ (
+ cd subdir &&
+ echo "Log with foo word" | git commit --allow-empty -F -
+ ) &&
+ commit_msg_is "Log with foo word"
+'
+
+test_expect_success 'commit -F overrides -t' '
+ (
+ cd subdir &&
+ echo "-F log" > f.log &&
+ echo "-t template" > t.template &&
+ git commit --allow-empty -F f.log -t t.template
+ ) &&
+ commit_msg_is "-F log"
+'
+
+test_expect_success 'Commit without message is allowed with --allow-empty-message' '
+ echo "more content" >>foo &&
+ git add foo &&
+ >empty &&
+ git commit --allow-empty-message <empty &&
+ commit_msg_is ""
+'
+
+test_expect_success 'Commit without message is no-no without --allow-empty-message' '
+ echo "more content" >>foo &&
+ git add foo &&
+ >empty &&
+ test_must_fail git commit <empty
+'
+
+test_expect_success 'Commit a message with --allow-empty-message' '
+ echo "even more content" >>foo &&
+ git add foo &&
+ git commit --allow-empty-message -m"hello there" &&
+ commit_msg_is "hello there"
+'
+
+test_done
diff --git a/t/t7500/add-comments b/t/t7500/add-comments
new file mode 100755
index 0000000..a72e65c
--- /dev/null
+++ b/t/t7500/add-comments
@@ -0,0 +1,4 @@
+#!/bin/sh
+echo "# this is a new comment" >> "$1"
+echo "# and so is this" >> "$1"
+exit 0
diff --git a/t/t7500/add-content b/t/t7500/add-content
new file mode 100755
index 0000000..2fa3d86
--- /dev/null
+++ b/t/t7500/add-content
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo "commit message" >> "$1"
+exit 0
diff --git a/t/t7500/add-signed-off b/t/t7500/add-signed-off
new file mode 100755
index 0000000..e1d856a
--- /dev/null
+++ b/t/t7500/add-signed-off
@@ -0,0 +1,3 @@
+#!/bin/sh
+echo "Signed-off-by: foo <bar@frotz>" >> "$1"
+exit 0
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
new file mode 100755
index 0000000..8297cb4
--- /dev/null
+++ b/t/t7501-commit.sh
@@ -0,0 +1,440 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>
+#
+
+# FIXME: Test the various index usages, -i and -o, test reflog,
+# signoff
+
+test_description='git commit'
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success \
+ "initial status" \
+ "echo 'bongo bongo' >file &&
+ git add file && \
+ git status | grep 'Initial commit'"
+
+test_expect_success \
+ "fail initial amend" \
+ "test_must_fail git commit --amend"
+
+test_expect_success \
+ "initial commit" \
+ "git commit -m initial"
+
+test_expect_success \
+ "invalid options 1" \
+ "test_must_fail git commit -m foo -m bar -F file"
+
+test_expect_success \
+ "invalid options 2" \
+ "test_must_fail git commit -C HEAD -m illegal"
+
+test_expect_success \
+ "using paths with -a" \
+ "echo King of the bongo >file &&
+ test_must_fail git commit -m foo -a file"
+
+test_expect_success PERL \
+ "using paths with --interactive" \
+ "echo bong-o-bong >file &&
+ ! (echo 7 | git commit -m foo --interactive file)"
+
+test_expect_success \
+ "using invalid commit with -C" \
+ "test_must_fail git commit -C bogus"
+
+test_expect_success \
+ "testing nothing to commit" \
+ "test_must_fail git commit -m initial"
+
+test_expect_success \
+ "next commit" \
+ "echo 'bongo bongo bongo' >file \
+ git commit -m next -a"
+
+test_expect_success \
+ "commit message from non-existing file" \
+ "echo 'more bongo: bongo bongo bongo bongo' >file && \
+ test_must_fail git commit -F gah -a"
+
+# Empty except stray tabs and spaces on a few lines.
+sed -e 's/@$//' >msg <<EOF
+ @
+
+ @
+Signed-off-by: hula
+EOF
+test_expect_success \
+ "empty commit message" \
+ "test_must_fail git commit -F msg -a"
+
+test_expect_success \
+ "commit message from file" \
+ "echo 'this is the commit message, coming from a file' >msg && \
+ git commit -F msg -a"
+
+cat >editor <<\EOF
+#!/bin/sh
+sed -e "s/a file/an amend commit/g" < "$1" > "$1-"
+mv "$1-" "$1"
+EOF
+chmod 755 editor
+
+test_expect_success \
+ "amend commit" \
+ "EDITOR=./editor git commit --amend"
+
+test_expect_success \
+ "passing -m and -F" \
+ "echo 'enough with the bongos' >file && \
+ test_must_fail git commit -F msg -m amending ."
+
+test_expect_success \
+ "using message from other commit" \
+ "git commit -C HEAD^ ."
+
+cat >editor <<\EOF
+#!/bin/sh
+sed -e "s/amend/older/g" < "$1" > "$1-"
+mv "$1-" "$1"
+EOF
+chmod 755 editor
+
+test_expect_success \
+ "editing message from other commit" \
+ "echo 'hula hula' >file && \
+ EDITOR=./editor git commit -c HEAD^ -a"
+
+test_expect_success \
+ "message from stdin" \
+ "echo 'silly new contents' >file && \
+ echo commit message from stdin | git commit -F - -a"
+
+test_expect_success \
+ "overriding author from command line" \
+ "echo 'gak' >file && \
+ git commit -m 'author' --author 'Rubber Duck <rduck@convoy.org>' -a >output 2>&1"
+
+test_expect_success \
+ "commit --author output mentions author" \
+ "grep Rubber.Duck output"
+
+test_expect_success PERL \
+ "interactive add" \
+ "echo 7 | git commit --interactive | grep 'What now'"
+
+test_expect_success \
+ "showing committed revisions" \
+ "git rev-list HEAD >current"
+
+cat >editor <<\EOF
+#!/bin/sh
+sed -e "s/good/bad/g" < "$1" > "$1-"
+mv "$1-" "$1"
+EOF
+chmod 755 editor
+
+cat >msg <<EOF
+A good commit message.
+EOF
+
+test_expect_success \
+ 'editor not invoked if -F is given' '
+ echo "moo" >file &&
+ EDITOR=./editor git commit -a -F msg &&
+ git show -s --pretty=format:"%s" | grep -q good &&
+ echo "quack" >file &&
+ echo "Another good message." | EDITOR=./editor git commit -a -F - &&
+ git show -s --pretty=format:"%s" | grep -q good
+ '
+# We could just check the head sha1, but checking each commit makes it
+# easier to isolate bugs.
+
+cat >expected <<\EOF
+72c0dc9855b0c9dadcbfd5a31cab072e0cb774ca
+9b88fc14ce6b32e3d9ee021531a54f18a5cf38a2
+3536bbb352c3a1ef9a420f5b4242d48578b92aa7
+d381ac431806e53f3dd7ac2f1ae0534f36d738b9
+4fd44095ad6334f3ef72e4c5ec8ddf108174b54a
+402702b49136e7587daa9280e91e4bb7cb2179f7
+EOF
+
+test_expect_success \
+ 'validate git rev-list output.' \
+ 'test_cmp expected current'
+
+test_expect_success 'partial commit that involves removal (1)' '
+
+ git rm --cached file &&
+ mv file elif &&
+ git add elif &&
+ git commit -m "Partial: add elif" elif &&
+ git diff-tree --name-status HEAD^ HEAD >current &&
+ echo "A elif" >expected &&
+ test_cmp expected current
+
+'
+
+test_expect_success 'partial commit that involves removal (2)' '
+
+ git commit -m "Partial: remove file" file &&
+ git diff-tree --name-status HEAD^ HEAD >current &&
+ echo "D file" >expected &&
+ test_cmp expected current
+
+'
+
+test_expect_success 'partial commit that involves removal (3)' '
+
+ git rm --cached elif &&
+ echo elif >elif &&
+ git commit -m "Partial: modify elif" elif &&
+ git diff-tree --name-status HEAD^ HEAD >current &&
+ echo "M elif" >expected &&
+ test_cmp expected current
+
+'
+
+author="The Real Author <someguy@his.email.org>"
+test_expect_success 'amend commit to fix author' '
+
+ oldtick=$GIT_AUTHOR_DATE &&
+ test_tick &&
+ git reset --hard &&
+ git cat-file -p HEAD |
+ sed -e "s/author.*/author $author $oldtick/" \
+ -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
+ expected &&
+ git commit --amend --author="$author" &&
+ git cat-file -p HEAD > current &&
+ test_cmp expected current
+
+'
+
+test_expect_success 'amend commit to fix date' '
+
+ test_tick &&
+ newtick=$GIT_AUTHOR_DATE &&
+ git reset --hard &&
+ git cat-file -p HEAD |
+ sed -e "s/author.*/author $author $newtick/" \
+ -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
+ expected &&
+ git commit --amend --date="$newtick" &&
+ git cat-file -p HEAD > current &&
+ test_cmp expected current
+
+'
+
+test_expect_success 'sign off (1)' '
+
+ echo 1 >positive &&
+ git add positive &&
+ git commit -s -m "thank you" &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ (
+ echo thank you
+ echo
+ git var GIT_COMMITTER_IDENT |
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'sign off (2)' '
+
+ echo 2 >positive &&
+ git add positive &&
+ existing="Signed-off-by: Watch This <watchthis@example.com>" &&
+ git commit -s -m "thank you
+
+$existing" &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ (
+ echo thank you
+ echo
+ echo $existing
+ git var GIT_COMMITTER_IDENT |
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'signoff gap' '
+
+ echo 3 >positive &&
+ git add positive &&
+ alt="Alt-RFC-822-Header: Value" &&
+ git commit -s -m "welcome
+
+$alt" &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+ (
+ echo welcome
+ echo
+ echo $alt
+ git var GIT_COMMITTER_IDENT |
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'signoff gap 2' '
+
+ echo 4 >positive &&
+ git add positive &&
+ alt="fixed: 34" &&
+ git commit -s -m "welcome
+
+We have now
+$alt" &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+ (
+ echo welcome
+ echo
+ echo We have now
+ echo $alt
+ echo
+ git var GIT_COMMITTER_IDENT |
+ sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
+ ) >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'multiple -m' '
+
+ >negative &&
+ git add negative &&
+ git commit -m "one" -m "two" -m "three" &&
+ git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
+ (
+ echo one
+ echo
+ echo two
+ echo
+ echo three
+ ) >expected &&
+ test_cmp expected actual
+
+'
+
+author="The Real Author <someguy@his.email.org>"
+test_expect_success 'amend commit to fix author' '
+
+ oldtick=$GIT_AUTHOR_DATE &&
+ test_tick &&
+ git reset --hard &&
+ git cat-file -p HEAD |
+ sed -e "s/author.*/author $author $oldtick/" \
+ -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
+ expected &&
+ git commit --amend --author="$author" &&
+ git cat-file -p HEAD > current &&
+ test_cmp expected current
+
+'
+
+test_expect_success 'git commit <file> with dirty index' '
+ echo tacocat > elif &&
+ echo tehlulz > chz &&
+ git add chz &&
+ git commit elif -m "tacocat is a palindrome" &&
+ git show --stat | grep elif &&
+ git diff --cached | grep chz
+'
+
+test_expect_success 'same tree (single parent)' '
+
+ git reset --hard
+
+ if git commit -m empty
+ then
+ echo oops -- should have complained
+ false
+ else
+ : happy
+ fi
+
+'
+
+test_expect_success 'same tree (single parent) --allow-empty' '
+
+ git commit --allow-empty -m "forced empty" &&
+ git cat-file commit HEAD | grep forced
+
+'
+
+test_expect_success 'same tree (merge and amend merge)' '
+
+ git checkout -b side HEAD^ &&
+ echo zero >zero &&
+ git add zero &&
+ git commit -m "add zero" &&
+ git checkout master &&
+
+ git merge -s ours side -m "empty ok" &&
+ git diff HEAD^ HEAD >actual &&
+ : >expected &&
+ test_cmp expected actual &&
+
+ git commit --amend -m "empty really ok" &&
+ git diff HEAD^ HEAD >actual &&
+ : >expected &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'amend using the message from another commit' '
+
+ git reset --hard &&
+ test_tick &&
+ git commit --allow-empty -m "old commit" &&
+ old=$(git rev-parse --verify HEAD) &&
+ test_tick &&
+ git commit --allow-empty -m "new commit" &&
+ new=$(git rev-parse --verify HEAD) &&
+ test_tick &&
+ git commit --allow-empty --amend -C "$old" &&
+ git show --pretty="format:%ad %s" "$old" >expected &&
+ git show --pretty="format:%ad %s" HEAD >actual &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'amend using the message from a commit named with tag' '
+
+ git reset --hard &&
+ test_tick &&
+ git commit --allow-empty -m "old commit" &&
+ old=$(git rev-parse --verify HEAD) &&
+ git tag -a -m "tag on old" tagged-old HEAD &&
+ test_tick &&
+ git commit --allow-empty -m "new commit" &&
+ new=$(git rev-parse --verify HEAD) &&
+ test_tick &&
+ git commit --allow-empty --amend -C tagged-old &&
+ git show --pretty="format:%ad %s" "$old" >expected &&
+ git show --pretty="format:%ad %s" HEAD >actual &&
+ test_cmp expected actual
+
+'
+
+test_expect_success 'amend can copy notes' '
+
+ git config notes.rewrite.amend true &&
+ git config notes.rewriteRef "refs/notes/*" &&
+ test_commit foo &&
+ git notes add -m"a note" &&
+ test_tick &&
+ git commit --amend -m"new foo" &&
+ test "$(git notes show)" = "a note"
+
+'
+
+test_done
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
new file mode 100755
index 0000000..ac2e187
--- /dev/null
+++ b/t/t7502-commit.sh
@@ -0,0 +1,447 @@
+#!/bin/sh
+
+test_description='git commit porcelain-ish'
+
+. ./test-lib.sh
+
+# Arguments: [<prefix] [<commit message>] [<commit options>]
+check_summary_oneline() {
+ test_tick &&
+ git commit ${3+"$3"} -m "$2" | head -1 > act &&
+
+ # branch name
+ SUMMARY_PREFIX="$(git name-rev --name-only HEAD)" &&
+
+ # append the "special" prefix, like "root-commit", "detached HEAD"
+ if test -n "$1"
+ then
+ SUMMARY_PREFIX="$SUMMARY_PREFIX ($1)"
+ fi
+
+ # abbrev SHA-1
+ SUMMARY_POSTFIX="$(git log -1 --pretty='format:%h')"
+ echo "[$SUMMARY_PREFIX $SUMMARY_POSTFIX] $2" >exp &&
+
+ test_cmp exp act
+}
+
+test_expect_success 'output summary format' '
+
+ echo new >file1 &&
+ git add file1 &&
+ check_summary_oneline "root-commit" "initial" &&
+
+ echo change >>file1 &&
+ git add file1 &&
+ check_summary_oneline "" "a change"
+'
+
+test_expect_success 'output summary format for commit with an empty diff' '
+
+ check_summary_oneline "" "empty" "--allow-empty"
+'
+
+test_expect_success 'output summary format for merges' '
+
+ git checkout -b recursive-base &&
+ test_commit base file1 &&
+
+ git checkout -b recursive-a recursive-base &&
+ test_commit commit-a file1 &&
+
+ git checkout -b recursive-b recursive-base &&
+ test_commit commit-b file1 &&
+
+ # conflict
+ git checkout recursive-a &&
+ test_must_fail git merge recursive-b &&
+ # resolve the conflict
+ echo commit-a > file1 &&
+ git add file1 &&
+ check_summary_oneline "" "Merge"
+'
+
+output_tests_cleanup() {
+ # this is needed for "do not fire editor in the presence of conflicts"
+ git checkout master &&
+
+ # this is needed for the "partial removal" test to pass
+ git rm file1 &&
+ git commit -m "cleanup"
+}
+
+test_expect_success 'the basics' '
+
+ output_tests_cleanup &&
+
+ echo doing partial >"commit is" &&
+ mkdir not &&
+ echo very much encouraged but we should >not/forbid &&
+ git add "commit is" not &&
+ echo update added "commit is" file >"commit is" &&
+ echo also update another >not/forbid &&
+ test_tick &&
+ git commit -a -m "initial with -a" &&
+
+ git cat-file blob HEAD:"commit is" >current.1 &&
+ git cat-file blob HEAD:not/forbid >current.2 &&
+
+ cmp current.1 "commit is" &&
+ cmp current.2 not/forbid
+
+'
+
+test_expect_success 'partial' '
+
+ echo another >"commit is" &&
+ echo another >not/forbid &&
+ test_tick &&
+ git commit -m "partial commit to handle a file" "commit is" &&
+
+ changed=$(git diff-tree --name-only HEAD^ HEAD) &&
+ test "$changed" = "commit is"
+
+'
+
+test_expect_success 'partial modification in a subdirectory' '
+
+ test_tick &&
+ git commit -m "partial commit to subdirectory" not &&
+
+ changed=$(git diff-tree -r --name-only HEAD^ HEAD) &&
+ test "$changed" = "not/forbid"
+
+'
+
+test_expect_success 'partial removal' '
+
+ git rm not/forbid &&
+ git commit -m "partial commit to remove not/forbid" not &&
+
+ changed=$(git diff-tree -r --name-only HEAD^ HEAD) &&
+ test "$changed" = "not/forbid" &&
+ remain=$(git ls-tree -r --name-only HEAD) &&
+ test "$remain" = "commit is"
+
+'
+
+test_expect_success 'sign off' '
+
+ >positive &&
+ git add positive &&
+ git commit -s -m "thank you" &&
+ actual=$(git cat-file commit HEAD | sed -ne "s/Signed-off-by: //p") &&
+ expected=$(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/") &&
+ test "z$actual" = "z$expected"
+
+'
+
+test_expect_success 'multiple -m' '
+
+ >negative &&
+ git add negative &&
+ git commit -m "one" -m "two" -m "three" &&
+ actual=$(git cat-file commit HEAD | sed -e "1,/^\$/d") &&
+ expected=$(echo one; echo; echo two; echo; echo three) &&
+ test "z$actual" = "z$expected"
+
+'
+
+test_expect_success 'verbose' '
+
+ echo minus >negative &&
+ git add negative &&
+ git status -v | sed -ne "/^diff --git /p" >actual &&
+ echo "diff --git a/negative b/negative" >expect &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'verbose respects diff config' '
+
+ git config color.diff always &&
+ git status -v >actual &&
+ grep "\[1mdiff --git" actual &&
+ git config --unset color.diff
+'
+
+test_expect_success 'cleanup commit messages (verbatim,-t)' '
+
+ echo >>negative &&
+ { echo;echo "# text";echo; } >expect &&
+ git commit --cleanup=verbatim -t expect -a &&
+ git cat-file -p HEAD |sed -e "1,/^\$/d" |head -n 3 >actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'cleanup commit messages (verbatim,-F)' '
+
+ echo >>negative &&
+ git commit --cleanup=verbatim -F expect -a &&
+ git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'cleanup commit messages (verbatim,-m)' '
+
+ echo >>negative &&
+ git commit --cleanup=verbatim -m "$(cat expect)" -a &&
+ git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'cleanup commit messages (whitespace,-F)' '
+
+ echo >>negative &&
+ { echo;echo "# text";echo; } >text &&
+ echo "# text" >expect &&
+ git commit --cleanup=whitespace -F text -a &&
+ git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'cleanup commit messages (strip,-F)' '
+
+ echo >>negative &&
+ { echo;echo "# text";echo sample;echo; } >text &&
+ echo sample >expect &&
+ git commit --cleanup=strip -F text -a &&
+ git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+ test_cmp expect actual
+
+'
+
+echo "sample
+
+# Please enter the commit message for your changes. Lines starting
+# with '#' will be ignored, and an empty message aborts the commit." >expect
+
+test_expect_success 'cleanup commit messages (strip,-F,-e)' '
+
+ echo >>negative &&
+ { echo;echo sample;echo; } >text &&
+ git commit -e -F text -a &&
+ head -n 4 .git/COMMIT_EDITMSG >actual &&
+ test_cmp expect actual
+
+'
+
+echo "#
+# Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+#" >> expect
+
+test_expect_success 'author different from committer' '
+
+ echo >>negative &&
+ git commit -e -m "sample"
+ head -n 7 .git/COMMIT_EDITMSG >actual &&
+ test_cmp expect actual
+'
+
+mv expect expect.tmp
+sed '$d' < expect.tmp > expect
+rm -f expect.tmp
+echo "# Committer:
+#" >> expect
+
+test_expect_success 'committer is automatic' '
+
+ echo >>negative &&
+ (
+ unset GIT_COMMITTER_EMAIL
+ unset GIT_COMMITTER_NAME
+ # must fail because there is no change
+ test_must_fail git commit -e -m "sample"
+ ) &&
+ head -n 8 .git/COMMIT_EDITMSG | \
+ sed "s/^# Committer: .*/# Committer:/" >actual &&
+ test_cmp expect actual
+'
+
+pwd=`pwd`
+cat >> .git/FAKE_EDITOR << EOF
+#! /bin/sh
+echo editor started > "$pwd/.git/result"
+exit 0
+EOF
+chmod +x .git/FAKE_EDITOR
+
+test_expect_success 'do not fire editor in the presence of conflicts' '
+
+ git clean -f &&
+ echo f >g &&
+ git add g &&
+ git commit -m "add g" &&
+ git branch second &&
+ echo master >g &&
+ echo g >h &&
+ git add g h &&
+ git commit -m "modify g and add h" &&
+ git checkout second &&
+ echo second >g &&
+ git add g &&
+ git commit -m second &&
+ # Must fail due to conflict
+ test_must_fail git cherry-pick -n master &&
+ echo "editor not started" >.git/result &&
+ (
+ GIT_EDITOR="$(pwd)/.git/FAKE_EDITOR" &&
+ export GIT_EDITOR &&
+ test_must_fail git commit
+ ) &&
+ test "$(cat .git/result)" = "editor not started"
+'
+
+pwd=`pwd`
+cat >.git/FAKE_EDITOR <<EOF
+#! $SHELL_PATH
+# kill -TERM command added below.
+EOF
+
+test_expect_success EXECKEEPSPID 'a SIGTERM should break locks' '
+ echo >>negative &&
+ ! "$SHELL_PATH" -c '\''
+ echo kill -TERM $$ >> .git/FAKE_EDITOR
+ GIT_EDITOR=.git/FAKE_EDITOR
+ export GIT_EDITOR
+ exec git commit -a'\'' &&
+ test ! -f .git/index.lock
+'
+
+rm -f .git/MERGE_MSG .git/COMMIT_EDITMSG
+git reset -q --hard
+
+test_expect_success 'Hand committing of a redundant merge removes dups' '
+
+ git rev-parse second master >expect &&
+ test_must_fail git merge second master &&
+ git checkout master g &&
+ EDITOR=: git commit -a &&
+ git cat-file commit HEAD | sed -n -e "s/^parent //p" -e "/^$/q" >actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'A single-liner subject with a token plus colon is not a footer' '
+
+ git reset --hard &&
+ git commit -s -m "hello: kitty" --allow-empty &&
+ git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+ test $(wc -l <actual) = 3
+
+'
+
+cat >.git/FAKE_EDITOR <<EOF
+#!$SHELL_PATH
+mv "\$1" "\$1.orig"
+(
+ echo message
+ cat "\$1.orig"
+) >"\$1"
+EOF
+
+echo '## Custom template' >template
+
+clear_config () {
+ (
+ git config --unset-all "$1"
+ case $? in
+ 0|5) exit 0 ;;
+ *) exit 1 ;;
+ esac
+ )
+}
+
+try_commit () {
+ git reset --hard &&
+ echo >>negative &&
+ GIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template &&
+ case "$use_template" in
+ '')
+ ! grep "^## Custom template" .git/COMMIT_EDITMSG ;;
+ *)
+ grep "^## Custom template" .git/COMMIT_EDITMSG ;;
+ esac
+}
+
+try_commit_status_combo () {
+
+ test_expect_success 'commit' '
+ clear_config commit.status &&
+ try_commit "" &&
+ grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+ test_expect_success 'commit' '
+ clear_config commit.status &&
+ try_commit "" &&
+ grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+ test_expect_success 'commit --status' '
+ clear_config commit.status &&
+ try_commit --status &&
+ grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+ test_expect_success 'commit --no-status' '
+ clear_config commit.status &&
+ try_commit --no-status
+ ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+ test_expect_success 'commit with commit.status = yes' '
+ clear_config commit.status &&
+ git config commit.status yes &&
+ try_commit "" &&
+ grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+ test_expect_success 'commit with commit.status = no' '
+ clear_config commit.status &&
+ git config commit.status no &&
+ try_commit "" &&
+ ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+ test_expect_success 'commit --status with commit.status = yes' '
+ clear_config commit.status &&
+ git config commit.status yes &&
+ try_commit --status &&
+ grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+ test_expect_success 'commit --no-status with commit.status = yes' '
+ clear_config commit.status &&
+ git config commit.status yes &&
+ try_commit --no-status &&
+ ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+ test_expect_success 'commit --status with commit.status = no' '
+ clear_config commit.status &&
+ git config commit.status no &&
+ try_commit --status &&
+ grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+ test_expect_success 'commit --no-status with commit.status = no' '
+ clear_config commit.status &&
+ git config commit.status no &&
+ try_commit --no-status &&
+ ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+ '
+
+}
+
+try_commit_status_combo
+
+use_template="-t template"
+
+try_commit_status_combo
+
+test_done
diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
new file mode 100755
index 0000000..8528f64
--- /dev/null
+++ b/t/t7503-pre-commit-hook.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+test_description='pre-commit hook'
+
+. ./test-lib.sh
+
+test_expect_success 'with no hook' '
+
+ echo "foo" > file &&
+ git add file &&
+ git commit -m "first"
+
+'
+
+test_expect_success '--no-verify with no hook' '
+
+ echo "bar" > file &&
+ git add file &&
+ git commit --no-verify -m "bar"
+
+'
+
+# now install hook that always succeeds
+HOOKDIR="$(git rev-parse --git-dir)/hooks"
+HOOK="$HOOKDIR/pre-commit"
+mkdir -p "$HOOKDIR"
+cat > "$HOOK" <<EOF
+#!/bin/sh
+exit 0
+EOF
+chmod +x "$HOOK"
+
+test_expect_success 'with succeeding hook' '
+
+ echo "more" >> file &&
+ git add file &&
+ git commit -m "more"
+
+'
+
+test_expect_success '--no-verify with succeeding hook' '
+
+ echo "even more" >> file &&
+ git add file &&
+ git commit --no-verify -m "even more"
+
+'
+
+# now a hook that fails
+cat > "$HOOK" <<EOF
+#!/bin/sh
+exit 1
+EOF
+
+test_expect_success 'with failing hook' '
+
+ echo "another" >> file &&
+ git add file &&
+ test_must_fail git commit -m "another"
+
+'
+
+test_expect_success '--no-verify with failing hook' '
+
+ echo "stuff" >> file &&
+ git add file &&
+ git commit --no-verify -m "stuff"
+
+'
+
+chmod -x "$HOOK"
+test_expect_success POSIXPERM 'with non-executable hook' '
+
+ echo "content" >> file &&
+ git add file &&
+ git commit -m "content"
+
+'
+
+test_expect_success POSIXPERM '--no-verify with non-executable hook' '
+
+ echo "more content" >> file &&
+ git add file &&
+ git commit --no-verify -m "more content"
+
+'
+
+test_done
diff --git a/t/t7504-commit-msg-hook.sh b/t/t7504-commit-msg-hook.sh
new file mode 100755
index 0000000..1f53ea8
--- /dev/null
+++ b/t/t7504-commit-msg-hook.sh
@@ -0,0 +1,223 @@
+#!/bin/sh
+
+test_description='commit-msg hook'
+
+. ./test-lib.sh
+
+test_expect_success 'with no hook' '
+
+ echo "foo" > file &&
+ git add file &&
+ git commit -m "first"
+
+'
+
+# set up fake editor for interactive editing
+cat > fake-editor <<'EOF'
+#!/bin/sh
+cp FAKE_MSG "$1"
+exit 0
+EOF
+chmod +x fake-editor
+
+## Not using test_set_editor here so we can easily ensure the editor variable
+## is only set for the editor tests
+FAKE_EDITOR="$(pwd)/fake-editor"
+export FAKE_EDITOR
+
+test_expect_success 'with no hook (editor)' '
+
+ echo "more foo" >> file &&
+ git add file &&
+ echo "more foo" > FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit
+
+'
+
+test_expect_success '--no-verify with no hook' '
+
+ echo "bar" > file &&
+ git add file &&
+ git commit --no-verify -m "bar"
+
+'
+
+test_expect_success '--no-verify with no hook (editor)' '
+
+ echo "more bar" > file &&
+ git add file &&
+ echo "more bar" > FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
+
+'
+
+# now install hook that always succeeds
+HOOKDIR="$(git rev-parse --git-dir)/hooks"
+HOOK="$HOOKDIR/commit-msg"
+mkdir -p "$HOOKDIR"
+cat > "$HOOK" <<EOF
+#!/bin/sh
+exit 0
+EOF
+chmod +x "$HOOK"
+
+test_expect_success 'with succeeding hook' '
+
+ echo "more" >> file &&
+ git add file &&
+ git commit -m "more"
+
+'
+
+test_expect_success 'with succeeding hook (editor)' '
+
+ echo "more more" >> file &&
+ git add file &&
+ echo "more more" > FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit
+
+'
+
+test_expect_success '--no-verify with succeeding hook' '
+
+ echo "even more" >> file &&
+ git add file &&
+ git commit --no-verify -m "even more"
+
+'
+
+test_expect_success '--no-verify with succeeding hook (editor)' '
+
+ echo "even more more" >> file &&
+ git add file &&
+ echo "even more more" > FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
+
+'
+
+# now a hook that fails
+cat > "$HOOK" <<EOF
+#!/bin/sh
+exit 1
+EOF
+
+test_expect_success 'with failing hook' '
+
+ echo "another" >> file &&
+ git add file &&
+ test_must_fail git commit -m "another"
+
+'
+
+test_expect_success 'with failing hook (editor)' '
+
+ echo "more another" >> file &&
+ git add file &&
+ echo "more another" > FAKE_MSG &&
+ ! (GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit)
+
+'
+
+test_expect_success '--no-verify with failing hook' '
+
+ echo "stuff" >> file &&
+ git add file &&
+ git commit --no-verify -m "stuff"
+
+'
+
+test_expect_success '--no-verify with failing hook (editor)' '
+
+ echo "more stuff" >> file &&
+ git add file &&
+ echo "more stuff" > FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
+
+'
+
+chmod -x "$HOOK"
+test_expect_success POSIXPERM 'with non-executable hook' '
+
+ echo "content" >> file &&
+ git add file &&
+ git commit -m "content"
+
+'
+
+test_expect_success POSIXPERM 'with non-executable hook (editor)' '
+
+ echo "content again" >> file &&
+ git add file &&
+ echo "content again" > FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -m "content again"
+
+'
+
+test_expect_success POSIXPERM '--no-verify with non-executable hook' '
+
+ echo "more content" >> file &&
+ git add file &&
+ git commit --no-verify -m "more content"
+
+'
+
+test_expect_success POSIXPERM '--no-verify with non-executable hook (editor)' '
+
+ echo "even more content" >> file &&
+ git add file &&
+ echo "even more content" > FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
+
+'
+
+# now a hook that edits the commit message
+cat > "$HOOK" <<'EOF'
+#!/bin/sh
+echo "new message" > "$1"
+exit 0
+EOF
+chmod +x "$HOOK"
+
+commit_msg_is () {
+ test "`git log --pretty=format:%s%b -1`" = "$1"
+}
+
+test_expect_success 'hook edits commit message' '
+
+ echo "additional" >> file &&
+ git add file &&
+ git commit -m "additional" &&
+ commit_msg_is "new message"
+
+'
+
+test_expect_success 'hook edits commit message (editor)' '
+
+ echo "additional content" >> file &&
+ git add file &&
+ echo "additional content" > FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit &&
+ commit_msg_is "new message"
+
+'
+
+test_expect_success "hook doesn't edit commit message" '
+
+ echo "plus" >> file &&
+ git add file &&
+ git commit --no-verify -m "plus" &&
+ commit_msg_is "plus"
+
+'
+
+test_expect_success "hook doesn't edit commit message (editor)" '
+
+ echo "more plus" >> file &&
+ git add file &&
+ echo "more plus" > FAKE_MSG &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify &&
+ commit_msg_is "more plus"
+
+'
+
+test_done
diff --git a/t/t7505-prepare-commit-msg-hook.sh b/t/t7505-prepare-commit-msg-hook.sh
new file mode 100755
index 0000000..ff18962
--- /dev/null
+++ b/t/t7505-prepare-commit-msg-hook.sh
@@ -0,0 +1,159 @@
+#!/bin/sh
+
+test_description='prepare-commit-msg hook'
+
+. ./test-lib.sh
+
+test_expect_success 'with no hook' '
+
+ echo "foo" > file &&
+ git add file &&
+ git commit -m "first"
+
+'
+
+# set up fake editor for interactive editing
+cat > fake-editor <<'EOF'
+#!/bin/sh
+exit 0
+EOF
+chmod +x fake-editor
+
+## Not using test_set_editor here so we can easily ensure the editor variable
+## is only set for the editor tests
+FAKE_EDITOR="$(pwd)/fake-editor"
+export FAKE_EDITOR
+
+# now install hook that always succeeds and adds a message
+HOOKDIR="$(git rev-parse --git-dir)/hooks"
+HOOK="$HOOKDIR/prepare-commit-msg"
+mkdir -p "$HOOKDIR"
+echo "#!$SHELL_PATH" > "$HOOK"
+cat >> "$HOOK" <<'EOF'
+
+if test "$2" = commit; then
+ source=$(git rev-parse "$3")
+else
+ source=${2-default}
+fi
+if test "$GIT_EDITOR" = :; then
+ sed -e "1s/.*/$source (no editor)/" "$1" > msg.tmp
+else
+ sed -e "1s/.*/$source/" "$1" > msg.tmp
+fi
+mv msg.tmp "$1"
+exit 0
+EOF
+chmod +x "$HOOK"
+
+echo dummy template > "$(git rev-parse --git-dir)/template"
+
+test_expect_success 'with hook (-m)' '
+
+ echo "more" >> file &&
+ git add file &&
+ git commit -m "more" &&
+ test "`git log -1 --pretty=format:%s`" = "message (no editor)"
+
+'
+
+test_expect_success 'with hook (-m editor)' '
+
+ echo "more" >> file &&
+ git add file &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -e -m "more more" &&
+ test "`git log -1 --pretty=format:%s`" = message
+
+'
+
+test_expect_success 'with hook (-t)' '
+
+ echo "more" >> file &&
+ git add file &&
+ git commit -t "$(git rev-parse --git-dir)/template" &&
+ test "`git log -1 --pretty=format:%s`" = template
+
+'
+
+test_expect_success 'with hook (-F)' '
+
+ echo "more" >> file &&
+ git add file &&
+ (echo more | git commit -F -) &&
+ test "`git log -1 --pretty=format:%s`" = "message (no editor)"
+
+'
+
+test_expect_success 'with hook (-F editor)' '
+
+ echo "more" >> file &&
+ git add file &&
+ (echo more more | GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -e -F -) &&
+ test "`git log -1 --pretty=format:%s`" = message
+
+'
+
+test_expect_success 'with hook (-C)' '
+
+ head=`git rev-parse HEAD` &&
+ echo "more" >> file &&
+ git add file &&
+ git commit -C $head &&
+ test "`git log -1 --pretty=format:%s`" = "$head (no editor)"
+
+'
+
+test_expect_success 'with hook (editor)' '
+
+ echo "more more" >> file &&
+ git add file &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit &&
+ test "`git log -1 --pretty=format:%s`" = default
+
+'
+
+test_expect_success 'with hook (--amend)' '
+
+ head=`git rev-parse HEAD` &&
+ echo "more" >> file &&
+ git add file &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --amend &&
+ test "`git log -1 --pretty=format:%s`" = "$head"
+
+'
+
+test_expect_success 'with hook (-c)' '
+
+ head=`git rev-parse HEAD` &&
+ echo "more" >> file &&
+ git add file &&
+ GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head &&
+ test "`git log -1 --pretty=format:%s`" = "$head"
+
+'
+
+cat > "$HOOK" <<'EOF'
+#!/bin/sh
+exit 1
+EOF
+
+test_expect_success 'with failing hook' '
+
+ head=`git rev-parse HEAD` &&
+ echo "more" >> file &&
+ git add file &&
+ ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head
+
+'
+
+test_expect_success 'with failing hook (--no-verify)' '
+
+ head=`git rev-parse HEAD` &&
+ echo "more" >> file &&
+ git add file &&
+ ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head
+
+'
+
+
+test_done
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
new file mode 100755
index 0000000..3d4f85d
--- /dev/null
+++ b/t/t7506-status-submodule.sh
@@ -0,0 +1,190 @@
+#!/bin/sh
+
+test_description='git status for submodule'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_create_repo sub &&
+ (
+ cd sub &&
+ : >bar &&
+ git add bar &&
+ git commit -m " Add bar" &&
+ : >foo &&
+ git add foo &&
+ git commit -m " Add foo"
+ ) &&
+ echo output > .gitignore &&
+ git add sub .gitignore &&
+ git commit -m "Add submodule sub"
+'
+
+test_expect_success 'status clean' '
+ git status >output &&
+ grep "nothing to commit" output
+'
+
+test_expect_success 'commit --dry-run -a clean' '
+ test_must_fail git commit --dry-run -a >output &&
+ grep "nothing to commit" output
+'
+
+test_expect_success 'status with modified file in submodule' '
+ (cd sub && git reset --hard) &&
+ echo "changed" >sub/foo &&
+ git status >output &&
+ grep "modified: sub (modified content)" output
+'
+
+test_expect_success 'status with modified file in submodule (porcelain)' '
+ (cd sub && git reset --hard) &&
+ echo "changed" >sub/foo &&
+ git status --porcelain >output &&
+ diff output - <<-\EOF
+ M sub
+ EOF
+'
+
+test_expect_success 'status with added file in submodule' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ git status >output &&
+ grep "modified: sub (modified content)" output
+'
+
+test_expect_success 'status with added file in submodule (porcelain)' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ git status --porcelain >output &&
+ diff output - <<-\EOF
+ M sub
+ EOF
+'
+
+test_expect_success 'status with untracked file in submodule' '
+ (cd sub && git reset --hard) &&
+ echo "content" >sub/new-file &&
+ git status >output &&
+ grep "modified: sub (untracked content)" output
+'
+
+test_expect_success 'status -uno with untracked file in submodule' '
+ git status -uno >output &&
+ grep "^nothing to commit" output
+'
+
+test_expect_success 'status with untracked file in submodule (porcelain)' '
+ git status --porcelain >output &&
+ diff output - <<-\EOF
+ M sub
+ EOF
+'
+
+test_expect_success 'status with added and untracked file in submodule' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ echo "content" >sub/new-file &&
+ git status >output &&
+ grep "modified: sub (modified content, untracked content)" output
+'
+
+test_expect_success 'status with added and untracked file in submodule (porcelain)' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ echo "content" >sub/new-file &&
+ git status --porcelain >output &&
+ diff output - <<-\EOF
+ M sub
+ EOF
+'
+
+test_expect_success 'status with modified file in modified submodule' '
+ (cd sub && git reset --hard) &&
+ rm sub/new-file &&
+ (cd sub && echo "next change" >foo && git commit -m "next change" foo) &&
+ echo "changed" >sub/foo &&
+ git status >output &&
+ grep "modified: sub (new commits, modified content)" output
+'
+
+test_expect_success 'status with modified file in modified submodule (porcelain)' '
+ (cd sub && git reset --hard) &&
+ echo "changed" >sub/foo &&
+ git status --porcelain >output &&
+ diff output - <<-\EOF
+ M sub
+ EOF
+'
+
+test_expect_success 'status with added file in modified submodule' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ git status >output &&
+ grep "modified: sub (new commits, modified content)" output
+'
+
+test_expect_success 'status with added file in modified submodule (porcelain)' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ git status --porcelain >output &&
+ diff output - <<-\EOF
+ M sub
+ EOF
+'
+
+test_expect_success 'status with untracked file in modified submodule' '
+ (cd sub && git reset --hard) &&
+ echo "content" >sub/new-file &&
+ git status >output &&
+ grep "modified: sub (new commits, untracked content)" output
+'
+
+test_expect_success 'status with untracked file in modified submodule (porcelain)' '
+ git status --porcelain >output &&
+ diff output - <<-\EOF
+ M sub
+ EOF
+'
+
+test_expect_success 'status with added and untracked file in modified submodule' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ echo "content" >sub/new-file &&
+ git status >output &&
+ grep "modified: sub (new commits, modified content, untracked content)" output
+'
+
+test_expect_success 'status with added and untracked file in modified submodule (porcelain)' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ echo "content" >sub/new-file &&
+ git status --porcelain >output &&
+ diff output - <<-\EOF
+ M sub
+ EOF
+'
+
+test_expect_success 'setup .git file for sub' '
+ (cd sub &&
+ rm -f new-file
+ REAL="$(pwd)/../.real" &&
+ mv .git "$REAL"
+ echo "gitdir: $REAL" >.git) &&
+ echo .real >>.gitignore &&
+ git commit -m "added .real to .gitignore" .gitignore
+'
+
+test_expect_success 'status with added file in modified submodule with .git file' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ git status >output &&
+ grep "modified: sub (new commits, modified content)" output
+'
+
+test_expect_success 'rm submodule contents' '
+ rm -rf sub/* sub/.git
+'
+
+test_expect_success 'status clean (empty submodule dir)' '
+ git status >output &&
+ grep "nothing to commit" output
+'
+
+test_expect_success 'status -a clean (empty submodule dir)' '
+ test_must_fail git commit --dry-run -a >output &&
+ grep "nothing to commit" output
+'
+
+test_done
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
new file mode 100755
index 0000000..da5bd3b
--- /dev/null
+++ b/t/t7507-commit-verbose.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='verbose commit template'
+. ./test-lib.sh
+
+cat >check-for-diff <<EOF
+#!$SHELL_PATH
+exec grep '^diff --git' "\$1"
+EOF
+chmod +x check-for-diff
+test_set_editor "$PWD/check-for-diff"
+
+cat >message <<'EOF'
+subject
+
+body
+EOF
+
+test_expect_success 'setup' '
+ echo content >file &&
+ git add file &&
+ git commit -F message
+'
+
+test_expect_success 'initial commit shows verbose diff' '
+ git commit --amend -v
+'
+
+test_expect_success 'second commit' '
+ echo content modified >file &&
+ git add file &&
+ git commit -F message
+'
+
+check_message() {
+ git log -1 --pretty=format:%s%n%n%b >actual &&
+ test_cmp "$1" actual
+}
+
+test_expect_success 'verbose diff is stripped out' '
+ git commit --amend -v &&
+ check_message message
+'
+
+test_expect_success 'verbose diff is stripped out (mnemonicprefix)' '
+ git config diff.mnemonicprefix true &&
+ git commit --amend -v &&
+ check_message message
+'
+
+cat >diff <<'EOF'
+This is an example commit message that contains a diff.
+
+diff --git c/file i/file
+new file mode 100644
+index 0000000..f95c11d
+--- /dev/null
++++ i/file
+@@ -0,0 +1 @@
++this is some content
+EOF
+
+test_expect_success 'diff in message is retained without -v' '
+ git commit --amend -F diff &&
+ check_message diff
+'
+
+test_expect_failure 'diff in message is retained with -v' '
+ git commit --amend -F diff -v &&
+ check_message diff
+'
+
+test_done
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
new file mode 100755
index 0000000..a72fe3a
--- /dev/null
+++ b/t/t7508-status.sh
@@ -0,0 +1,938 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='git status'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ : >tracked &&
+ : >modified &&
+ mkdir dir1 &&
+ : >dir1/tracked &&
+ : >dir1/modified &&
+ mkdir dir2 &&
+ : >dir1/tracked &&
+ : >dir1/modified &&
+ git add . &&
+
+ git status >output &&
+
+ test_tick &&
+ git commit -m initial &&
+ : >untracked &&
+ : >dir1/untracked &&
+ : >dir2/untracked &&
+ echo 1 >dir1/modified &&
+ echo 2 >dir2/modified &&
+ echo 3 >dir2/added &&
+ git add dir2/added
+'
+
+test_expect_success 'status (1)' '
+
+ grep "use \"git rm --cached <file>\.\.\.\" to unstage" output
+
+'
+
+cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+EOF
+
+test_expect_success 'status (2)' '
+
+ git status >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+# new file: dir2/added
+#
+# Changed but not updated:
+# modified: dir1/modified
+#
+# Untracked files:
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+EOF
+
+git config advice.statusHints false
+
+test_expect_success 'status (advice.statusHints false)' '
+
+ git status >output &&
+ test_cmp expect output
+
+'
+
+git config --unset advice.statusHints
+
+cat >expect <<\EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status -s' '
+
+ git status -s >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+## master
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status -s -b' '
+
+ git status -s -b >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files not listed (use -u option to show untracked files)
+EOF
+test_expect_success 'status -uno' '
+ mkdir dir3 &&
+ : >dir3/untracked1 &&
+ : >dir3/untracked2 &&
+ git status -uno >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'status (status.showUntrackedFiles no)' '
+ git config status.showuntrackedfiles no
+ git status >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# new file: dir2/added
+#
+# Changed but not updated:
+# modified: dir1/modified
+#
+# Untracked files not listed
+EOF
+git config advice.statusHints false
+test_expect_success 'status -uno (advice.statusHints false)' '
+ git status -uno >output &&
+ test_cmp expect output
+'
+git config --unset advice.statusHints
+
+cat >expect << EOF
+ M dir1/modified
+A dir2/added
+EOF
+test_expect_success 'status -s -uno' '
+ git config --unset status.showuntrackedfiles
+ git status -s -uno >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'status -s (status.showUntrackedFiles no)' '
+ git config status.showuntrackedfiles no
+ git status -s >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# dir3/
+# expect
+# output
+# untracked
+EOF
+test_expect_success 'status -unormal' '
+ git status -unormal >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'status (status.showUntrackedFiles normal)' '
+ git config status.showuntrackedfiles normal
+ git status >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? dir3/
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s -unormal' '
+ git config --unset status.showuntrackedfiles
+ git status -s -unormal >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'status -s (status.showUntrackedFiles normal)' '
+ git config status.showuntrackedfiles normal
+ git status -s >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# dir3/untracked1
+# dir3/untracked2
+# expect
+# output
+# untracked
+EOF
+test_expect_success 'status -uall' '
+ git status -uall >output &&
+ test_cmp expect output
+'
+test_expect_success 'status (status.showUntrackedFiles all)' '
+ git config status.showuntrackedfiles all
+ git status >output &&
+ rm -rf dir3 &&
+ git config --unset status.showuntrackedfiles &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s -uall' '
+ git config --unset status.showuntrackedfiles
+ git status -s -uall >output &&
+ test_cmp expect output
+'
+test_expect_success 'status -s (status.showUntrackedFiles all)' '
+ git config status.showuntrackedfiles all
+ git status -s >output &&
+ rm -rf dir3 &&
+ git config --unset status.showuntrackedfiles &&
+ test_cmp expect output
+'
+
+cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: ../dir2/added
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# untracked
+# ../dir2/modified
+# ../dir2/untracked
+# ../expect
+# ../output
+# ../untracked
+EOF
+
+test_expect_success 'status with relative paths' '
+
+ (cd dir1 && git status) >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ M modified
+A ../dir2/added
+?? untracked
+?? ../dir2/modified
+?? ../dir2/untracked
+?? ../expect
+?? ../output
+?? ../untracked
+EOF
+test_expect_success 'status -s with relative paths' '
+
+ (cd dir1 && git status -s) >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status --porcelain ignores relative paths setting' '
+
+ (cd dir1 && git status --porcelain) >output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'setup unique colors' '
+
+ git config status.color.untracked blue
+
+'
+
+cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# <GREEN>new file: dir2/added<RESET>
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# <RED>modified: dir1/modified<RESET>
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# <BLUE>dir1/untracked<RESET>
+# <BLUE>dir2/modified<RESET>
+# <BLUE>dir2/untracked<RESET>
+# <BLUE>expect<RESET>
+# <BLUE>output<RESET>
+# <BLUE>untracked<RESET>
+EOF
+
+test_expect_success 'status with color.ui' '
+
+ git config color.ui always &&
+ git status | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'status with color.status' '
+
+ git config --unset color.ui &&
+ git config color.status always &&
+ git status | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ <RED>M<RESET> dir1/modified
+<GREEN>A<RESET> dir2/added
+<BLUE>??<RESET> dir1/untracked
+<BLUE>??<RESET> dir2/modified
+<BLUE>??<RESET> dir2/untracked
+<BLUE>??<RESET> expect
+<BLUE>??<RESET> output
+<BLUE>??<RESET> untracked
+EOF
+
+test_expect_success 'status -s with color.ui' '
+
+ git config --unset color.status &&
+ git config color.ui always &&
+ git status -s | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'status -s with color.status' '
+
+ git config --unset color.ui &&
+ git config color.status always &&
+ git status -s | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+## <GREEN>master<RESET>
+ <RED>M<RESET> dir1/modified
+<GREEN>A<RESET> dir2/added
+<BLUE>??<RESET> dir1/untracked
+<BLUE>??<RESET> dir2/modified
+<BLUE>??<RESET> dir2/untracked
+<BLUE>??<RESET> expect
+<BLUE>??<RESET> output
+<BLUE>??<RESET> untracked
+EOF
+
+test_expect_success 'status -s -b with color.status' '
+
+ git status -s -b | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status --porcelain ignores color.ui' '
+
+ git config --unset color.status &&
+ git config color.ui always &&
+ git status --porcelain | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+test_expect_success 'status --porcelain ignores color.status' '
+
+ git config --unset color.ui &&
+ git config color.status always &&
+ git status --porcelain | test_decode_color >output &&
+ test_cmp expect output
+
+'
+
+# recover unconditionally from color tests
+git config --unset color.status
+git config --unset color.ui
+
+test_expect_success 'status --porcelain ignores -b' '
+
+ git status --porcelain -b >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+EOF
+
+
+test_expect_success 'status without relative paths' '
+
+ git config status.relativePaths false
+ (cd dir1 && git status) >output &&
+ test_cmp expect output
+
+'
+
+cat >expect <<\EOF
+ M dir1/modified
+A dir2/added
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+
+test_expect_success 'status -s without relative paths' '
+
+ (cd dir1 && git status -s) >output &&
+ test_cmp expect output
+
+'
+
+cat <<EOF >expect
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/
+# expect
+# output
+# untracked
+EOF
+test_expect_success 'dry-run of partial commit excluding new file in index' '
+ git commit --dry-run dir1/modified >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M dir1/modified
+EOF
+test_expect_success 'status refreshes the index' '
+ touch dir2/added &&
+ git status &&
+ git diff-files >output &&
+ test_cmp expect output
+'
+
+test_expect_success 'setup status submodule summary' '
+ test_create_repo sm && (
+ cd sm &&
+ >foo &&
+ git add foo &&
+ git commit -m "Add foo"
+ ) &&
+ git add sm
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+# new file: sm
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+EOF
+test_expect_success 'status submodule summary is disabled by default' '
+ git status >output &&
+ test_cmp expect output
+'
+
+# we expect the same as the previous test
+test_expect_success 'status --untracked-files=all does not show submodule' '
+ git status --untracked-files=all >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+A dir2/added
+A sm
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s submodule summary is disabled by default' '
+ git status -s >output &&
+ test_cmp expect output
+'
+
+# we expect the same as the previous test
+test_expect_success 'status -s --untracked-files=all does not show submodule' '
+ git status -s --untracked-files=all >output &&
+ test_cmp expect output
+'
+
+head=$(cd sm && git rev-parse --short=7 --verify HEAD)
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# new file: dir2/added
+# new file: sm
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Submodule changes to be committed:
+#
+# * sm 0000000...$head (1):
+# > Add foo
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+EOF
+test_expect_success 'status submodule summary' '
+ git config status.submodulesummary 10 &&
+ git status >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+A dir2/added
+A sm
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s submodule summary' '
+ git status -s >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+test_expect_success 'status submodule summary (clean submodule)' '
+ git commit -m "commit submodule" &&
+ git config status.submodulesummary 10 &&
+ test_must_fail git commit --dry-run >output &&
+ test_cmp expect output &&
+ git status >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+ M dir1/modified
+?? dir1/untracked
+?? dir2/modified
+?? dir2/untracked
+?? expect
+?? output
+?? untracked
+EOF
+test_expect_success 'status -s submodule summary (clean submodule)' '
+ git status -s >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD^1 <file>..." to unstage)
+#
+# new file: dir2/added
+# new file: sm
+#
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Submodule changes to be committed:
+#
+# * sm 0000000...$head (1):
+# > Add foo
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+EOF
+test_expect_success 'commit --dry-run submodule summary (--amend)' '
+ git config status.submodulesummary 10 &&
+ git commit --dry-run --amend >output &&
+ test_cmp expect output
+'
+
+test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
+ (
+ chmod a-w .git &&
+ # make dir1/tracked stat-dirty
+ >dir1/tracked1 && mv -f dir1/tracked1 dir1/tracked &&
+ git status -s >output &&
+ ! grep dir1/tracked output &&
+ # make sure "status" succeeded without writing index out
+ git diff-files | grep dir1/tracked
+ )
+ status=$?
+ chmod 775 .git
+ (exit $status)
+'
+
+cat > expect << EOF
+# On branch master
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' '
+ echo modified > sm/untracked &&
+ git status --ignore-submodules=untracked > output &&
+ test_cmp expect output
+'
+
+test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '
+ git status --ignore-submodules=dirty > output &&
+ test_cmp expect output
+'
+
+test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' '
+ echo modified > sm/foo &&
+ git status --ignore-submodules=dirty > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+# On branch master
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+# (commit or discard the untracked or modified content in submodules)
+#
+# modified: dir1/modified
+# modified: sm (modified content)
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" '
+ git status --ignore-submodules=untracked > output &&
+ test_cmp expect output
+'
+
+head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD)
+
+cat > expect << EOF
+# On branch master
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+# modified: sm (new commits)
+#
+# Submodules changed but not updated:
+#
+# * sm $head...$head2 (1):
+# > 2nd commit
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" '
+ git status --ignore-submodules=untracked > output &&
+ test_cmp expect output
+'
+
+test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" '
+ git status --ignore-submodules=dirty > output &&
+ test_cmp expect output
+'
+
+cat > expect << EOF
+# On branch master
+# Changed but not updated:
+# (use "git add <file>..." to update what will be committed)
+# (use "git checkout -- <file>..." to discard changes in working directory)
+#
+# modified: dir1/modified
+#
+# Untracked files:
+# (use "git add <file>..." to include in what will be committed)
+#
+# dir1/untracked
+# dir2/modified
+# dir2/untracked
+# expect
+# output
+# untracked
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success "--ignore-submodules=all suppresses submodule summary" '
+ git status --ignore-submodules=all > output &&
+ test_cmp expect output
+'
+
+test_done
diff --git a/t/t7509-commit.sh b/t/t7509-commit.sh
new file mode 100755
index 0000000..3ea33db
--- /dev/null
+++ b/t/t7509-commit.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Erick Mattos
+#
+
+test_description='git commit --reset-author'
+
+. ./test-lib.sh
+
+author_header () {
+ git cat-file commit "$1" |
+ sed -n -e '/^$/q' -e '/^author /p'
+}
+
+message_body () {
+ git cat-file commit "$1" |
+ sed -e '1,/^$/d'
+}
+
+test_expect_success '-C option copies authorship and message' '
+ echo "Initial" >foo &&
+ git add foo &&
+ test_tick &&
+ git commit -m "Initial Commit" --author Frigate\ \<flying@over.world\> &&
+ git tag Initial &&
+ echo "Test 1" >>foo &&
+ test_tick &&
+ git commit -a -C Initial &&
+ author_header Initial >expect &&
+ author_header HEAD >actual &&
+ test_cmp expect actual &&
+
+ message_body Initial >expect &&
+ message_body HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-C option copies only the message with --reset-author' '
+ echo "Test 2" >>foo &&
+ test_tick &&
+ git commit -a -C Initial --reset-author &&
+ echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+ author_header HEAD >actual
+ test_cmp expect actual &&
+
+ message_body Initial >expect &&
+ message_body HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-c option copies authorship and message' '
+ echo "Test 3" >>foo &&
+ test_tick &&
+ EDITOR=: VISUAL=: git commit -a -c Initial &&
+ author_header Initial >expect &&
+ author_header HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '-c option copies only the message with --reset-author' '
+ echo "Test 4" >>foo &&
+ test_tick &&
+ EDITOR=: VISUAL=: git commit -a -c Initial --reset-author &&
+ echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+ author_header HEAD >actual &&
+ test_cmp expect actual &&
+
+ message_body Initial >expect &&
+ message_body HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--amend option copies authorship' '
+ git checkout Initial &&
+ echo "Test 5" >>foo &&
+ test_tick &&
+ git commit -a --amend -m "amend test" &&
+ author_header Initial >expect &&
+ author_header HEAD >actual &&
+
+ echo "amend test" >expect &&
+ message_body HEAD >actual &&
+ test_cmp expect actual
+'
+
+sha1_file() {
+ echo "$*" | sed "s#..#.git/objects/&/#"
+}
+remove_object() {
+ rm -f $(sha1_file "$*")
+}
+no_reflog() {
+ cp .git/config .git/config.saved &&
+ echo "[core] logallrefupdates = false" >>.git/config &&
+ test_when_finished "mv -f .git/config.saved .git/config" &&
+
+ if test -e .git/logs
+ then
+ mv .git/logs . &&
+ test_when_finished "mv logs .git/"
+ fi
+}
+
+test_expect_success '--amend option with empty author' '
+ git cat-file commit Initial >tmp &&
+ sed "s/author [^<]* </author </" tmp >empty-author &&
+ no_reflog &&
+ sha=$(git hash-object -t commit -w empty-author) &&
+ test_when_finished "remove_object $sha" &&
+ git checkout $sha &&
+ test_when_finished "git checkout Initial" &&
+ echo "Empty author test" >>foo &&
+ test_tick &&
+ ! git commit -a -m "empty author" --amend 2>err &&
+ grep "empty ident" err
+'
+
+test_expect_success '--amend option with missing author' '
+ git cat-file commit Initial >tmp &&
+ sed "s/author [^<]* </author </" tmp >malformed &&
+ no_reflog &&
+ sha=$(git hash-object -t commit -w malformed) &&
+ test_when_finished "remove_object $sha" &&
+ git checkout $sha &&
+ test_when_finished "git checkout Initial" &&
+ echo "Missing author test" >>foo &&
+ test_tick &&
+ ! git commit -a -m "malformed author" --amend 2>err &&
+ grep "empty ident" err
+'
+
+test_expect_success '--reset-author makes the commit ours even with --amend option' '
+ git checkout Initial &&
+ echo "Test 6" >>foo &&
+ test_tick &&
+ git commit -a --reset-author -m "Changed again" --amend &&
+ echo "author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> $GIT_AUTHOR_DATE" >expect &&
+ author_header HEAD >actual &&
+ test_cmp expect actual &&
+
+ echo "Changed again" >expect &&
+ message_body HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success '--reset-author and --author are mutually exclusive' '
+ git checkout Initial &&
+ echo "Test 7" >>foo &&
+ test_tick &&
+ test_must_fail git commit -a --reset-author --author="Xyzzy <frotz@nitfol.xz>"
+'
+
+test_expect_success '--reset-author should be rejected without -c/-C/--amend' '
+ git checkout Initial &&
+ echo "Test 7" >>foo &&
+ test_tick &&
+ test_must_fail git commit -a --reset-author -m done
+'
+
+test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
new file mode 100755
index 0000000..cde8390
--- /dev/null
+++ b/t/t7600-merge.sh
@@ -0,0 +1,603 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Lars Hjemli
+#
+
+test_description='git merge
+
+Testing basic merge operations/option parsing.'
+
+. ./test-lib.sh
+
+cat >file <<EOF
+1
+2
+3
+4
+5
+6
+7
+8
+9
+EOF
+
+cat >file.1 <<EOF
+1 X
+2
+3
+4
+5
+6
+7
+8
+9
+EOF
+
+cat >file.5 <<EOF
+1
+2
+3
+4
+5 X
+6
+7
+8
+9
+EOF
+
+cat >file.9 <<EOF
+1
+2
+3
+4
+5
+6
+7
+8
+9 X
+EOF
+
+cat >result.1 <<EOF
+1 X
+2
+3
+4
+5
+6
+7
+8
+9
+EOF
+
+cat >result.1-5 <<EOF
+1 X
+2
+3
+4
+5 X
+6
+7
+8
+9
+EOF
+
+cat >result.1-5-9 <<EOF
+1 X
+2
+3
+4
+5 X
+6
+7
+8
+9 X
+EOF
+
+create_merge_msgs() {
+ echo "Merge commit 'c2'" >msg.1-5 &&
+ echo "Merge commit 'c2'; commit 'c3'" >msg.1-5-9 &&
+ echo "Squashed commit of the following:" >squash.1 &&
+ echo >>squash.1 &&
+ git log --no-merges ^HEAD c1 >>squash.1 &&
+ echo "Squashed commit of the following:" >squash.1-5 &&
+ echo >>squash.1-5 &&
+ git log --no-merges ^HEAD c2 >>squash.1-5 &&
+ echo "Squashed commit of the following:" >squash.1-5-9 &&
+ echo >>squash.1-5-9 &&
+ git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 &&
+ echo > msg.nolog &&
+ echo "* commit 'c3':" >msg.log &&
+ echo " commit 3" >>msg.log &&
+ echo >>msg.log
+}
+
+verify_diff() {
+ if ! test_cmp "$1" "$2"
+ then
+ echo "$3"
+ false
+ fi
+}
+
+verify_merge() {
+ verify_diff "$2" "$1" "[OOPS] bad merge result" &&
+ if test $(git ls-files -u | wc -l) -gt 0
+ then
+ echo "[OOPS] unmerged files"
+ false
+ fi &&
+ if test_must_fail git diff --exit-code
+ then
+ echo "[OOPS] working tree != index"
+ false
+ fi &&
+ if test -n "$3"
+ then
+ git show -s --pretty=format:%s HEAD >msg.act &&
+ verify_diff "$3" msg.act "[OOPS] bad merge message"
+ fi
+}
+
+verify_head() {
+ if test "$1" != "$(git rev-parse HEAD)"
+ then
+ echo "[OOPS] HEAD != $1"
+ false
+ fi
+}
+
+verify_parents() {
+ i=1
+ while test $# -gt 0
+ do
+ if test "$1" != "$(git rev-parse HEAD^$i)"
+ then
+ echo "[OOPS] HEAD^$i != $1"
+ return 1
+ fi
+ i=$(expr $i + 1)
+ shift
+ done
+}
+
+verify_mergeheads() {
+ i=1
+ if ! test -f .git/MERGE_HEAD
+ then
+ echo "[OOPS] MERGE_HEAD is missing"
+ false
+ fi &&
+ while test $# -gt 0
+ do
+ head=$(head -n $i .git/MERGE_HEAD | sed -ne \$p)
+ if test "$1" != "$head"
+ then
+ echo "[OOPS] MERGE_HEAD $i != $1"
+ return 1
+ fi
+ i=$(expr $i + 1)
+ shift
+ done
+}
+
+verify_no_mergehead() {
+ if test -f .git/MERGE_HEAD
+ then
+ echo "[OOPS] MERGE_HEAD exists"
+ false
+ fi
+}
+
+
+test_expect_success 'setup' '
+ git add file &&
+ test_tick &&
+ git commit -m "commit 0" &&
+ git tag c0 &&
+ c0=$(git rev-parse HEAD) &&
+ cp file.1 file &&
+ git add file &&
+ test_tick &&
+ git commit -m "commit 1" &&
+ git tag c1 &&
+ c1=$(git rev-parse HEAD) &&
+ git reset --hard "$c0" &&
+ cp file.5 file &&
+ git add file &&
+ test_tick &&
+ git commit -m "commit 2" &&
+ git tag c2 &&
+ c2=$(git rev-parse HEAD) &&
+ git reset --hard "$c0" &&
+ cp file.9 file &&
+ git add file &&
+ test_tick &&
+ git commit -m "commit 3" &&
+ git tag c3 &&
+ c3=$(git rev-parse HEAD)
+ git reset --hard "$c0" &&
+ create_merge_msgs
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'test option parsing' '
+ test_must_fail git merge -$ c1 &&
+ test_must_fail git merge --no-such c1 &&
+ 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
+'
+
+test_expect_success 'reject non-strategy with a git-merge-foo name' '
+ test_must_fail git merge -s index c1
+'
+
+test_expect_success 'merge c0 with c1' '
+ git reset --hard c0 &&
+ git merge c1 &&
+ verify_merge file result.1 &&
+ verify_head "$c1"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c0 with c1 with --ff-only' '
+ git reset --hard c0 &&
+ git merge --ff-only c1 &&
+ git merge --ff-only HEAD c0 c1 &&
+ verify_merge file result.1 &&
+ verify_head "$c1"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2' '
+ git reset --hard c1 &&
+ test_tick &&
+ git merge c2 &&
+ verify_merge file result.1-5 msg.1-5 &&
+ verify_parents $c1 $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 and c3' '
+ git reset --hard c1 &&
+ test_tick &&
+ git merge c2 c3 &&
+ verify_merge file result.1-5-9 msg.1-5-9 &&
+ verify_parents $c1 $c2 $c3
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'failing merges with --ff-only' '
+ git reset --hard c1 &&
+ test_tick &&
+ test_must_fail git merge --ff-only c2 &&
+ test_must_fail git merge --ff-only c3 &&
+ test_must_fail git merge --ff-only c2 c3
+'
+
+test_expect_success 'merge c0 with c1 (no-commit)' '
+ git reset --hard c0 &&
+ git merge --no-commit c1 &&
+ verify_merge file result.1 &&
+ verify_head $c1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (no-commit)' '
+ git reset --hard c1 &&
+ git merge --no-commit c2 &&
+ verify_merge file result.1-5 &&
+ verify_head $c1 &&
+ verify_mergeheads $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 and c3 (no-commit)' '
+ git reset --hard c1 &&
+ git merge --no-commit c2 c3 &&
+ verify_merge file result.1-5-9 &&
+ verify_head $c1 &&
+ verify_mergeheads $c2 $c3
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c0 with c1 (squash)' '
+ git reset --hard c0 &&
+ git merge --squash c1 &&
+ verify_merge file result.1 &&
+ verify_head $c0 &&
+ verify_no_mergehead &&
+ verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c0 with c1 (squash, ff-only)' '
+ git reset --hard c0 &&
+ git merge --squash --ff-only c1 &&
+ verify_merge file result.1 &&
+ verify_head $c0 &&
+ verify_no_mergehead &&
+ verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (squash)' '
+ git reset --hard c1 &&
+ git merge --squash c2 &&
+ verify_merge file result.1-5 &&
+ verify_head $c1 &&
+ verify_no_mergehead &&
+ verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
+ git reset --hard c1 &&
+ test_must_fail git merge --squash --ff-only c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 and c3 (squash)' '
+ git reset --hard c1 &&
+ git merge --squash c2 c3 &&
+ verify_merge file result.1-5-9 &&
+ verify_head $c1 &&
+ verify_no_mergehead &&
+ verify_diff squash.1-5-9 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (no-commit in config)' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "--no-commit" &&
+ git merge c2 &&
+ verify_merge file result.1-5 &&
+ verify_head $c1 &&
+ verify_mergeheads $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (squash in config)' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "--squash" &&
+ git merge c2 &&
+ verify_merge file result.1-5 &&
+ verify_head $c1 &&
+ verify_no_mergehead &&
+ verify_diff squash.1-5 .git/SQUASH_MSG "[OOPS] bad squash message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'override config option -n with --summary' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "-n" &&
+ test_tick &&
+ git merge --summary c2 >diffstat.txt &&
+ verify_merge file result.1-5 msg.1-5 &&
+ verify_parents $c1 $c2 &&
+ if ! grep "^ file | *2 +-$" diffstat.txt
+ then
+ echo "[OOPS] diffstat was not generated with --summary"
+ false
+ fi
+'
+
+test_expect_success 'override config option -n with --stat' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "-n" &&
+ test_tick &&
+ git merge --stat c2 >diffstat.txt &&
+ verify_merge file result.1-5 msg.1-5 &&
+ verify_parents $c1 $c2 &&
+ if ! grep "^ file | *2 +-$" diffstat.txt
+ then
+ echo "[OOPS] diffstat was not generated with --stat"
+ false
+ fi
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'override config option --stat' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "--stat" &&
+ test_tick &&
+ git merge -n c2 >diffstat.txt &&
+ verify_merge file result.1-5 msg.1-5 &&
+ verify_parents $c1 $c2 &&
+ if grep "^ file | *2 +-$" diffstat.txt
+ then
+ echo "[OOPS] diffstat was generated"
+ false
+ fi
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (override --no-commit)' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "--no-commit" &&
+ test_tick &&
+ git merge --commit c2 &&
+ verify_merge file result.1-5 msg.1-5 &&
+ verify_parents $c1 $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c2 (override --squash)' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "--squash" &&
+ test_tick &&
+ git merge --no-squash c2 &&
+ verify_merge file result.1-5 msg.1-5 &&
+ verify_parents $c1 $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c0 with c1 (no-ff)' '
+ git reset --hard c0 &&
+ git config branch.master.mergeoptions "" &&
+ test_tick &&
+ git merge --no-ff c1 &&
+ verify_merge file result.1 &&
+ verify_parents $c0 $c1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'combining --squash and --no-ff is refused' '
+ test_must_fail git merge --squash --no-ff c1 &&
+ test_must_fail git merge --no-ff --squash c1
+'
+
+test_expect_success 'combining --ff-only and --no-ff is refused' '
+ test_must_fail git merge --ff-only --no-ff c1 &&
+ test_must_fail git merge --no-ff --ff-only c1
+'
+
+test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
+ git reset --hard c0 &&
+ git config branch.master.mergeoptions "--no-ff" &&
+ git merge --ff c1 &&
+ verify_merge file result.1 &&
+ verify_head $c1
+'
+
+test_expect_success 'merge log message' '
+ git reset --hard c0 &&
+ git merge --no-log c2 &&
+ git show -s --pretty=format:%b HEAD >msg.act &&
+ verify_diff msg.nolog msg.act "[OOPS] bad merge log message" &&
+
+ git merge --log c3 &&
+ git show -s --pretty=format:%b HEAD >msg.act &&
+ verify_diff msg.log msg.act "[OOPS] bad merge log message" &&
+
+ git reset --hard HEAD^ &&
+ git config merge.log yes &&
+ git merge c3 &&
+ git show -s --pretty=format:%b HEAD >msg.act &&
+ verify_diff msg.log msg.act "[OOPS] bad merge log message"
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c0, c2, c0, and c1' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "" &&
+ test_tick &&
+ git merge c0 c2 c0 c1 &&
+ verify_merge file result.1-5 &&
+ verify_parents $c1 $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c0, c2, c0, and c1' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "" &&
+ test_tick &&
+ git merge c0 c2 c0 c1 &&
+ verify_merge file result.1-5 &&
+ verify_parents $c1 $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge c1 with c1 and c2' '
+ git reset --hard c1 &&
+ git config branch.master.mergeoptions "" &&
+ test_tick &&
+ git merge c1 c2 &&
+ verify_merge file result.1-5 &&
+ verify_parents $c1 $c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge fast-forward in a dirty tree' '
+ git reset --hard c0 &&
+ mv file file1 &&
+ cat file1 >file &&
+ rm -f file1 &&
+ git merge c2
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'in-index merge' '
+ git reset --hard c0 &&
+ git merge --no-ff -s resolve c1 > out &&
+ grep "Wonderful." out &&
+ verify_parents $c0 $c1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'refresh the index before merging' '
+ git reset --hard c1 &&
+ cp file file.n && mv -f file.n file &&
+ git merge c3
+'
+
+cat >expected <<EOF
+Merge branch 'c5' (early part)
+EOF
+
+test_expect_success 'merge early part of c2' '
+ git reset --hard c3 &&
+ echo c4 > c4.c &&
+ git add c4.c &&
+ git commit -m c4 &&
+ git tag c4 &&
+ echo c5 > c5.c &&
+ git add c5.c &&
+ git commit -m c5 &&
+ git tag c5 &&
+ git reset --hard c3 &&
+ echo c6 > c6.c &&
+ git add c6.c &&
+ git commit -m c6 &&
+ git tag c6 &&
+ git merge c5~1 &&
+ git show -s --pretty=format:%s HEAD > actual &&
+ test_cmp actual expected
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge --no-ff --no-commit && commit' '
+ git reset --hard c0 &&
+ git merge --no-ff --no-commit c1 &&
+ EDITOR=: git commit &&
+ verify_parents $c0 $c1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'amending no-ff merge commit' '
+ EDITOR=: git commit --amend &&
+ verify_parents $c0 $c1
+'
+
+test_debug 'gitk --all'
+
+test_done
diff --git a/t/t7601-merge-pull-config.sh b/t/t7601-merge-pull-config.sh
new file mode 100755
index 0000000..7ba94ea
--- /dev/null
+++ b/t/t7601-merge-pull-config.sh
@@ -0,0 +1,156 @@
+#!/bin/sh
+
+test_description='git merge
+
+Testing pull.* configuration parsing.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo c0 >c0.c &&
+ git add c0.c &&
+ git commit -m c0 &&
+ git tag c0 &&
+ echo c1 >c1.c &&
+ git add c1.c &&
+ git commit -m c1 &&
+ git tag c1 &&
+ git reset --hard c0 &&
+ echo c2 >c2.c &&
+ git add c2.c &&
+ git commit -m c2 &&
+ git tag c2 &&
+ git reset --hard c0 &&
+ echo c3 >c3.c &&
+ git add c3.c &&
+ git commit -m c3 &&
+ git tag c3
+'
+
+test_expect_success 'merge c1 with c2' '
+ git reset --hard c1 &&
+ test -f c0.c &&
+ test -f c1.c &&
+ test ! -f c2.c &&
+ test ! -f c3.c &&
+ git merge c2 &&
+ test -f c1.c &&
+ test -f c2.c
+'
+
+test_expect_success 'merge c1 with c2 (ours in pull.twohead)' '
+ git reset --hard c1 &&
+ git config pull.twohead ours &&
+ git merge c2 &&
+ test -f c1.c &&
+ ! test -f c2.c
+'
+
+test_expect_success 'merge c1 with c2 and c3 (recursive in pull.octopus)' '
+ git reset --hard c1 &&
+ git config pull.octopus "recursive" &&
+ test_must_fail git merge c2 c3 &&
+ test "$(git rev-parse c1)" = "$(git rev-parse HEAD)"
+'
+
+test_expect_success 'merge c1 with c2 and c3 (recursive and octopus in pull.octopus)' '
+ git reset --hard c1 &&
+ git config pull.octopus "recursive octopus" &&
+ git merge c2 c3 &&
+ test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+ test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+ test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
+ git diff --exit-code &&
+ test -f c0.c &&
+ test -f c1.c &&
+ test -f c2.c &&
+ test -f c3.c
+'
+
+conflict_count()
+{
+ {
+ git diff-files --name-only
+ git ls-files --unmerged
+ } | wc -l
+}
+
+# c4 - c5
+# \ c6
+#
+# There are two conflicts here:
+#
+# 1) Because foo.c is renamed to bar.c, recursive will handle this,
+# resolve won't.
+#
+# 2) One in conflict.c and that will always fail.
+
+test_expect_success 'setup conflicted merge' '
+ git reset --hard c0 &&
+ echo A >conflict.c &&
+ git add conflict.c &&
+ echo contents >foo.c &&
+ git add foo.c &&
+ git commit -m c4 &&
+ git tag c4 &&
+ echo B >conflict.c &&
+ git add conflict.c &&
+ git mv foo.c bar.c &&
+ git commit -m c5 &&
+ git tag c5 &&
+ git reset --hard c4 &&
+ echo C >conflict.c &&
+ git add conflict.c &&
+ echo secondline >> foo.c &&
+ git add foo.c &&
+ git commit -m c6 &&
+ git tag c6
+'
+
+# First do the merge with resolve and recursive then verify that
+# recusive is choosen.
+
+test_expect_success 'merge picks up the best result' '
+ git config --unset-all pull.twohead &&
+ git reset --hard c5 &&
+ git merge -s resolve c6
+ resolve_count=$(conflict_count) &&
+ git reset --hard c5 &&
+ git merge -s recursive c6
+ recursive_count=$(conflict_count) &&
+ git reset --hard c5 &&
+ git merge -s recursive -s resolve c6
+ auto_count=$(conflict_count) &&
+ test $auto_count = $recursive_count &&
+ test $auto_count != $resolve_count
+'
+
+test_expect_success 'merge picks up the best result (from config)' '
+ git config pull.twohead "recursive resolve" &&
+ git reset --hard c5 &&
+ git merge -s resolve c6
+ resolve_count=$(conflict_count) &&
+ git reset --hard c5 &&
+ git merge -s recursive c6
+ recursive_count=$(conflict_count) &&
+ git reset --hard c5 &&
+ git merge c6
+ auto_count=$(conflict_count) &&
+ test $auto_count = $recursive_count &&
+ test $auto_count != $resolve_count
+'
+
+test_expect_success 'merge errors out on invalid strategy' '
+ git config pull.twohead "foobar" &&
+ git reset --hard c5 &&
+ test_must_fail git merge c6
+'
+
+test_expect_success 'merge errors out on invalid strategy' '
+ git config --unset-all pull.twohead &&
+ git reset --hard c5 &&
+ test_must_fail git merge -s "resolve recursive" c6
+'
+
+test_done
diff --git a/t/t7602-merge-octopus-many.sh b/t/t7602-merge-octopus-many.sh
new file mode 100755
index 0000000..2746169
--- /dev/null
+++ b/t/t7602-merge-octopus-many.sh
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='git merge
+
+Testing octopus merge with more than 25 refs.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo c0 > c0.c &&
+ git add c0.c &&
+ git commit -m c0 &&
+ git tag c0 &&
+ i=1 &&
+ while test $i -le 30
+ do
+ git reset --hard c0 &&
+ echo c$i > c$i.c &&
+ git add c$i.c &&
+ git commit -m c$i &&
+ git tag c$i &&
+ i=`expr $i + 1` || return 1
+ done
+'
+
+test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
+ git reset --hard c1 &&
+ i=2 &&
+ refs="" &&
+ while test $i -le 30
+ do
+ refs="$refs c$i"
+ i=`expr $i + 1`
+ done
+ git merge $refs &&
+ test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+ i=1 &&
+ while test $i -le 30
+ do
+ test "$(git rev-parse c$i)" = "$(git rev-parse HEAD^$i)" &&
+ i=`expr $i + 1` || return 1
+ done &&
+ git diff --exit-code &&
+ i=1 &&
+ while test $i -le 30
+ do
+ test -f c$i.c &&
+ i=`expr $i + 1` || return 1
+ done
+'
+
+cat >expected <<\EOF
+Trying simple merge with c2
+Trying simple merge with c3
+Trying simple merge with c4
+Merge made by octopus.
+ c2.c | 1 +
+ c3.c | 1 +
+ c4.c | 1 +
+ 3 files changed, 3 insertions(+), 0 deletions(-)
+ create mode 100644 c2.c
+ create mode 100644 c3.c
+ create mode 100644 c4.c
+EOF
+
+test_expect_success 'merge output uses pretty names' '
+ git reset --hard c1 &&
+ git merge c2 c3 c4 >actual &&
+ test_cmp actual expected
+'
+
+cat >expected <<\EOF
+Already up-to-date with c4
+Trying simple merge with c5
+Merge made by octopus.
+ c5.c | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ create mode 100644 c5.c
+EOF
+
+test_expect_success 'merge up-to-date output uses pretty names' '
+ git merge c4 c5 >actual &&
+ test_cmp actual expected
+'
+
+cat >expected <<\EOF
+Fast-forwarding to: c1
+Trying simple merge with c2
+Merge made by octopus.
+ c1.c | 1 +
+ c2.c | 1 +
+ 2 files changed, 2 insertions(+), 0 deletions(-)
+ create mode 100644 c1.c
+ create mode 100644 c2.c
+EOF
+
+test_expect_success 'merge fast-forward output uses pretty names' '
+ git reset --hard c0 &&
+ git merge c1 c2 >actual &&
+ test_cmp actual expected
+'
+
+test_done
diff --git a/t/t7603-merge-reduce-heads.sh b/t/t7603-merge-reduce-heads.sh
new file mode 100755
index 0000000..7e17eb4
--- /dev/null
+++ b/t/t7603-merge-reduce-heads.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+test_description='git merge
+
+Testing octopus merge when reducing parents to independent branches.'
+
+. ./test-lib.sh
+
+# 0 - 1
+# \ 2
+# \ 3
+# \ 4 - 5
+#
+# So 1, 2, 3 and 5 should be kept, 4 should be avoided.
+
+test_expect_success 'setup' '
+ echo c0 > c0.c &&
+ git add c0.c &&
+ git commit -m c0 &&
+ git tag c0 &&
+ echo c1 > c1.c &&
+ git add c1.c &&
+ git commit -m c1 &&
+ git tag c1 &&
+ git reset --hard c0 &&
+ echo c2 > c2.c &&
+ git add c2.c &&
+ git commit -m c2 &&
+ git tag c2 &&
+ git reset --hard c0 &&
+ echo c3 > c3.c &&
+ git add c3.c &&
+ git commit -m c3 &&
+ git tag c3 &&
+ git reset --hard c0 &&
+ echo c4 > c4.c &&
+ git add c4.c &&
+ git commit -m c4 &&
+ git tag c4 &&
+ echo c5 > c5.c &&
+ git add c5.c &&
+ git commit -m c5 &&
+ git tag c5
+'
+
+test_expect_success 'merge c1 with c2, c3, c4, c5' '
+ git reset --hard c1 &&
+ git merge c2 c3 c4 c5 &&
+ test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+ test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+ test "$(git rev-parse c3)" = "$(git rev-parse HEAD^3)" &&
+ test "$(git rev-parse c5)" = "$(git rev-parse HEAD^4)" &&
+ git diff --exit-code &&
+ test -f c0.c &&
+ test -f c1.c &&
+ test -f c2.c &&
+ test -f c3.c &&
+ test -f c4.c &&
+ test -f c5.c
+'
+
+test_expect_success 'setup' '
+ for i in A B C D E
+ do
+ echo $i > $i.c &&
+ git add $i.c &&
+ git commit -m $i &&
+ git tag $i
+ done &&
+ git reset --hard A &&
+ for i in F G H I
+ do
+ echo $i > $i.c &&
+ git add $i.c &&
+ git commit -m $i &&
+ git tag $i
+ done
+'
+
+test_expect_success 'merge E and I' '
+ git reset --hard A &&
+ git merge E I
+'
+
+test_expect_success 'verify merge result' '
+ test $(git rev-parse HEAD^1) = $(git rev-parse E) &&
+ test $(git rev-parse HEAD^2) = $(git rev-parse I)
+'
+
+test_expect_success 'add conflicts' '
+ git reset --hard E &&
+ echo foo > file.c &&
+ git add file.c &&
+ git commit -m E2 &&
+ git tag E2 &&
+ git reset --hard I &&
+ echo bar >file.c &&
+ git add file.c &&
+ git commit -m I2 &&
+ git tag I2
+'
+
+test_expect_success 'merge E2 and I2, causing a conflict and resolve it' '
+ git reset --hard A &&
+ test_must_fail git merge E2 I2 &&
+ echo baz > file.c &&
+ git add file.c &&
+ git commit -m "resolve conflict"
+'
+
+test_expect_success 'verify merge result' '
+ test $(git rev-parse HEAD^1) = $(git rev-parse E2) &&
+ test $(git rev-parse HEAD^2) = $(git rev-parse I2)
+'
+test_done
diff --git a/t/t7604-merge-custom-message.sh b/t/t7604-merge-custom-message.sh
new file mode 100755
index 0000000..9114785
--- /dev/null
+++ b/t/t7604-merge-custom-message.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='git merge
+
+Testing merge when using a custom message for the merge commit.'
+
+. ./test-lib.sh
+
+create_merge_msgs() {
+ echo >exp.subject "custom message"
+
+ cp exp.subject exp.log &&
+ echo >>exp.log "" &&
+ echo >>exp.log "* commit 'c2':" &&
+ echo >>exp.log " c2"
+}
+
+test_expect_success 'setup' '
+ echo c0 > c0.c &&
+ git add c0.c &&
+ git commit -m c0 &&
+ git tag c0 &&
+ echo c1 > c1.c &&
+ git add c1.c &&
+ git commit -m c1 &&
+ git tag c1 &&
+ git reset --hard c0 &&
+ echo c2 > c2.c &&
+ git add c2.c &&
+ git commit -m c2 &&
+ git tag c2 &&
+ create_merge_msgs
+'
+
+
+test_expect_success 'merge c2 with a custom message' '
+ git reset --hard c1 &&
+ git merge -m "$(cat exp.subject)" c2 &&
+ git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+ test_cmp exp.subject actual
+'
+
+test_expect_success 'merge --log appends to custom message' '
+ git reset --hard c1 &&
+ git merge --log -m "$(cat exp.subject)" c2 &&
+ git cat-file commit HEAD | sed -e "1,/^$/d" >actual &&
+ test_cmp exp.log actual
+'
+
+test_done
diff --git a/t/t7605-merge-resolve.sh b/t/t7605-merge-resolve.sh
new file mode 100755
index 0000000..0cb9d11
--- /dev/null
+++ b/t/t7605-merge-resolve.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='git merge
+
+Testing the resolve strategy.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo c0 > c0.c &&
+ git add c0.c &&
+ git commit -m c0 &&
+ git tag c0 &&
+ echo c1 > c1.c &&
+ git add c1.c &&
+ git commit -m c1 &&
+ git tag c1 &&
+ git reset --hard c0 &&
+ echo c2 > c2.c &&
+ git add c2.c &&
+ git commit -m c2 &&
+ git tag c2 &&
+ git reset --hard c0 &&
+ echo c3 > c2.c &&
+ git add c2.c &&
+ git commit -m c3 &&
+ git tag c3
+'
+
+test_expect_success 'merge c1 to c2' '
+ git reset --hard c1 &&
+ git merge -s resolve c2 &&
+ test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+ test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+ git diff --exit-code &&
+ test -f c0.c &&
+ test -f c1.c &&
+ test -f c2.c &&
+ test 3 = $(git ls-tree -r HEAD | wc -l) &&
+ test 3 = $(git ls-files | wc -l)
+'
+
+test_expect_success 'merge c2 to c3 (fails)' '
+ git reset --hard c2 &&
+ test_must_fail git merge -s resolve c3
+'
+test_done
diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh
new file mode 100755
index 0000000..52a451d
--- /dev/null
+++ b/t/t7606-merge-custom.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='git merge
+
+Testing a custom strategy.'
+
+. ./test-lib.sh
+
+cat >git-merge-theirs <<EOF
+#!$SHELL_PATH
+eval git read-tree --reset -u \\\$\$#
+EOF
+chmod +x git-merge-theirs
+PATH=.:$PATH
+export PATH
+
+test_expect_success 'setup' '
+ echo c0 >c0.c &&
+ git add c0.c &&
+ git commit -m c0 &&
+ git tag c0 &&
+ echo c1 >c1.c &&
+ git add c1.c &&
+ git commit -m c1 &&
+ git tag c1 &&
+ git reset --hard c0 &&
+ echo c1c1 >c1.c &&
+ echo c2 >c2.c &&
+ git add c1.c c2.c &&
+ git commit -m c2 &&
+ git tag c2
+'
+
+test_expect_success 'merge c2 with a custom strategy' '
+ git reset --hard c1 &&
+ git merge -s theirs c2 &&
+ test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+ test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+ test "$(git rev-parse c2^{tree})" = "$(git rev-parse HEAD^{tree})" &&
+ git diff --exit-code &&
+ git diff --exit-code c2 HEAD &&
+ git diff --exit-code c2 &&
+ test -f c0.c &&
+ grep c1c1 c1.c &&
+ test -f c2.c
+'
+
+test_done
diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh
new file mode 100755
index 0000000..49f4e15
--- /dev/null
+++ b/t/t7607-merge-overwrite.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='git-merge
+
+Do not overwrite changes.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo c0 > c0.c &&
+ git add c0.c &&
+ git commit -m c0 &&
+ git tag c0 &&
+ echo c1 > c1.c &&
+ git add c1.c &&
+ git commit -m c1 &&
+ git tag c1 &&
+ git reset --hard c0 &&
+ echo c2 > c2.c &&
+ git add c2.c &&
+ git commit -m c2 &&
+ git tag c2 &&
+ git reset --hard c1 &&
+ echo "c1 a" > c1.c &&
+ git add c1.c &&
+ git commit -m "c1 a" &&
+ git tag c1a &&
+ echo "VERY IMPORTANT CHANGES" > important
+'
+
+test_expect_success 'will not overwrite untracked file' '
+ git reset --hard c1 &&
+ cat important > c2.c &&
+ ! git merge c2 &&
+ test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite new file' '
+ git reset --hard c1 &&
+ cat important > c2.c &&
+ git add c2.c &&
+ ! git merge c2 &&
+ test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite staged changes' '
+ git reset --hard c1 &&
+ cat important > c2.c &&
+ git add c2.c &&
+ rm c2.c &&
+ ! git merge c2 &&
+ git checkout c2.c &&
+ test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite removed file' '
+ git reset --hard c1 &&
+ git rm c1.c &&
+ git commit -m "rm c1.c" &&
+ cat important > c1.c &&
+ ! git merge c1a &&
+ test_cmp important c1.c
+'
+
+test_expect_success 'will not overwrite re-added file' '
+ git reset --hard c1 &&
+ git rm c1.c &&
+ git commit -m "rm c1.c" &&
+ cat important > c1.c &&
+ git add c1.c &&
+ ! git merge c1a &&
+ test_cmp important c1.c
+'
+
+test_expect_success 'will not overwrite removed file with staged changes' '
+ git reset --hard c1 &&
+ git rm c1.c &&
+ git commit -m "rm c1.c" &&
+ cat important > c1.c &&
+ git add c1.c &&
+ rm c1.c &&
+ ! git merge c1a &&
+ git checkout c1.c &&
+ test_cmp important c1.c
+'
+
+test_done
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
new file mode 100755
index 0000000..28d5679
--- /dev/null
+++ b/t/t7608-merge-messages.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='test auto-generated merge messages'
+. ./test-lib.sh
+
+check_oneline() {
+ echo "$1" | sed "s/Q/'/g" >expect &&
+ git log -1 --pretty=tformat:%s >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'merge local branch' '
+ test_commit master-1 &&
+ git checkout -b local-branch &&
+ test_commit branch-1 &&
+ git checkout master &&
+ test_commit master-2 &&
+ git merge local-branch &&
+ check_oneline "Merge branch Qlocal-branchQ"
+'
+
+test_expect_success 'merge octopus branches' '
+ git checkout -b octopus-a master &&
+ test_commit octopus-1 &&
+ git checkout -b octopus-b master &&
+ test_commit octopus-2 &&
+ git checkout master &&
+ git merge octopus-a octopus-b &&
+ check_oneline "Merge branches Qoctopus-aQ and Qoctopus-bQ"
+'
+
+test_expect_success 'merge tag' '
+ git checkout -b tag-branch master &&
+ test_commit tag-1 &&
+ git checkout master &&
+ test_commit master-3 &&
+ git merge tag-1 &&
+ check_oneline "Merge commit Qtag-1Q"
+'
+
+test_expect_success 'ambiguous tag' '
+ git checkout -b ambiguous master &&
+ test_commit ambiguous &&
+ git checkout master &&
+ test_commit master-4 &&
+ git merge ambiguous &&
+ check_oneline "Merge commit QambiguousQ"
+'
+
+test_expect_success 'remote branch' '
+ git checkout -b remote master &&
+ test_commit remote-1 &&
+ git update-ref refs/remotes/origin/master remote &&
+ git checkout master &&
+ test_commit master-5 &&
+ git merge origin/master &&
+ check_oneline "Merge remote branch Qorigin/masterQ"
+'
+
+test_done
diff --git a/t/t7610-mergetool.sh b/t/t7610-mergetool.sh
new file mode 100755
index 0000000..e768c3e
--- /dev/null
+++ b/t/t7610-mergetool.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Charles Bailey
+#
+
+test_description='git mergetool
+
+Testing basic merge tool invocation'
+
+. ./test-lib.sh
+
+# All the mergetool test work by checking out a temporary branch based
+# off 'branch1' and then merging in master and checking the results of
+# running mergetool
+
+test_expect_success 'setup' '
+ echo master >file1 &&
+ mkdir subdir &&
+ echo master sub >subdir/file3 &&
+ git add file1 subdir/file3 &&
+ git commit -m "added file1" &&
+
+ git checkout -b branch1 master &&
+ echo branch1 change >file1 &&
+ echo branch1 newfile >file2 &&
+ echo branch1 sub >subdir/file3 &&
+ git add file1 file2 subdir/file3 &&
+ git commit -m "branch1 changes" &&
+
+ git checkout master &&
+ echo master updated >file1 &&
+ echo master new >file2 &&
+ echo master new sub >subdir/file3 &&
+ git add file1 file2 subdir/file3 &&
+ git commit -m "master updates" &&
+
+ git config merge.tool mytool &&
+ git config mergetool.mytool.cmd "cat \"\$REMOTE\" >\"\$MERGED\"" &&
+ git config mergetool.mytool.trustExitCode true
+'
+
+test_expect_success 'custom mergetool' '
+ git checkout -b test1 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 ) &&
+ ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+ test "$(cat file1)" = "master updated" &&
+ test "$(cat file2)" = "master new" &&
+ test "$(cat subdir/file3)" = "master new sub" &&
+ git commit -m "branch1 resolved with mergetool"
+'
+
+test_expect_success 'mergetool crlf' '
+ git config core.autocrlf true &&
+ git checkout -b test2 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 ) &&
+ ( yes "" | git mergetool subdir/file3 >/dev/null 2>&1 ) &&
+ test "$(printf x | cat file1 -)" = "$(printf "master updated\r\nx")" &&
+ test "$(printf x | cat file2 -)" = "$(printf "master new\r\nx")" &&
+ test "$(printf x | cat subdir/file3 -)" = "$(printf "master new sub\r\nx")" &&
+ git commit -m "branch1 resolved with mergetool - autocrlf" &&
+ git config core.autocrlf false &&
+ git reset --hard
+'
+
+test_expect_success 'mergetool in subdir' '
+ git checkout -b test3 branch1
+ cd subdir && (
+ test_must_fail git merge master >/dev/null 2>&1 &&
+ ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
+ test "$(cat file3)" = "master new sub" )
+'
+
+# We can't merge files from parent directories when running mergetool
+# from a subdir. Is this a bug?
+#
+#test_expect_failure 'mergetool in subdir' '
+# cd subdir && (
+# ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+# ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+# test "$(cat ../file1)" = "master updated" &&
+# test "$(cat ../file2)" = "master new" &&
+# git commit -m "branch1 resolved with mergetool - subdir" )
+#'
+
+test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
new file mode 100755
index 0000000..c2f66ff
--- /dev/null
+++ b/t/t7700-repack.sh
@@ -0,0 +1,168 @@
+#!/bin/sh
+
+test_description='git repack works correctly'
+
+. ./test-lib.sh
+
+test_expect_success 'objects in packs marked .keep are not repacked' '
+ echo content1 > file1 &&
+ echo content2 > file2 &&
+ git add . &&
+ test_tick &&
+ git commit -m initial_commit &&
+ # Create two packs
+ # The first pack will contain all of the objects except one
+ git rev-list --objects --all | grep -v file2 |
+ git pack-objects pack > /dev/null &&
+ # The second pack will contain the excluded object
+ packsha1=$(git rev-list --objects --all | grep file2 |
+ git pack-objects pack) &&
+ touch -r pack-$packsha1.pack pack-$packsha1.keep &&
+ objsha1=$(git verify-pack -v pack-$packsha1.idx | head -n 1 |
+ sed -e "s/^\([0-9a-f]\{40\}\).*/\1/") &&
+ mv pack-* .git/objects/pack/ &&
+ git repack -A -d -l &&
+ git prune-packed &&
+ for p in .git/objects/pack/*.idx; do
+ idx=$(basename $p)
+ test "pack-$packsha1.idx" = "$idx" && continue
+ if git verify-pack -v $p | egrep "^$objsha1"; then
+ found_duplicate_object=1
+ echo "DUPLICATE OBJECT FOUND"
+ break
+ fi
+ done &&
+ test -z "$found_duplicate_object"
+'
+
+test_expect_success 'loose objects in alternate ODB are not repacked' '
+ mkdir alt_objects &&
+ echo `pwd`/alt_objects > .git/objects/info/alternates &&
+ echo content3 > file3 &&
+ objsha1=$(GIT_OBJECT_DIRECTORY=alt_objects git hash-object -w file3) &&
+ git add file3 &&
+ test_tick &&
+ git commit -m commit_file3 &&
+ git repack -a -d -l &&
+ git prune-packed &&
+ for p in .git/objects/pack/*.idx; do
+ if git verify-pack -v $p | egrep "^$objsha1"; then
+ found_duplicate_object=1
+ echo "DUPLICATE OBJECT FOUND"
+ break
+ fi
+ done &&
+ test -z "$found_duplicate_object"
+'
+
+test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
+ mkdir alt_objects/pack
+ mv .git/objects/pack/* alt_objects/pack &&
+ git repack -a &&
+ myidx=$(ls -1 .git/objects/pack/*.idx) &&
+ test -f "$myidx" &&
+ for p in alt_objects/pack/*.idx; do
+ git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p"
+ done | while read sha1 rest; do
+ if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then
+ echo "Missing object in local pack: $sha1"
+ return 1
+ fi
+ done
+'
+
+test_expect_success 'packed obs in alt ODB are repacked when local repo has packs' '
+ rm -f .git/objects/pack/* &&
+ echo new_content >> file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit -m more_content &&
+ git repack &&
+ git repack -a -d &&
+ myidx=$(ls -1 .git/objects/pack/*.idx) &&
+ test -f "$myidx" &&
+ for p in alt_objects/pack/*.idx; do
+ git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p"
+ done | while read sha1 rest; do
+ if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then
+ echo "Missing object in local pack: $sha1"
+ return 1
+ fi
+ done
+'
+
+test_expect_success 'packed obs in alternate ODB kept pack are repacked' '
+ # swap the .keep so the commit object is in the pack with .keep
+ for p in alt_objects/pack/*.pack
+ do
+ base_name=$(basename $p .pack)
+ if test -f alt_objects/pack/$base_name.keep
+ then
+ rm alt_objects/pack/$base_name.keep
+ else
+ touch alt_objects/pack/$base_name.keep
+ fi
+ done
+ git repack -a -d &&
+ myidx=$(ls -1 .git/objects/pack/*.idx) &&
+ test -f "$myidx" &&
+ for p in alt_objects/pack/*.idx; do
+ git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p"
+ done | while read sha1 rest; do
+ if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then
+ echo "Missing object in local pack: $sha1"
+ return 1
+ fi
+ done
+'
+
+test_expect_success 'packed unreachable obs in alternate ODB are not loosened' '
+ rm -f alt_objects/pack/*.keep &&
+ mv .git/objects/pack/* alt_objects/pack/ &&
+ csha1=$(git rev-parse HEAD^{commit}) &&
+ git reset --hard HEAD^ &&
+ test_tick &&
+ git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
+ # The pack-objects call on the next line is equivalent to
+ # git repack -A -d without the call to prune-packed
+ git pack-objects --honor-pack-keep --non-empty --all --reflog \
+ --unpack-unreachable </dev/null pack &&
+ rm -f .git/objects/pack/* &&
+ mv pack-* .git/objects/pack/ &&
+ test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+ egrep "^$csha1 " | sort | uniq | wc -l) &&
+ echo > .git/objects/info/alternates &&
+ test_must_fail git show $csha1
+'
+
+test_expect_success 'local packed unreachable obs that exist in alternate ODB are not loosened' '
+ echo `pwd`/alt_objects > .git/objects/info/alternates &&
+ echo "$csha1" | git pack-objects --non-empty --all --reflog pack &&
+ rm -f .git/objects/pack/* &&
+ mv pack-* .git/objects/pack/ &&
+ # The pack-objects call on the next line is equivalent to
+ # git repack -A -d without the call to prune-packed
+ git pack-objects --honor-pack-keep --non-empty --all --reflog \
+ --unpack-unreachable </dev/null pack &&
+ rm -f .git/objects/pack/* &&
+ mv pack-* .git/objects/pack/ &&
+ test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+ egrep "^$csha1 " | sort | uniq | wc -l) &&
+ echo > .git/objects/info/alternates &&
+ test_must_fail git show $csha1
+'
+
+test_expect_success 'objects made unreachable by grafts only are kept' '
+ test_tick &&
+ git commit --allow-empty -m "commit 4" &&
+ H0=$(git rev-parse HEAD) &&
+ H1=$(git rev-parse HEAD^) &&
+ H2=$(git rev-parse HEAD^^) &&
+ echo "$H0 $H2" > .git/info/grafts &&
+ git reflog expire --expire=$test_tick --expire-unreachable=$test_tick --all &&
+ git repack -a -d &&
+ git cat-file -t $H1
+ '
+
+test_done
+
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
new file mode 100755
index 0000000..200ab61
--- /dev/null
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -0,0 +1,98 @@
+#!/bin/sh
+
+test_description='git repack works correctly'
+
+. ./test-lib.sh
+
+fsha1=
+csha1=
+tsha1=
+
+test_expect_success '-A with -d option leaves unreachable objects unpacked' '
+ echo content > file1 &&
+ git add . &&
+ test_tick &&
+ git commit -m initial_commit &&
+ # create a transient branch with unique content
+ git checkout -b transient_branch &&
+ echo more content >> file1 &&
+ # record the objects created in the database for file, commit, tree
+ fsha1=$(git hash-object file1) &&
+ test_tick &&
+ git commit -a -m more_content &&
+ csha1=$(git rev-parse HEAD^{commit}) &&
+ tsha1=$(git rev-parse HEAD^{tree}) &&
+ git checkout master &&
+ echo even more content >> file1 &&
+ test_tick &&
+ git commit -a -m even_more_content &&
+ # delete the transient branch
+ git branch -D transient_branch &&
+ # pack the repo
+ git repack -A -d -l &&
+ # verify objects are packed in repository
+ test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+ egrep "^($fsha1|$csha1|$tsha1) " |
+ sort | uniq | wc -l) &&
+ git show $fsha1 &&
+ git show $csha1 &&
+ git show $tsha1 &&
+ # now expire the reflog, while keeping reachable ones but expiring
+ # unreachables immediately
+ test_tick &&
+ sometimeago=$(( $test_tick - 10000 )) &&
+ git reflog expire --expire=$sometimeago --expire-unreachable=$test_tick --all &&
+ # and repack
+ git repack -A -d -l &&
+ # verify objects are retained unpacked
+ test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+ egrep "^($fsha1|$csha1|$tsha1) " |
+ sort | uniq | wc -l) &&
+ git show $fsha1 &&
+ git show $csha1 &&
+ git show $tsha1
+'
+
+compare_mtimes ()
+{
+ read tref rest &&
+ while read t rest; do
+ test "$tref" = "$t" || break
+ done
+}
+
+test_expect_success '-A without -d option leaves unreachable objects packed' '
+ fsha1path=$(echo "$fsha1" | sed -e "s|\(..\)|\1/|") &&
+ fsha1path=".git/objects/$fsha1path" &&
+ csha1path=$(echo "$csha1" | sed -e "s|\(..\)|\1/|") &&
+ csha1path=".git/objects/$csha1path" &&
+ tsha1path=$(echo "$tsha1" | sed -e "s|\(..\)|\1/|") &&
+ tsha1path=".git/objects/$tsha1path" &&
+ git branch transient_branch $csha1 &&
+ git repack -a -d -l &&
+ test ! -f "$fsha1path" &&
+ test ! -f "$csha1path" &&
+ test ! -f "$tsha1path" &&
+ test 1 = $(ls -1 .git/objects/pack/pack-*.pack | wc -l) &&
+ packfile=$(ls .git/objects/pack/pack-*.pack) &&
+ git branch -D transient_branch &&
+ test_tick &&
+ git repack -A -l &&
+ test ! -f "$fsha1path" &&
+ test ! -f "$csha1path" &&
+ test ! -f "$tsha1path" &&
+ git show $fsha1 &&
+ git show $csha1 &&
+ git show $tsha1
+'
+
+test_expect_success 'unpacked objects receive timestamp of pack file' '
+ tmppack=".git/objects/pack/tmp_pack" &&
+ ln "$packfile" "$tmppack" &&
+ git repack -A -l -d &&
+ test-chmtime -v +0 "$tmppack" "$fsha1path" "$csha1path" "$tsha1path" \
+ > mtimes &&
+ compare_mtimes < mtimes
+'
+
+test_done
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
new file mode 100755
index 0000000..196827e
--- /dev/null
+++ b/t/t7800-difftool.sh
@@ -0,0 +1,276 @@
+#!/bin/sh
+#
+# Copyright (c) 2009, 2010 David Aguilar
+#
+
+test_description='git-difftool
+
+Testing basic diff tool invocation
+'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping difftool tests, perl not available'
+ test_done
+fi
+
+LF='
+'
+
+remove_config_vars()
+{
+ # Unset all config variables used by git-difftool
+ git config --unset diff.tool
+ git config --unset diff.guitool
+ git config --unset difftool.test-tool.cmd
+ git config --unset difftool.prompt
+ git config --unset merge.tool
+ git config --unset mergetool.test-tool.cmd
+ git config --unset mergetool.prompt
+ return 0
+}
+
+restore_test_defaults()
+{
+ # Restores the test defaults used by several tests
+ remove_config_vars
+ unset GIT_DIFF_TOOL
+ unset GIT_DIFFTOOL_PROMPT
+ unset GIT_DIFFTOOL_NO_PROMPT
+ git config diff.tool test-tool &&
+ git config difftool.test-tool.cmd 'cat $LOCAL'
+ git config difftool.bogus-tool.cmd false
+}
+
+prompt_given()
+{
+ prompt="$1"
+ test "$prompt" = "Hit return to launch 'test-tool': branch"
+}
+
+# Create a file on master and change it on branch
+test_expect_success 'setup' '
+ echo master >file &&
+ git add file &&
+ git commit -m "added file" &&
+
+ git checkout -b branch master &&
+ echo branch >file &&
+ git commit -a -m "branch changed file" &&
+ git checkout master
+'
+
+# Configure a custom difftool.<tool>.cmd and use it
+test_expect_success 'custom commands' '
+ restore_test_defaults &&
+ git config difftool.test-tool.cmd "cat \$REMOTE" &&
+
+ diff=$(git difftool --no-prompt branch) &&
+ test "$diff" = "master" &&
+
+ restore_test_defaults &&
+ diff=$(git difftool --no-prompt branch) &&
+ test "$diff" = "branch"
+'
+
+# Ensures that git-difftool ignores bogus --tool values
+test_expect_success 'difftool ignores bad --tool values' '
+ diff=$(git difftool --no-prompt --tool=bad-tool branch)
+ test "$?" = 1 &&
+ test "$diff" = ""
+'
+
+test_expect_success 'difftool honors --gui' '
+ git config merge.tool bogus-tool &&
+ git config diff.tool bogus-tool &&
+ git config diff.guitool test-tool &&
+
+ diff=$(git difftool --no-prompt --gui branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults
+'
+
+test_expect_success 'difftool --gui works without configured diff.guitool' '
+ git config diff.tool test-tool &&
+
+ diff=$(git difftool --no-prompt --gui branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults
+'
+
+# Specify the diff tool using $GIT_DIFF_TOOL
+test_expect_success 'GIT_DIFF_TOOL variable' '
+ git config --unset diff.tool
+ GIT_DIFF_TOOL=test-tool &&
+ export GIT_DIFF_TOOL &&
+
+ diff=$(git difftool --no-prompt branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults
+'
+
+# Test the $GIT_*_TOOL variables and ensure
+# that $GIT_DIFF_TOOL always wins unless --tool is specified
+test_expect_success 'GIT_DIFF_TOOL overrides' '
+ git config diff.tool bogus-tool &&
+ git config merge.tool bogus-tool &&
+
+ GIT_DIFF_TOOL=test-tool &&
+ export GIT_DIFF_TOOL &&
+
+ diff=$(git difftool --no-prompt branch) &&
+ test "$diff" = "branch" &&
+
+ GIT_DIFF_TOOL=bogus-tool &&
+ export GIT_DIFF_TOOL &&
+
+ diff=$(git difftool --no-prompt --tool=test-tool branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults
+'
+
+# Test that we don't have to pass --no-prompt to difftool
+# when $GIT_DIFFTOOL_NO_PROMPT is true
+test_expect_success 'GIT_DIFFTOOL_NO_PROMPT variable' '
+ GIT_DIFFTOOL_NO_PROMPT=true &&
+ export GIT_DIFFTOOL_NO_PROMPT &&
+
+ diff=$(git difftool branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults
+'
+
+# git-difftool supports the difftool.prompt variable.
+# Test that GIT_DIFFTOOL_PROMPT can override difftool.prompt = false
+test_expect_success 'GIT_DIFFTOOL_PROMPT variable' '
+ git config difftool.prompt false &&
+ GIT_DIFFTOOL_PROMPT=true &&
+ export GIT_DIFFTOOL_PROMPT &&
+
+ prompt=$(echo | git difftool branch | tail -1) &&
+ prompt_given "$prompt" &&
+
+ restore_test_defaults
+'
+
+# Test that we don't have to pass --no-prompt when difftool.prompt is false
+test_expect_success 'difftool.prompt config variable is false' '
+ git config difftool.prompt false &&
+
+ diff=$(git difftool branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults
+'
+
+# Test that we don't have to pass --no-prompt when mergetool.prompt is false
+test_expect_success 'difftool merge.prompt = false' '
+ git config --unset difftool.prompt
+ git config mergetool.prompt false &&
+
+ diff=$(git difftool branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults
+'
+
+# Test that the -y flag can override difftool.prompt = true
+test_expect_success 'difftool.prompt can overridden with -y' '
+ git config difftool.prompt true &&
+
+ diff=$(git difftool -y branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults
+'
+
+# Test that the --prompt flag can override difftool.prompt = false
+test_expect_success 'difftool.prompt can overridden with --prompt' '
+ git config difftool.prompt false &&
+
+ prompt=$(echo | git difftool --prompt branch | tail -1) &&
+ prompt_given "$prompt" &&
+
+ restore_test_defaults
+'
+
+# Test that the last flag passed on the command-line wins
+test_expect_success 'difftool last flag wins' '
+ diff=$(git difftool --prompt --no-prompt branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults &&
+
+ prompt=$(echo | git difftool --no-prompt --prompt branch | tail -1) &&
+ prompt_given "$prompt" &&
+
+ restore_test_defaults
+'
+
+# git-difftool falls back to git-mergetool config variables
+# so test that behavior here
+test_expect_success 'difftool + mergetool config variables' '
+ remove_config_vars
+ git config merge.tool test-tool &&
+ git config mergetool.test-tool.cmd "cat \$LOCAL" &&
+
+ diff=$(git difftool --no-prompt branch) &&
+ test "$diff" = "branch" &&
+
+ # set merge.tool to something bogus, diff.tool to test-tool
+ git config merge.tool bogus-tool &&
+ git config diff.tool test-tool &&
+
+ diff=$(git difftool --no-prompt branch) &&
+ test "$diff" = "branch" &&
+
+ restore_test_defaults
+'
+
+test_expect_success 'difftool.<tool>.path' '
+ git config difftool.tkdiff.path echo &&
+ diff=$(git difftool --tool=tkdiff --no-prompt branch) &&
+ git config --unset difftool.tkdiff.path &&
+ lines=$(echo "$diff" | grep file | wc -l) &&
+ test "$lines" -eq 1 &&
+
+ restore_test_defaults
+'
+
+test_expect_success 'difftool --extcmd=cat' '
+ diff=$(git difftool --no-prompt --extcmd=cat branch) &&
+ test "$diff" = branch"$LF"master
+'
+
+test_expect_success 'difftool --extcmd cat' '
+ diff=$(git difftool --no-prompt --extcmd cat branch) &&
+ test "$diff" = branch"$LF"master
+'
+
+test_expect_success 'difftool -x cat' '
+ diff=$(git difftool --no-prompt -x cat branch) &&
+ test "$diff" = branch"$LF"master
+'
+
+test_expect_success 'difftool --extcmd echo arg1' '
+ diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"echo\ \$1\" branch)
+ test "$diff" = file
+'
+
+test_expect_success 'difftool --extcmd cat arg1' '
+ diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$1\" branch)
+ test "$diff" = master
+'
+
+test_expect_success 'difftool --extcmd cat arg2' '
+ diff=$(git difftool --no-prompt --extcmd sh\ -c\ \"cat\ \$2\" branch)
+ test "$diff" = branch
+'
+
+test_done
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
new file mode 100755
index 0000000..8a63227
--- /dev/null
+++ b/t/t7810-grep.sh
@@ -0,0 +1,530 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git grep various.
+'
+
+. ./test-lib.sh
+
+cat >hello.c <<EOF
+#include <stdio.h>
+int main(int argc, const char **argv)
+{
+ printf("Hello world.\n");
+ return 0;
+ /* char ?? */
+}
+EOF
+
+test_expect_success setup '
+ {
+ echo foo mmap bar
+ echo foo_mmap bar
+ echo foo_mmap bar mmap
+ echo foo mmap bar_mmap
+ echo foo_mmap bar mmap baz
+ } >file &&
+ echo vvv >v &&
+ echo ww w >w &&
+ echo x x xx x >x &&
+ echo y yy >y &&
+ echo zzz > z &&
+ mkdir t &&
+ echo test >t/t &&
+ echo vvv >t/v &&
+ mkdir t/a &&
+ echo vvv >t/a/v &&
+ git add . &&
+ test_tick &&
+ git commit -m initial
+'
+
+test_expect_success 'grep should not segfault with a bad input' '
+ test_must_fail git grep "("
+'
+
+for H in HEAD ''
+do
+ case "$H" in
+ HEAD) HC='HEAD:' L='HEAD' ;;
+ '') HC= L='in working tree' ;;
+ esac
+
+ test_expect_success "grep -w $L" '
+ {
+ echo ${HC}file:1:foo mmap bar
+ echo ${HC}file:3:foo_mmap bar mmap
+ echo ${HC}file:4:foo mmap bar_mmap
+ echo ${HC}file:5:foo_mmap bar mmap baz
+ } >expected &&
+ git grep -n -w -e mmap $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep -w $L (w)" '
+ : >expected &&
+ ! git grep -n -w -e "^w" >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep -w $L (x)" '
+ {
+ echo ${HC}x:1:x x xx x
+ } >expected &&
+ git grep -n -w -e "x xx* x" $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep -w $L (y-1)" '
+ {
+ echo ${HC}y:1:y yy
+ } >expected &&
+ git grep -n -w -e "^y" $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep -w $L (y-2)" '
+ : >expected &&
+ if git grep -n -w -e "^y y" $H >actual
+ then
+ echo should not have matched
+ cat actual
+ false
+ else
+ test_cmp expected actual
+ fi
+ '
+
+ test_expect_success "grep -w $L (z)" '
+ : >expected &&
+ if git grep -n -w -e "^z" $H >actual
+ then
+ echo should not have matched
+ cat actual
+ false
+ else
+ test_cmp expected actual
+ fi
+ '
+
+ test_expect_success "grep $L (t-1)" '
+ echo "${HC}t/t:1:test" >expected &&
+ git grep -n -e test $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L (t-2)" '
+ echo "${HC}t:1:test" >expected &&
+ (
+ cd t &&
+ git grep -n -e test $H
+ ) >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep $L (t-3)" '
+ echo "${HC}t/t:1:test" >expected &&
+ (
+ cd t &&
+ git grep --full-name -n -e test $H
+ ) >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep -c $L (no /dev/null)" '
+ ! git grep -c test $H | grep /dev/null
+ '
+
+ test_expect_success "grep --max-depth -1 $L" '
+ {
+ echo ${HC}t/a/v:1:vvv
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth -1 -n -e vvv $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 $L" '
+ {
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 -- '*' $L" '
+ {
+ echo ${HC}t/a/v:1:vvv
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 1 $L" '
+ {
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 1 -n -e vvv $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 -- t $L" '
+ {
+ echo ${HC}t/v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H -- t >actual &&
+ test_cmp expected actual
+ '
+
+done
+
+cat >expected <<EOF
+file:foo mmap bar_mmap
+EOF
+
+test_expect_success 'grep -e A --and -e B' '
+ git grep -e "foo mmap" --and -e bar_mmap >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo_mmap bar mmap
+file:foo_mmap bar mmap baz
+EOF
+
+
+test_expect_success 'grep ( -e A --or -e B ) --and -e B' '
+ git grep \( -e foo_ --or -e baz \) \
+ --and -e " mmap" >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+EOF
+
+test_expect_success 'grep -e A --and --not -e B' '
+ git grep -e "foo mmap" --and --not -e bar_mmap >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep should ignore GREP_OPTIONS' '
+ GREP_OPTIONS=-v git grep " mmap bar\$" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -f, non-existent file' '
+ test_must_fail git grep -f patterns
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+file:foo_mmap bar
+file:foo_mmap bar mmap
+file:foo mmap bar_mmap
+file:foo_mmap bar mmap baz
+EOF
+
+cat >pattern <<EOF
+mmap
+EOF
+
+test_expect_success 'grep -f, one pattern' '
+ git grep -f pattern >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+file:foo_mmap bar
+file:foo_mmap bar mmap
+file:foo mmap bar_mmap
+file:foo_mmap bar mmap baz
+t/a/v:vvv
+t/v:vvv
+v:vvv
+EOF
+
+cat >patterns <<EOF
+mmap
+vvv
+EOF
+
+test_expect_success 'grep -f, multiple patterns' '
+ git grep -f patterns >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+file:foo mmap bar
+file:foo_mmap bar
+file:foo_mmap bar mmap
+file:foo mmap bar_mmap
+file:foo_mmap bar mmap baz
+t/a/v:vvv
+t/v:vvv
+v:vvv
+EOF
+
+cat >patterns <<EOF
+
+mmap
+
+vvv
+
+EOF
+
+test_expect_success 'grep -f, ignore empty lines' '
+ git grep -f patterns >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+y:y yy
+--
+z:zzz
+EOF
+
+test_expect_success 'grep -q, silently report matches' '
+ >empty &&
+ git grep -q mmap >actual &&
+ test_cmp empty actual &&
+ test_must_fail git grep -q qfwfq >actual &&
+ test_cmp empty actual
+'
+
+# Create 1024 file names that sort between "y" and "z" to make sure
+# the two files are handled by different calls to an external grep.
+# This depends on MAXARGS in builtin-grep.c being 1024 or less.
+c32="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v"
+test_expect_success 'grep -C1, hunk mark between files' '
+ for a in $c32; do for b in $c32; do : >y-$a$b; done; done &&
+ git add y-?? &&
+ git grep -C1 "^[yz]" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep -C1 hunk mark between files' '
+ git grep -C1 "^[yz]" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'log grep setup' '
+ echo a >>file &&
+ test_tick &&
+ GIT_AUTHOR_NAME="With * Asterisk" \
+ GIT_AUTHOR_EMAIL="xyzzy@frotz.com" \
+ git commit -a -m "second" &&
+
+ echo a >>file &&
+ test_tick &&
+ git commit -a -m "third"
+
+'
+
+test_expect_success 'log grep (1)' '
+ git log --author=author --pretty=tformat:%s >actual &&
+ ( echo third ; echo initial ) >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log grep (2)' '
+ git log --author=" * " -F --pretty=tformat:%s >actual &&
+ ( echo second ) >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log grep (3)' '
+ git log --author="^A U" --pretty=tformat:%s >actual &&
+ ( echo third ; echo initial ) >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log grep (4)' '
+ git log --author="frotz\.com>$" --pretty=tformat:%s >actual &&
+ ( echo second ) >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log grep (5)' '
+ git log --author=Thor -F --pretty=tformat:%s >actual &&
+ ( echo third ; echo initial ) >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log grep (6)' '
+ git log --author=-0700 --pretty=tformat:%s >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'log --grep --author implicitly uses all-match' '
+ # grep matches initial and second but not third
+ # author matches only initial and third
+ git log --author="A U Thor" --grep=s --grep=l --format=%s >actual &&
+ echo initial >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'grep with CE_VALID file' '
+ git update-index --assume-unchanged t/t &&
+ rm t/t &&
+ test "$(git grep test)" = "t/t:test" &&
+ git update-index --no-assume-unchanged t/t &&
+ git checkout t/t
+'
+
+cat >expected <<EOF
+hello.c=#include <stdio.h>
+hello.c: return 0;
+EOF
+
+test_expect_success 'grep -p with userdiff' '
+ git config diff.custom.funcname "^#" &&
+ echo "hello.c diff=custom" >.gitattributes &&
+ git grep -p return >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c: return 0;
+EOF
+
+test_expect_success 'grep -p' '
+ rm -f .gitattributes &&
+ git grep -p return >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c-#include <stdio.h>
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c- printf("Hello world.\n");
+hello.c: return 0;
+EOF
+
+test_expect_success 'grep -p -B5' '
+ git grep -p -B5 return >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'grep from a subdirectory to search wider area (1)' '
+ mkdir -p s &&
+ (
+ cd s && git grep "x x x" ..
+ )
+'
+
+test_expect_success 'grep from a subdirectory to search wider area (2)' '
+ mkdir -p s &&
+ (
+ cd s || exit 1
+ ( git grep xxyyzz .. >out ; echo $? >status )
+ ! test -s out &&
+ test 1 = $(cat status)
+ )
+'
+
+cat >expected <<EOF
+hello.c:int main(int argc, const char **argv)
+EOF
+
+test_expect_success 'grep -Fi' '
+ git grep -Fi "CHAR *" >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'outside of git repository' '
+ rm -fr non &&
+ mkdir -p non/git/sub &&
+ echo hello >non/git/file1 &&
+ echo world >non/git/sub/file2 &&
+ echo ".*o*" >non/git/.gitignore &&
+ {
+ echo file1:hello &&
+ echo sub/file2:world
+ } >non/expect.full &&
+ echo file2:world >non/expect.sub
+ (
+ GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
+ export GIT_CEILING_DIRECTORIES &&
+ cd non/git &&
+ test_must_fail git grep o &&
+ git grep --no-index o >../actual.full &&
+ test_cmp ../expect.full ../actual.full
+ cd sub &&
+ test_must_fail git grep o &&
+ git grep --no-index o >../../actual.sub &&
+ test_cmp ../../expect.sub ../../actual.sub
+ )
+'
+
+test_expect_success 'inside git repository but with --no-index' '
+ rm -fr is &&
+ mkdir -p is/git/sub &&
+ echo hello >is/git/file1 &&
+ echo world >is/git/sub/file2 &&
+ echo ".*o*" >is/git/.gitignore &&
+ {
+ echo file1:hello &&
+ echo sub/file2:world
+ } >is/expect.full &&
+ : >is/expect.empty &&
+ echo file2:world >is/expect.sub
+ (
+ cd is/git &&
+ git init &&
+ test_must_fail git grep o >../actual.full &&
+ test_cmp ../expect.empty ../actual.full &&
+ git grep --no-index o >../actual.full &&
+ test_cmp ../expect.full ../actual.full &&
+ cd sub &&
+ test_must_fail git grep o >../../actual.sub &&
+ test_cmp ../../expect.empty ../../actual.sub &&
+ git grep --no-index o >../../actual.sub &&
+ test_cmp ../../expect.sub ../../actual.sub
+ )
+'
+
+test_expect_success 'setup double-dash tests' '
+cat >double-dash <<EOF &&
+--
+->
+other
+EOF
+git add double-dash
+'
+
+cat >expected <<EOF
+double-dash:->
+EOF
+test_expect_success 'grep -- pattern' '
+ git grep -- "->" >actual &&
+ test_cmp expected actual
+'
+test_expect_success 'grep -- pattern -- pathspec' '
+ git grep -- "->" -- double-dash >actual &&
+ test_cmp expected actual
+'
+test_expect_success 'grep -e pattern -- path' '
+ git grep -e "->" -- double-dash >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<EOF
+double-dash:--
+EOF
+test_expect_success 'grep -e -- -- path' '
+ git grep -e -- -- double-dash >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t7811-grep-open.sh b/t/t7811-grep-open.sh
new file mode 100755
index 0000000..568a6f2
--- /dev/null
+++ b/t/t7811-grep-open.sh
@@ -0,0 +1,168 @@
+#!/bin/sh
+
+test_description='git grep --open-files-in-pager
+'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-pager.sh
+unset PAGER GIT_PAGER
+
+test_expect_success 'setup' '
+ test_commit initial grep.h "
+enum grep_pat_token {
+ GREP_PATTERN,
+ GREP_PATTERN_HEAD,
+ GREP_PATTERN_BODY,
+ GREP_AND,
+ GREP_OPEN_PAREN,
+ GREP_CLOSE_PAREN,
+ GREP_NOT,
+ GREP_OR,
+};" &&
+
+ test_commit add-user revision.c "
+ }
+ if (seen_dashdash)
+ read_pathspec_from_stdin(revs, &sb, prune);
+ strbuf_release(&sb);
+}
+
+static void add_grep(struct rev_info *revs, const char *ptn, enum grep_pat_token what)
+{
+ append_grep_pattern(&revs->grep_filter, ptn, \"command line\", 0, what);
+" &&
+
+ mkdir subdir &&
+ test_commit subdir subdir/grep.c "enum grep_pat_token" &&
+
+ test_commit uninteresting unrelated "hello, world" &&
+
+ echo GREP_PATTERN >untracked
+'
+
+test_expect_success SIMPLEPAGER 'git grep -O' '
+ cat >$less <<-\EOF &&
+ #!/bin/sh
+ printf "%s\n" "$@" >pager-args
+ EOF
+ chmod +x $less &&
+ cat >expect.less <<-\EOF &&
+ +/*GREP_PATTERN
+ grep.h
+ EOF
+ echo grep.h >expect.notless &&
+ >empty &&
+
+ PATH=.:$PATH git grep -O GREP_PATTERN >out &&
+ {
+ test_cmp expect.less pager-args ||
+ test_cmp expect.notless pager-args
+ } &&
+ test_cmp empty out
+'
+
+test_expect_success 'git grep -O --cached' '
+ test_must_fail git grep --cached -O GREP_PATTERN >out 2>msg &&
+ grep open-files-in-pager msg
+'
+
+test_expect_success 'git grep -O --no-index' '
+ rm -f expect.less pager-args out &&
+ cat >expect <<-\EOF &&
+ grep.h
+ untracked
+ EOF
+ >empty &&
+
+ (
+ GIT_PAGER='\''printf "%s\n" >pager-args'\'' &&
+ export GIT_PAGER &&
+ git grep --no-index -O GREP_PATTERN >out
+ ) &&
+ test_cmp expect pager-args &&
+ test_cmp empty out
+'
+
+test_expect_success 'setup: fake "less"' '
+ cat >less <<-\EOF &&
+ #!/bin/sh
+ printf "%s\n" "$@" >actual
+ EOF
+ chmod +x less
+'
+
+test_expect_success 'git grep -O jumps to line in less' '
+ cat >expect <<-\EOF &&
+ +/*GREP_PATTERN
+ grep.h
+ EOF
+ >empty &&
+
+ GIT_PAGER=./less git grep -O GREP_PATTERN >out &&
+ test_cmp expect actual &&
+ test_cmp empty out &&
+
+ git grep -O./less GREP_PATTERN >out2 &&
+ test_cmp expect actual &&
+ test_cmp empty out2
+'
+
+test_expect_success 'modified file' '
+ rm -f actual &&
+ cat >expect <<-\EOF &&
+ +/*enum grep_pat_token
+ grep.h
+ revision.c
+ subdir/grep.c
+ unrelated
+ EOF
+ >empty &&
+
+ echo "enum grep_pat_token" >unrelated &&
+ test_when_finished "git checkout HEAD unrelated" &&
+ GIT_PAGER=./less git grep -F -O "enum grep_pat_token" >out &&
+ test_cmp expect actual &&
+ test_cmp empty out
+'
+
+test_config() {
+ git config "$1" "$2" &&
+ test_when_finished "git config --unset $1"
+}
+
+test_expect_success 'copes with color settings' '
+ rm -f actual &&
+ echo grep.h >expect &&
+ test_config color.grep always &&
+ test_config color.grep.filename yellow &&
+ test_config color.grep.separator green &&
+ git grep -O'\''printf "%s\n" >actual'\'' GREP_AND &&
+ test_cmp expect actual
+'
+
+test_expect_success 'run from subdir' '
+ rm -f actual &&
+ echo grep.c >expect &&
+ >empty &&
+
+ (
+ cd subdir &&
+ export GIT_PAGER &&
+ GIT_PAGER='\''printf "%s\n" >../args'\'' &&
+ git grep -O "enum grep_pat_token" >../out &&
+ git grep -O"pwd >../dir; :" "enum grep_pat_token" >../out2
+ ) &&
+ case $(cat dir) in
+ *subdir)
+ : good
+ ;;
+ *)
+ false
+ ;;
+ esac &&
+ test_cmp expect args &&
+ test_cmp empty out &&
+ test_cmp empty out2
+'
+
+test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
new file mode 100755
index 0000000..45cb60e
--- /dev/null
+++ b/t/t8001-annotate.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+test_description='git annotate'
+. ./test-lib.sh
+
+PROG='git annotate'
+. "$TEST_DIRECTORY"/annotate-tests.sh
+
+test_expect_success \
+ 'Annotating an old revision works' \
+ '[ $(git annotate file master | awk "{print \$3}" | grep -c "^A$") -eq 2 ] && \
+ [ $(git annotate file master | awk "{print \$3}" | grep -c "^B$") -eq 2 ]'
+
+
+test_done
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
new file mode 100755
index 0000000..597cf04
--- /dev/null
+++ b/t/t8002-blame.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+test_description='git blame'
+. ./test-lib.sh
+
+PROG='git blame -c'
+. "$TEST_DIRECTORY"/annotate-tests.sh
+
+test_done
diff --git a/t/t8003-blame.sh b/t/t8003-blame.sh
new file mode 100755
index 0000000..230143c
--- /dev/null
+++ b/t/t8003-blame.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+
+test_description='git blame corner cases'
+. ./test-lib.sh
+
+pick_fc='s/^[0-9a-f^]* *\([^ ]*\) *(\([^ ]*\) .*/\1-\2/'
+
+test_expect_success setup '
+
+ echo A A A A A >one &&
+ echo B B B B B >two &&
+ echo C C C C C >tres &&
+ echo ABC >mouse &&
+ for i in 1 2 3 4 5 6 7 8 9
+ do
+ echo $i
+ done >nine_lines &&
+ for i in 1 2 3 4 5 6 7 8 9 a
+ do
+ echo $i
+ done >ten_lines &&
+ git add one two tres mouse nine_lines ten_lines &&
+ test_tick &&
+ GIT_AUTHOR_NAME=Initial git commit -m Initial &&
+
+ cat one >uno &&
+ mv two dos &&
+ cat one >>tres &&
+ echo DEF >>mouse
+ git add uno dos tres mouse &&
+ test_tick &&
+ GIT_AUTHOR_NAME=Second git commit -a -m Second &&
+
+ echo GHIJK >>mouse &&
+ git add mouse &&
+ test_tick &&
+ GIT_AUTHOR_NAME=Third git commit -m Third &&
+
+ cat mouse >cow &&
+ git add cow &&
+ test_tick &&
+ GIT_AUTHOR_NAME=Fourth git commit -m Fourth &&
+
+ {
+ echo ABC
+ echo DEF
+ echo XXXX
+ echo GHIJK
+ } >cow &&
+ git add cow &&
+ test_tick &&
+ GIT_AUTHOR_NAME=Fifth git commit -m Fifth
+'
+
+test_expect_success 'straight copy without -C' '
+
+ git blame uno | grep Second
+
+'
+
+test_expect_success 'straight move without -C' '
+
+ git blame dos | grep Initial
+
+'
+
+test_expect_success 'straight copy with -C' '
+
+ git blame -C1 uno | grep Second
+
+'
+
+test_expect_success 'straight move with -C' '
+
+ git blame -C1 dos | grep Initial
+
+'
+
+test_expect_success 'straight copy with -C -C' '
+
+ git blame -C -C1 uno | grep Initial
+
+'
+
+test_expect_success 'straight move with -C -C' '
+
+ git blame -C -C1 dos | grep Initial
+
+'
+
+test_expect_success 'append without -C' '
+
+ git blame -L2 tres | grep Second
+
+'
+
+test_expect_success 'append with -C' '
+
+ git blame -L2 -C1 tres | grep Second
+
+'
+
+test_expect_success 'append with -C -C' '
+
+ git blame -L2 -C -C1 tres | grep Second
+
+'
+
+test_expect_success 'append with -C -C -C' '
+
+ git blame -L2 -C -C -C1 tres | grep Initial
+
+'
+
+test_expect_success 'blame wholesale copy' '
+
+ git blame -f -C -C1 HEAD^ -- cow | sed -e "$pick_fc" >current &&
+ {
+ echo mouse-Initial
+ echo mouse-Second
+ echo mouse-Third
+ } >expected &&
+ test_cmp expected current
+
+'
+
+test_expect_success 'blame wholesale copy and more' '
+
+ git blame -f -C -C1 HEAD -- cow | sed -e "$pick_fc" >current &&
+ {
+ echo mouse-Initial
+ echo mouse-Second
+ echo cow-Fifth
+ echo mouse-Third
+ } >expected &&
+ test_cmp expected current
+
+'
+
+test_expect_success 'blame path that used to be a directory' '
+ mkdir path &&
+ echo A A A A A >path/file &&
+ echo B B B B B >path/elif &&
+ git add path &&
+ test_tick &&
+ git commit -m "path was a directory" &&
+ rm -fr path &&
+ echo A A A A A >path &&
+ git add path &&
+ test_tick &&
+ git commit -m "path is a regular file" &&
+ git blame HEAD^.. -- path
+'
+
+test_expect_success 'blame to a commit with no author name' '
+ TREE=`git rev-parse HEAD:`
+ cat >badcommit <<EOF
+tree $TREE
+author <noname> 1234567890 +0000
+committer David Reiss <dreiss@facebook.com> 1234567890 +0000
+
+some message
+EOF
+ COMMIT=`git hash-object -t commit -w badcommit`
+ git --no-pager blame $COMMIT -- uno >/dev/null
+'
+
+test_expect_success 'blame -L with invalid start' '
+ test_must_fail git blame -L5 tres 2>errors &&
+ grep "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_expect_success 'indent of line numbers, nine lines' '
+ git blame nine_lines >actual &&
+ test $(grep -c " " actual) = 0
+'
+
+test_expect_success 'indent of line numbers, ten lines' '
+ git blame ten_lines >actual &&
+ test $(grep -c " " actual) = 9
+'
+
+test_done
diff --git a/t/t8004-blame.sh b/t/t8004-blame.sh
new file mode 100755
index 0000000..ba19ac1
--- /dev/null
+++ b/t/t8004-blame.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+# Based on a test case submitted by Björn Steinbrink.
+
+test_description='git blame on conflicted files'
+. ./test-lib.sh
+
+test_expect_success 'setup first case' '
+ # Create the old file
+ echo "Old line" > file1 &&
+ git add file1 &&
+ git commit --author "Old Line <ol@localhost>" -m file1.a &&
+
+ # Branch
+ git checkout -b foo &&
+
+ # Do an ugly move and change
+ git rm file1 &&
+ echo "New line ..." > file2 &&
+ echo "... and more" >> file2 &&
+ git add file2 &&
+ git commit --author "U Gly <ug@localhost>" -m ugly &&
+
+ # Back to master and change something
+ git checkout master &&
+ echo "
+
+bla" >> file1 &&
+ git commit --author "Old Line <ol@localhost>" -a -m file1.b &&
+
+ # Back to foo and merge master
+ git checkout foo &&
+ if git merge master; then
+ echo needed conflict here
+ exit 1
+ else
+ echo merge failed - resolving automatically
+ fi &&
+ echo "New line ...
+... and more
+
+bla
+Even more" > file2 &&
+ git rm file1 &&
+ git commit --author "M Result <mr@localhost>" -a -m merged &&
+
+ # Back to master and change file1 again
+ git checkout master &&
+ sed s/bla/foo/ <file1 >X &&
+ rm file1 &&
+ mv X file1 &&
+ git commit --author "No Bla <nb@localhost>" -a -m replace &&
+
+ # Try to merge into foo again
+ git checkout foo &&
+ if git merge master; then
+ echo needed conflict here
+ exit 1
+ else
+ echo merge failed - test is setup
+ fi
+'
+
+test_expect_success \
+ 'blame runs on unconflicted file while other file has conflicts' '
+ git blame file2
+'
+
+test_expect_success 'blame runs on conflicted file in stages 1,3' '
+ git blame file1
+'
+
+test_done
diff --git a/t/t8005-blame-i18n.sh b/t/t8005-blame-i18n.sh
new file mode 100755
index 0000000..cb39055
--- /dev/null
+++ b/t/t8005-blame-i18n.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='git blame encoding conversion'
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/t8005/utf8.txt
+. "$TEST_DIRECTORY"/t8005/euc-japan.txt
+. "$TEST_DIRECTORY"/t8005/sjis.txt
+
+test_expect_success 'setup the repository' '
+ # Create the file
+ echo "UTF-8 LINE" > file &&
+ git add file &&
+ git commit --author "$UTF8_NAME <utf8@localhost>" -m "$UTF8_MSG" &&
+
+ echo "EUC-JAPAN LINE" >> file &&
+ git add file &&
+ git config i18n.commitencoding eucJP &&
+ git commit --author "$EUC_JAPAN_NAME <euc-japan@localhost>" -m "$EUC_JAPAN_MSG" &&
+
+ echo "SJIS LINE" >> file &&
+ git add file &&
+ git config i18n.commitencoding SJIS &&
+ git commit --author "$SJIS_NAME <sjis@localhost>" -m "$SJIS_MSG"
+'
+
+cat >expected <<EOF
+author $SJIS_NAME
+summary $SJIS_MSG
+author $SJIS_NAME
+summary $SJIS_MSG
+author $SJIS_NAME
+summary $SJIS_MSG
+EOF
+
+test_expect_success \
+ 'blame respects i18n.commitencoding' '
+ git blame --incremental file | \
+ egrep "^(author|summary) " > actual &&
+ test_cmp actual expected
+'
+
+cat >expected <<EOF
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+EOF
+
+test_expect_success \
+ 'blame respects i18n.logoutputencoding' '
+ git config i18n.logoutputencoding eucJP &&
+ git blame --incremental file | \
+ egrep "^(author|summary) " > actual &&
+ test_cmp actual expected
+'
+
+cat >expected <<EOF
+author $UTF8_NAME
+summary $UTF8_MSG
+author $UTF8_NAME
+summary $UTF8_MSG
+author $UTF8_NAME
+summary $UTF8_MSG
+EOF
+
+test_expect_success \
+ 'blame respects --encoding=UTF-8' '
+ git blame --incremental --encoding=UTF-8 file | \
+ egrep "^(author|summary) " > actual &&
+ test_cmp actual expected
+'
+
+cat >expected <<EOF
+author $SJIS_NAME
+summary $SJIS_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+author $UTF8_NAME
+summary $UTF8_MSG
+EOF
+
+test_expect_success \
+ 'blame respects --encoding=none' '
+ git blame --incremental --encoding=none file | \
+ egrep "^(author|summary) " > actual &&
+ test_cmp actual expected
+'
+
+test_done
diff --git a/t/t8005/euc-japan.txt b/t/t8005/euc-japan.txt
new file mode 100644
index 0000000..288f040
--- /dev/null
+++ b/t/t8005/euc-japan.txt
@@ -0,0 +1,2 @@
+EUC_JAPAN_NAME="»³ÅÄ ÂÀϺ"
+EUC_JAPAN_MSG="¥Ö¥ì¡¼¥à¤Î¥Æ¥¹¥È¤Ç¤¹¡£"
diff --git a/t/t8005/sjis.txt b/t/t8005/sjis.txt
new file mode 100644
index 0000000..bbdefea
--- /dev/null
+++ b/t/t8005/sjis.txt
@@ -0,0 +1,2 @@
+SJIS_NAME="ŽR“c ‘¾˜Y"
+SJIS_MSG="ƒuƒŒ[ƒ€‚̃eƒXƒg‚Å‚·B"
diff --git a/t/t8005/utf8.txt b/t/t8005/utf8.txt
new file mode 100644
index 0000000..4d00dbe
--- /dev/null
+++ b/t/t8005/utf8.txt
@@ -0,0 +1,2 @@
+UTF8_NAME="山田 太郎"
+UTF8_MSG="ブレームã®ãƒ†ã‚¹ãƒˆã§ã™ã€‚"
diff --git a/t/t8006-blame-textconv.sh b/t/t8006-blame-textconv.sh
new file mode 100755
index 0000000..9ad96d4
--- /dev/null
+++ b/t/t8006-blame-textconv.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+test_description='git blame textconv support'
+. ./test-lib.sh
+
+find_blame() {
+ sed -e 's/^[^(]*//'
+}
+
+cat >helper <<'EOF'
+#!/bin/sh
+sed 's/^/converted: /' "$@"
+EOF
+chmod +x helper
+
+test_expect_success 'setup ' '
+ echo test 1 >one.bin &&
+ echo test number 2 >two.bin &&
+ git add . &&
+ GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
+ echo test 1 version 2 >one.bin &&
+ echo test number 2 version 2 >>two.bin &&
+ GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
+'
+
+cat >expected <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) test 1 version 2
+EOF
+
+test_expect_success 'no filter specified' '
+ git blame one.bin >blame &&
+ find_blame Number2 <blame >result &&
+ test_cmp expected result
+'
+
+test_expect_success 'setup textconv filters' '
+ echo "*.bin diff=test" >.gitattributes &&
+ git config diff.test.textconv ./helper &&
+ git config diff.test.cachetextconv false
+'
+
+test_expect_success 'blame with --no-textconv' '
+ git blame --no-textconv one.bin >blame &&
+ find_blame <blame> result &&
+ test_cmp expected result
+'
+
+cat >expected <<EOF
+(Number2 2010-01-01 20:00:00 +0000 1) converted: test 1 version 2
+EOF
+
+test_expect_success 'basic blame on last commit' '
+ git blame one.bin >blame &&
+ find_blame <blame >result &&
+ test_cmp expected result
+'
+
+cat >expected <<EOF
+(Number1 2010-01-01 18:00:00 +0000 1) converted: test number 2
+(Number2 2010-01-01 20:00:00 +0000 2) converted: test number 2 version 2
+EOF
+
+test_expect_success 'blame --textconv going through revisions' '
+ git blame --textconv two.bin >blame &&
+ find_blame <blame >result &&
+ test_cmp expected result
+'
+
+test_expect_success 'make a new commit' '
+ echo "test number 2 version 3" >>two.bin &&
+ GIT_AUTHOR_NAME=Number3 git commit -a -m Third --date="2010-01-01 22:00:00"
+'
+
+test_expect_success 'blame from previous revision' '
+ git blame HEAD^ two.bin >blame &&
+ find_blame <blame >result &&
+ test_cmp expected result
+'
+
+test_done
diff --git a/t/t8007-cat-file-textconv.sh b/t/t8007-cat-file-textconv.sh
new file mode 100755
index 0000000..38ac05e
--- /dev/null
+++ b/t/t8007-cat-file-textconv.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='git cat-file textconv support'
+. ./test-lib.sh
+
+cat >helper <<'EOF'
+#!/bin/sh
+sed 's/^/converted: /' "$@"
+EOF
+chmod +x helper
+
+test_expect_success 'setup ' '
+ echo test >one.bin &&
+ git add . &&
+ GIT_AUTHOR_NAME=Number1 git commit -a -m First --date="2010-01-01 18:00:00" &&
+ echo test version 2 >one.bin &&
+ GIT_AUTHOR_NAME=Number2 git commit -a -m Second --date="2010-01-01 20:00:00"
+'
+
+cat >expected <<EOF
+fatal: git cat-file --textconv: unable to run textconv on :one.bin
+EOF
+
+test_expect_success 'no filter specified' '
+ git cat-file --textconv :one.bin 2>result
+ test_cmp expected result
+'
+
+test_expect_success 'setup textconv filters' '
+ echo "*.bin diff=test" >.gitattributes &&
+ git config diff.test.textconv ./helper &&
+ git config diff.test.cachetextconv false
+'
+
+cat >expected <<EOF
+test version 2
+EOF
+
+test_expect_success 'cat-file without --textconv' '
+ git cat-file blob :one.bin >result &&
+ test_cmp expected result
+'
+
+cat >expected <<EOF
+test
+EOF
+
+test_expect_success 'cat-file without --textconv on previous commit' '
+ git cat-file -p HEAD^:one.bin >result &&
+ test_cmp expected result
+'
+
+cat >expected <<EOF
+converted: test version 2
+EOF
+
+test_expect_success 'cat-file --textconv on last commit' '
+ git cat-file --textconv :one.bin >result &&
+ test_cmp expected result
+'
+
+cat >expected <<EOF
+converted: test
+EOF
+
+test_expect_success 'cat-file --textconv on previous commit' '
+ git cat-file --textconv HEAD^:one.bin >result &&
+ test_cmp expected result
+'
+test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
new file mode 100755
index 0000000..23597cc
--- /dev/null
+++ b/t/t9001-send-email.sh
@@ -0,0 +1,998 @@
+#!/bin/sh
+
+test_description='git send-email'
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping git send-email tests, perl not available'
+ test_done
+fi
+
+PROG='git send-email'
+test_expect_success \
+ 'prepare reference tree' \
+ 'echo "1A quick brown fox jumps over the" >file &&
+ echo "lazy dog" >>file &&
+ git add file &&
+ GIT_AUTHOR_NAME="A" git commit -a -m "Initial."'
+
+test_expect_success \
+ 'Setup helper tool' \
+ '(echo "#!$SHELL_PATH"
+ echo shift
+ echo output=1
+ echo "while test -f commandline\$output; do output=\$((\$output+1)); done"
+ echo for a
+ echo do
+ echo " echo \"!\$a!\""
+ echo "done >commandline\$output"
+ echo "cat > msgtxt\$output"
+ ) >fake.sendmail &&
+ chmod +x ./fake.sendmail &&
+ git add fake.sendmail &&
+ GIT_AUTHOR_NAME="A" git commit -a -m "Second."'
+
+clean_fake_sendmail() {
+ rm -f commandline* msgtxt*
+}
+
+test_expect_success 'Extract patches' '
+ patches=`git format-patch -s --cc="One <one@example.com>" --cc=two@example.com -n HEAD^1`
+'
+
+# Test no confirm early to ensure remaining tests will not hang
+test_no_confirm () {
+ rm -f no_confirm_okay
+ echo n | \
+ GIT_SEND_EMAIL_NOTTY=1 \
+ git send-email \
+ --from="Example <from@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $@ \
+ $patches > stdout &&
+ test_must_fail grep "Send this email" stdout &&
+ > no_confirm_okay
+}
+
+# Exit immediately to prevent hang if a no-confirm test fails
+check_no_confirm () {
+ test -f no_confirm_okay || {
+ skip_all='confirm test failed; skipping remaining tests to prevent hanging'
+ test_done
+ }
+}
+
+test_expect_success 'No confirm with --suppress-cc' '
+ test_no_confirm --suppress-cc=sob
+'
+check_no_confirm
+
+test_expect_success 'No confirm with --confirm=never' '
+ test_no_confirm --confirm=never
+'
+check_no_confirm
+
+# leave sendemail.confirm set to never after this so that none of the
+# remaining tests prompt unintentionally.
+test_expect_success 'No confirm with sendemail.confirm=never' '
+ git config sendemail.confirm never &&
+ test_no_confirm --compose --subject=foo
+'
+check_no_confirm
+
+test_expect_success 'Send patches' '
+ git send-email --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
+
+cat >expected <<\EOF
+!nobody@example.com!
+!author@example.com!
+!one@example.com!
+!two@example.com!
+EOF
+test_expect_success \
+ 'Verify commandline' \
+ 'test_cmp expected commandline1'
+
+test_expect_success 'Send patches with --envelope-sender' '
+ clean_fake_sendmail &&
+ git send-email --envelope-sender="Patch Contributer <patch@example.com>" --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
+
+cat >expected <<\EOF
+!patch@example.com!
+!-i!
+!nobody@example.com!
+!author@example.com!
+!one@example.com!
+!two@example.com!
+EOF
+test_expect_success \
+ 'Verify commandline' \
+ 'test_cmp expected commandline1'
+
+test_expect_success 'Send patches with --envelope-sender=auto' '
+ clean_fake_sendmail &&
+ git send-email --envelope-sender=auto --suppress-cc=sob --from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
+
+cat >expected <<\EOF
+!nobody@example.com!
+!-i!
+!nobody@example.com!
+!author@example.com!
+!one@example.com!
+!two@example.com!
+EOF
+test_expect_success \
+ 'Verify commandline' \
+ 'test_cmp expected commandline1'
+
+cat >expected-show-all-headers <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<cc@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<bcc@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: cc@example.com,
+ A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+In-Reply-To: <unique-message-id@example.com>
+References: <unique-message-id@example.com>
+
+Result: OK
+EOF
+
+test_expect_success 'Show all headers' '
+ git send-email \
+ --dry-run \
+ --suppress-cc=sob \
+ --from="Example <from@example.com>" \
+ --to=to@example.com \
+ --cc=cc@example.com \
+ --bcc=bcc@example.com \
+ --in-reply-to="<unique-message-id@example.com>" \
+ --smtp-server relay.example.com \
+ $patches |
+ sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \
+ -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+ -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
+ >actual-show-all-headers &&
+ test_cmp expected-show-all-headers actual-show-all-headers
+'
+
+test_expect_success 'Prompting works' '
+ clean_fake_sendmail &&
+ (echo "Example <from@example.com>"
+ echo "to@example.com"
+ echo ""
+ ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches \
+ 2>errors &&
+ grep "^From: Example <from@example.com>\$" msgtxt1 &&
+ grep "^To: to@example.com\$" msgtxt1
+'
+
+test_expect_success 'cccmd works' '
+ clean_fake_sendmail &&
+ cp $patches cccmd.patch &&
+ echo cccmd--cccmd@example.com >>cccmd.patch &&
+ {
+ echo "#!$SHELL_PATH"
+ echo sed -n -e s/^cccmd--//p \"\$1\"
+ } > cccmd-sed &&
+ chmod +x cccmd-sed &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --cc-cmd=./cccmd-sed \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ cccmd.patch \
+ &&
+ grep "^ cccmd@example.com" msgtxt1
+'
+
+z8=zzzzzzzz
+z64=$z8$z8$z8$z8$z8$z8$z8$z8
+z512=$z64$z64$z64$z64$z64$z64$z64$z64
+test_expect_success 'reject long lines' '
+ clean_fake_sendmail &&
+ cp $patches longline.patch &&
+ echo $z512$z512 >>longline.patch &&
+ test_must_fail git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches longline.patch \
+ 2>errors &&
+ grep longline.patch errors
+'
+
+test_expect_success 'no patch was sent' '
+ ! test -e commandline1
+'
+
+test_expect_success 'Author From: in message body' '
+ clean_fake_sendmail &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches &&
+ sed "1,/^\$/d" < msgtxt1 > msgbody1
+ grep "From: A <author@example.com>" msgbody1
+'
+
+test_expect_success 'Author From: not in message body' '
+ clean_fake_sendmail &&
+ git send-email \
+ --from="A <author@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches &&
+ sed "1,/^\$/d" < msgtxt1 > msgbody1
+ ! grep "From: A <author@example.com>" msgbody1
+'
+
+test_expect_success 'allow long lines with --no-validate' '
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --novalidate \
+ $patches longline.patch \
+ 2>errors
+'
+
+test_expect_success 'Invalid In-Reply-To' '
+ clean_fake_sendmail &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --in-reply-to=" " \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches
+ 2>errors
+ ! grep "^In-Reply-To: < *>" msgtxt1
+'
+
+test_expect_success 'Valid In-Reply-To when prompting' '
+ clean_fake_sendmail &&
+ (echo "From Example <from@example.com>"
+ echo "To Example <to@example.com>"
+ echo ""
+ ) | env GIT_SEND_EMAIL_NOTTY=1 git send-email \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches 2>errors &&
+ ! grep "^In-Reply-To: < *>" msgtxt1
+'
+
+test_expect_success 'setup fake editor' '
+ (echo "#!$SHELL_PATH" &&
+ echo "echo fake edit >>\"\$1\""
+ ) >fake-editor &&
+ chmod +x fake-editor
+'
+
+test_set_editor "$(pwd)/fake-editor"
+
+test_expect_success '--compose works' '
+ clean_fake_sendmail &&
+ git send-email \
+ --compose --subject foo \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches \
+ 2>errors
+'
+
+test_expect_success 'first message is compose text' '
+ grep "^fake edit" msgtxt1
+'
+
+test_expect_success 'second message is patch' '
+ grep "Subject:.*Second" msgtxt2
+'
+
+cat >expected-suppress-sob <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<cc@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: cc@example.com,
+ A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_suppression () {
+ git send-email \
+ --dry-run \
+ --suppress-cc=$1 ${2+"--suppress-cc=$2"} \
+ --from="Example <from@example.com>" \
+ --to=to@example.com \
+ --smtp-server relay.example.com \
+ $patches |
+ sed -e "s/^\(Date:\).*/\1 DATE-STRING/" \
+ -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+ -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
+ >actual-suppress-$1${2+"-$2"} &&
+ test_cmp expected-suppress-$1${2+"-$2"} actual-suppress-$1${2+"-$2"}
+}
+
+test_expect_success 'sendemail.cc set' '
+ git config sendemail.cc cc@example.com &&
+ test_suppression sob
+'
+
+cat >expected-suppress-sob <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success 'sendemail.cc unset' '
+ git config --unset sendemail.cc &&
+ test_suppression sob
+'
+
+cat >expected-suppress-cccmd <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+(body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<committer@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com,
+ C O Mitter <committer@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success 'sendemail.cccmd' '
+ echo echo cc-cmd@example.com > cccmd &&
+ chmod +x cccmd &&
+ git config sendemail.cccmd ./cccmd &&
+ test_suppression cccmd
+'
+
+cat >expected-suppress-all <<\EOF
+0001-Second.patch
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=all' '
+ test_suppression all
+'
+
+cat >expected-suppress-body <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+(cc-cmd) Adding cc: cc-cmd@example.com from: './cccmd'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<cc-cmd@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com,
+ cc-cmd@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=body' '
+ test_suppression body
+'
+
+cat >expected-suppress-body-cccmd <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=body --suppress-cc=cccmd' '
+ test_suppression body cccmd
+'
+
+cat >expected-suppress-sob <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=sob' '
+ git config --unset sendemail.cccmd
+ test_suppression sob
+'
+
+cat >expected-suppress-bodycc <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(mbox) Adding cc: One <one@example.com> from line 'Cc: One <one@example.com>, two@example.com'
+(mbox) Adding cc: two@example.com from line 'Cc: One <one@example.com>, two@example.com'
+(body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<committer@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com,
+ C O Mitter <committer@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=bodycc' '
+ test_suppression bodycc
+'
+
+cat >expected-suppress-cc <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+(body) Adding cc: C O Mitter <committer@example.com> from line 'Signed-off-by: C O Mitter <committer@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<committer@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>,
+ C O Mitter <committer@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success '--suppress-cc=cc' '
+ test_suppression cc
+'
+
+test_confirm () {
+ echo y | \
+ GIT_SEND_EMAIL_NOTTY=1 \
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $@ $patches > stdout &&
+ grep "Send this email" stdout
+}
+
+test_expect_success '--confirm=always' '
+ test_confirm --confirm=always --suppress-cc=all
+'
+
+test_expect_success '--confirm=auto' '
+ test_confirm --confirm=auto
+'
+
+test_expect_success '--confirm=cc' '
+ test_confirm --confirm=cc
+'
+
+test_expect_success '--confirm=compose' '
+ test_confirm --confirm=compose --compose
+'
+
+test_expect_success 'confirm by default (due to cc)' '
+ CONFIRM=$(git config --get sendemail.confirm) &&
+ git config --unset sendemail.confirm &&
+ test_confirm
+ ret="$?"
+ git config sendemail.confirm ${CONFIRM:-never}
+ test $ret = "0"
+'
+
+test_expect_success 'confirm by default (due to --compose)' '
+ CONFIRM=$(git config --get sendemail.confirm) &&
+ git config --unset sendemail.confirm &&
+ test_confirm --suppress-cc=all --compose
+ ret="$?"
+ git config sendemail.confirm ${CONFIRM:-never}
+ test $ret = "0"
+'
+
+test_expect_success 'confirm detects EOF (inform assumes y)' '
+ CONFIRM=$(git config --get sendemail.confirm) &&
+ git config --unset sendemail.confirm &&
+ rm -fr outdir &&
+ git format-patch -2 -o outdir &&
+ GIT_SEND_EMAIL_NOTTY=1 \
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ outdir/*.patch < /dev/null
+ ret="$?"
+ git config sendemail.confirm ${CONFIRM:-never}
+ test $ret = "0"
+'
+
+test_expect_success 'confirm detects EOF (auto causes failure)' '
+ CONFIRM=$(git config --get sendemail.confirm) &&
+ git config sendemail.confirm auto &&
+ GIT_SEND_EMAIL_NOTTY=1 &&
+ export GIT_SEND_EMAIL_NOTTY &&
+ test_must_fail git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches < /dev/null
+ ret="$?"
+ git config sendemail.confirm ${CONFIRM:-never}
+ test $ret = "0"
+'
+
+test_expect_success 'confirm doesnt loop forever' '
+ CONFIRM=$(git config --get sendemail.confirm) &&
+ git config sendemail.confirm auto &&
+ GIT_SEND_EMAIL_NOTTY=1 &&
+ export GIT_SEND_EMAIL_NOTTY &&
+ yes "bogus" | test_must_fail git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches
+ ret="$?"
+ git config sendemail.confirm ${CONFIRM:-never}
+ test $ret = "0"
+'
+
+test_expect_success 'utf8 Cc is rfc2047 encoded' '
+ clean_fake_sendmail &&
+ rm -fr outdir &&
+ git format-patch -1 -o outdir --cc="àéìöú <utf8@example.com>" &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ outdir/*.patch &&
+ grep "^ " msgtxt1 |
+ grep "=?UTF-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>"
+'
+
+test_expect_success '--compose adds MIME for utf8 body' '
+ clean_fake_sendmail &&
+ (echo "#!$SHELL_PATH" &&
+ echo "echo utf8 body: àéìöú >>\"\$1\""
+ ) >fake-editor-utf8 &&
+ chmod +x fake-editor-utf8 &&
+ GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \
+ git send-email \
+ --compose --subject foo \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches &&
+ grep "^utf8 body" msgtxt1 &&
+ grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1
+'
+
+test_expect_success '--compose respects user mime type' '
+ clean_fake_sendmail &&
+ (echo "#!$SHELL_PATH" &&
+ echo "(echo MIME-Version: 1.0"
+ echo " echo Content-Type: text/plain\\; charset=iso-8859-1"
+ echo " echo Content-Transfer-Encoding: 8bit"
+ echo " echo Subject: foo"
+ echo " echo "
+ echo " echo utf8 body: àéìöú) >\"\$1\""
+ ) >fake-editor-utf8-mime &&
+ chmod +x fake-editor-utf8-mime &&
+ GIT_EDITOR="\"$(pwd)/fake-editor-utf8-mime\"" \
+ git send-email \
+ --compose --subject foo \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches &&
+ grep "^utf8 body" msgtxt1 &&
+ grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 &&
+ ! grep "^Content-Type: text/plain; charset=UTF-8" msgtxt1
+'
+
+test_expect_success '--compose adds MIME for utf8 subject' '
+ clean_fake_sendmail &&
+ GIT_EDITOR="\"$(pwd)/fake-editor\"" \
+ git send-email \
+ --compose --subject utf8-sübjëct \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ $patches &&
+ grep "^fake edit" msgtxt1 &&
+ grep "^Subject: =?UTF-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
+'
+
+test_expect_success 'detects ambiguous reference/file conflict' '
+ echo master > master &&
+ git add master &&
+ git commit -m"add master" &&
+ test_must_fail git send-email --dry-run master 2>errors &&
+ grep disambiguate errors
+'
+
+test_expect_success 'feed two files' '
+ rm -fr outdir &&
+ git format-patch -2 -o outdir &&
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ outdir/000?-*.patch 2>errors >out &&
+ grep "^Subject: " out >subjects &&
+ test "z$(sed -n -e 1p subjects)" = "zSubject: [PATCH 1/2] Second." &&
+ test "z$(sed -n -e 2p subjects)" = "zSubject: [PATCH 2/2] add master"
+'
+
+test_expect_success 'in-reply-to but no threading' '
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --in-reply-to="<in-reply-id@example.com>" \
+ --nothread \
+ $patches |
+ grep "In-Reply-To: <in-reply-id@example.com>"
+'
+
+test_expect_success 'no in-reply-to and no threading' '
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --nothread \
+ $patches $patches >stdout &&
+ ! grep "In-Reply-To: " stdout
+'
+
+test_expect_success 'threading but no chain-reply-to' '
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --thread \
+ --nochain-reply-to \
+ $patches $patches >stdout &&
+ grep "In-Reply-To: " stdout
+'
+
+test_expect_success 'warning with an implicit --chain-reply-to' '
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ outdir/000?-*.patch 2>errors >out &&
+ grep "no-chain-reply-to" errors
+'
+
+test_expect_success 'no warning with an explicit --chain-reply-to' '
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --chain-reply-to \
+ outdir/000?-*.patch 2>errors >out &&
+ ! grep "no-chain-reply-to" errors
+'
+
+test_expect_success 'no warning with an explicit --no-chain-reply-to' '
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --nochain-reply-to \
+ outdir/000?-*.patch 2>errors >out &&
+ ! grep "no-chain-reply-to" errors
+'
+
+test_expect_success 'no warning with sendemail.chainreplyto = false' '
+ git config sendemail.chainreplyto false &&
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ outdir/000?-*.patch 2>errors >out &&
+ ! grep "no-chain-reply-to" errors
+'
+
+test_expect_success 'no warning with sendemail.chainreplyto = true' '
+ git config sendemail.chainreplyto true &&
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ outdir/000?-*.patch 2>errors >out &&
+ ! grep "no-chain-reply-to" errors
+'
+
+test_expect_success 'sendemail.to works' '
+ git config --replace-all sendemail.to "Somebody <somebody@ex.com>" &&
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ $patches $patches >stdout &&
+ grep "To: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success '--no-to overrides sendemail.to' '
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --no-to \
+ --to=nobody@example.com \
+ $patches $patches >stdout &&
+ grep "To: nobody@example.com" stdout &&
+ ! grep "To: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success 'sendemail.cc works' '
+ git config --replace-all sendemail.cc "Somebody <somebody@ex.com>" &&
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ $patches $patches >stdout &&
+ grep "Cc: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success '--no-cc overrides sendemail.cc' '
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --no-cc \
+ --cc=bodies@example.com \
+ --to=nobody@example.com \
+ $patches $patches >stdout &&
+ grep "Cc: bodies@example.com" stdout &&
+ ! grep "Cc: Somebody <somebody@ex.com>" stdout
+'
+
+test_expect_success 'sendemail.bcc works' '
+ git config --replace-all sendemail.bcc "Other <other@ex.com>" &&
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server relay.example.com \
+ $patches $patches >stdout &&
+ grep "RCPT TO:<other@ex.com>" stdout
+'
+
+test_expect_success '--no-bcc overrides sendemail.bcc' '
+ git send-email \
+ --dry-run \
+ --from="Example <nobody@example.com>" \
+ --no-bcc \
+ --bcc=bodies@example.com \
+ --to=nobody@example.com \
+ --smtp-server relay.example.com \
+ $patches $patches >stdout &&
+ grep "RCPT TO:<bodies@example.com>" stdout &&
+ ! grep "RCPT TO:<other@ex.com>" stdout
+'
+
+cat >email-using-8bit <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: author@example.com
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+Subject: subject goes here
+
+Dieser deutsche Text enthält einen Umlaut!
+EOF
+
+cat >content-type-decl <<EOF
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+EOF
+
+test_expect_success 'asks about and fixes 8bit encodings' '
+ clean_fake_sendmail &&
+ echo |
+ git send-email --from=author@example.com --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-8bit >stdout &&
+ grep "do not declare a Content-Transfer-Encoding" stdout &&
+ grep email-using-8bit stdout &&
+ grep "Which 8bit encoding" stdout &&
+ egrep "Content|MIME" msgtxt1 >actual &&
+ test_cmp actual content-type-decl
+'
+
+test_expect_success 'sendemail.8bitEncoding works' '
+ clean_fake_sendmail &&
+ git config sendemail.assume8bitEncoding UTF-8 &&
+ echo bogus |
+ git send-email --from=author@example.com --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ email-using-8bit >stdout &&
+ egrep "Content|MIME" msgtxt1 >actual &&
+ test_cmp actual content-type-decl
+'
+
+test_expect_success '--8bit-encoding overrides sendemail.8bitEncoding' '
+ clean_fake_sendmail &&
+ git config sendemail.assume8bitEncoding "bogus too" &&
+ echo bogus |
+ git send-email --from=author@example.com --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --8bit-encoding=UTF-8 \
+ email-using-8bit >stdout &&
+ egrep "Content|MIME" msgtxt1 >actual &&
+ test_cmp actual content-type-decl
+'
+
+cat >email-using-8bit <<EOF
+From fe6ecc66ece37198fe5db91fa2fc41d9f4fe5cc4 Mon Sep 17 00:00:00 2001
+Message-Id: <bogus-message-id@example.com>
+From: author@example.com
+Date: Sat, 12 Jun 2010 15:53:58 +0200
+Subject: Dieser Betreff enthält auch einen Umlaut!
+
+Nothing to see here.
+EOF
+
+cat >expected <<EOF
+Subject: =?UTF-8?q?Dieser=20Betreff=20enth=C3=A4lt=20auch=20einen=20Umlaut!?=
+EOF
+
+test_expect_success '--8bit-encoding also treats subject' '
+ clean_fake_sendmail &&
+ echo bogus |
+ git send-email --from=author@example.com --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ --8bit-encoding=UTF-8 \
+ email-using-8bit >stdout &&
+ grep "Subject" msgtxt1 >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
new file mode 100755
index 0000000..13766ab
--- /dev/null
+++ b/t/t9100-git-svn-basic.sh
@@ -0,0 +1,291 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='git svn basic tests'
+GIT_SVN_LC_ALL=${LC_ALL:-$LANG}
+
+. ./lib-git-svn.sh
+
+say 'define NO_SVN_TESTS to skip git svn tests'
+
+case "$GIT_SVN_LC_ALL" in
+*.UTF-8)
+ test_set_prereq UTF8
+ ;;
+*)
+ say "# UTF-8 locale not set, some tests skipped ($GIT_SVN_LC_ALL)"
+ ;;
+esac
+
+test_expect_success \
+ 'initialize git svn' '
+ mkdir import &&
+ cd import &&
+ echo foo > foo &&
+ ln -s foo foo.link
+ mkdir -p dir/a/b/c/d/e &&
+ echo "deep dir" > dir/a/b/c/d/e/file &&
+ mkdir bar &&
+ echo "zzz" > bar/zzz &&
+ echo "#!/bin/sh" > exec.sh &&
+ chmod +x exec.sh &&
+ svn_cmd import -m "import for git svn" . "$svnrepo" >/dev/null &&
+ cd .. &&
+ rm -rf import &&
+ git svn init "$svnrepo"'
+
+test_expect_success \
+ 'import an SVN revision into git' \
+ 'git svn fetch'
+
+test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"'
+
+name='try a deep --rmdir with a commit'
+test_expect_success "$name" '
+ git checkout -f -b mybranch ${remotes_git_svn} &&
+ mv dir/a/b/c/d/e/file dir/file &&
+ cp dir/file file &&
+ git update-index --add --remove dir/a/b/c/d/e/file dir/file file &&
+ git commit -m "$name" &&
+ git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch &&
+ svn_cmd up "$SVN_TREE" &&
+ test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a'
+
+
+name='detect node change from file to directory #1'
+test_expect_success "$name" "
+ mkdir dir/new_file &&
+ mv dir/file dir/new_file/file &&
+ mv dir/new_file dir/file &&
+ git update-index --remove dir/file &&
+ git update-index --add dir/file/file &&
+ git commit -m '$name' &&
+ test_must_fail git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch" || true
+
+
+name='detect node change from directory to file #1'
+test_expect_success "$name" '
+ rm -rf dir "$GIT_DIR"/index &&
+ git checkout -f -b mybranch2 ${remotes_git_svn} &&
+ mv bar/zzz zzz &&
+ rm -rf bar &&
+ mv zzz bar &&
+ git update-index --remove -- bar/zzz &&
+ git update-index --add -- bar &&
+ git commit -m "$name" &&
+ test_must_fail git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch2' || true
+
+
+name='detect node change from file to directory #2'
+test_expect_success "$name" '
+ rm -f "$GIT_DIR"/index &&
+ git checkout -f -b mybranch3 ${remotes_git_svn} &&
+ rm bar/zzz &&
+ git update-index --remove bar/zzz &&
+ mkdir bar/zzz &&
+ echo yyy > bar/zzz/yyy &&
+ git update-index --add bar/zzz/yyy &&
+ git commit -m "$name" &&
+ test_must_fail git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch3' || true
+
+
+name='detect node change from directory to file #2'
+test_expect_success "$name" '
+ rm -f "$GIT_DIR"/index &&
+ git checkout -f -b mybranch4 ${remotes_git_svn} &&
+ rm -rf dir &&
+ git update-index --remove -- dir/file &&
+ touch dir &&
+ echo asdf > dir &&
+ git update-index --add -- dir &&
+ git commit -m "$name" &&
+ test_must_fail git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch4' || true
+
+
+name='remove executable bit from a file'
+test_expect_success "$name" '
+ rm -f "$GIT_DIR"/index &&
+ git checkout -f -b mybranch5 ${remotes_git_svn} &&
+ chmod -x exec.sh &&
+ git update-index exec.sh &&
+ git commit -m "$name" &&
+ git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch5 &&
+ svn_cmd up "$SVN_TREE" &&
+ test ! -x "$SVN_TREE"/exec.sh'
+
+
+name='add executable bit back file'
+test_expect_success "$name" '
+ chmod +x exec.sh &&
+ git update-index exec.sh &&
+ git commit -m "$name" &&
+ git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch5 &&
+ svn_cmd up "$SVN_TREE" &&
+ test -x "$SVN_TREE"/exec.sh'
+
+
+name='executable file becomes a symlink to bar/zzz (file)'
+test_expect_success "$name" '
+ rm exec.sh &&
+ ln -s bar/zzz exec.sh &&
+ git update-index exec.sh &&
+ git commit -m "$name" &&
+ git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch5 &&
+ svn_cmd up "$SVN_TREE" &&
+ test -L "$SVN_TREE"/exec.sh'
+
+name='new symlink is added to a file that was also just made executable'
+
+test_expect_success "$name" '
+ chmod +x bar/zzz &&
+ ln -s bar/zzz exec-2.sh &&
+ git update-index --add bar/zzz exec-2.sh &&
+ git commit -m "$name" &&
+ git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch5 &&
+ svn_cmd up "$SVN_TREE" &&
+ test -x "$SVN_TREE"/bar/zzz &&
+ test -L "$SVN_TREE"/exec-2.sh'
+
+name='modify a symlink to become a file'
+test_expect_success "$name" '
+ echo git help > help || true &&
+ rm exec-2.sh &&
+ cp help exec-2.sh &&
+ git update-index exec-2.sh &&
+ git commit -m "$name" &&
+ git svn set-tree --find-copies-harder --rmdir \
+ ${remotes_git_svn}..mybranch5 &&
+ svn_cmd up "$SVN_TREE" &&
+ test -f "$SVN_TREE"/exec-2.sh &&
+ test ! -L "$SVN_TREE"/exec-2.sh &&
+ test_cmp help "$SVN_TREE"/exec-2.sh'
+
+name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
+LC_ALL="$GIT_SVN_LC_ALL"
+export LC_ALL
+test_expect_success UTF8 "$name" "
+ echo '# hello' >> exec-2.sh &&
+ git update-index exec-2.sh &&
+ git commit -m 'éïâˆ' &&
+ git svn set-tree HEAD"
+unset LC_ALL
+
+name='test fetch functionality (svn => git) with alternate GIT_SVN_ID'
+GIT_SVN_ID=alt
+export GIT_SVN_ID
+test_expect_success "$name" \
+ 'git svn init "$svnrepo" && git svn fetch &&
+ git rev-list --pretty=raw ${remotes_git_svn} | grep ^tree | uniq > a &&
+ git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
+ test_cmp a b'
+
+name='check imported tree checksums expected tree checksums'
+rm -f expected
+if test_have_prereq UTF8
+then
+ echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected
+fi
+cat >> expected <<\EOF
+tree 83654bb36f019ae4fe77a0171f81075972087624
+tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1
+tree 0b094cbff17168f24c302e297f55bfac65eb8bd3
+tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
+tree 56a30b966619b863674f5978696f4a3594f2fca9
+tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
+tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
+EOF
+
+test_expect_success "$name" "test_cmp a expected"
+
+test_expect_success 'exit if remote refs are ambigious' "
+ git config --add svn-remote.svn.fetch \
+ bar:refs/${remotes_git_svn} &&
+ test_must_fail git svn migrate
+"
+
+test_expect_success 'exit if init-ing a would clobber a URL' '
+ svnadmin create "${PWD}/svnrepo2" &&
+ svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
+ git config --unset svn-remote.svn.fetch \
+ "^bar:refs/${remotes_git_svn}$" &&
+ test_must_fail git svn init "${svnrepo}2/bar"
+ '
+
+test_expect_success \
+ 'init allows us to connect to another directory in the same repo' '
+ git svn init --minimize-url -i bar "$svnrepo/bar" &&
+ git config --get svn-remote.svn.fetch \
+ "^bar:refs/remotes/bar$" &&
+ git config --get svn-remote.svn.fetch \
+ "^:refs/${remotes_git_svn}$"
+ '
+
+test_expect_success 'dcommit $rev does not clobber current branch' '
+ git svn fetch -i bar &&
+ git checkout -b my-bar refs/remotes/bar &&
+ echo 1 > foo &&
+ git add foo &&
+ git commit -m "change 1" &&
+ echo 2 > foo &&
+ git add foo &&
+ git commit -m "change 2" &&
+ old_head=$(git rev-parse HEAD) &&
+ git svn dcommit -i bar HEAD^ &&
+ test $old_head = $(git rev-parse HEAD) &&
+ test refs/heads/my-bar = $(git symbolic-ref HEAD) &&
+ git log refs/remotes/bar | grep "change 1" &&
+ ! git log refs/remotes/bar | grep "change 2" &&
+ git checkout master &&
+ git branch -D my-bar
+ '
+
+test_expect_success 'able to dcommit to a subdirectory' "
+ git svn fetch -i bar &&
+ git checkout -b my-bar refs/remotes/bar &&
+ echo abc > d &&
+ git update-index --add d &&
+ git commit -m '/bar/d should be in the log' &&
+ git svn dcommit -i bar &&
+ test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\" &&
+ mkdir newdir &&
+ echo new > newdir/dir &&
+ git update-index --add newdir/dir &&
+ git commit -m 'add a new directory' &&
+ git svn dcommit -i bar &&
+ test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\" &&
+ echo foo >> newdir/dir &&
+ git update-index newdir/dir &&
+ git commit -m 'modify a file in new directory' &&
+ git svn dcommit -i bar &&
+ test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\"
+ "
+
+test_expect_success 'able to set-tree to a subdirectory' "
+ echo cba > d &&
+ git update-index d &&
+ git commit -m 'update /bar/d' &&
+ git svn set-tree -i bar HEAD &&
+ test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\"
+ "
+
+test_expect_success 'git-svn works in a bare repository' '
+ mkdir bare-repo &&
+ ( cd bare-repo &&
+ git init --bare &&
+ GIT_DIR=. git svn init "$svnrepo" &&
+ git svn fetch ) &&
+ rm -rf bare-repo
+ '
+
+test_done
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
new file mode 100755
index 0000000..929499e
--- /dev/null
+++ b/t/t9101-git-svn-props.sh
@@ -0,0 +1,220 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='git svn property tests'
+. ./lib-git-svn.sh
+
+mkdir import
+
+a_crlf=
+a_lf=
+a_cr=
+a_ne_crlf=
+a_ne_lf=
+a_ne_cr=
+a_empty=
+a_empty_lf=
+a_empty_cr=
+a_empty_crlf=
+
+cd import
+ cat >> kw.c <<\EOF
+/* Somebody prematurely put a keyword into this file */
+/* $Id$ */
+EOF
+
+ printf "Hello\r\nWorld\r\n" > crlf
+ a_crlf=`git hash-object -w crlf`
+ printf "Hello\rWorld\r" > cr
+ a_cr=`git hash-object -w cr`
+ printf "Hello\nWorld\n" > lf
+ a_lf=`git hash-object -w lf`
+
+ printf "Hello\r\nWorld" > ne_crlf
+ a_ne_crlf=`git hash-object -w ne_crlf`
+ printf "Hello\nWorld" > ne_lf
+ a_ne_lf=`git hash-object -w ne_lf`
+ printf "Hello\rWorld" > ne_cr
+ a_ne_cr=`git hash-object -w ne_cr`
+
+ touch empty
+ a_empty=`git hash-object -w empty`
+ printf "\n" > empty_lf
+ a_empty_lf=`git hash-object -w empty_lf`
+ printf "\r" > empty_cr
+ a_empty_cr=`git hash-object -w empty_cr`
+ printf "\r\n" > empty_crlf
+ a_empty_crlf=`git hash-object -w empty_crlf`
+
+ svn_cmd import --no-auto-props -m 'import for git svn' . "$svnrepo" >/dev/null
+cd ..
+
+rm -rf import
+test_expect_success 'checkout working copy from svn' 'svn co "$svnrepo" test_wc'
+test_expect_success 'setup some commits to svn' \
+ 'cd test_wc &&
+ echo Greetings >> kw.c &&
+ poke kw.c &&
+ svn_cmd commit -m "Not yet an Id" &&
+ echo Hello world >> kw.c &&
+ poke kw.c &&
+ svn_cmd commit -m "Modified file, but still not yet an Id" &&
+ svn_cmd propset svn:keywords Id kw.c &&
+ poke kw.c &&
+ svn_cmd commit -m "Propset Id" &&
+ cd ..'
+
+test_expect_success 'initialize git svn' 'git svn init "$svnrepo"'
+test_expect_success 'fetch revisions from svn' 'git svn fetch'
+
+name='test svn:keywords ignoring'
+test_expect_success "$name" \
+ 'git checkout -b mybranch ${remotes_git_svn} &&
+ echo Hi again >> kw.c &&
+ git commit -a -m "test keywords ignoring" &&
+ git svn set-tree ${remotes_git_svn}..mybranch &&
+ git pull . ${remotes_git_svn}'
+
+expect='/* $Id$ */'
+got="`sed -ne 2p kw.c`"
+test_expect_success 'raw $Id$ found in kw.c' "test '$expect' = '$got'"
+
+test_expect_success "propset CR on crlf files" \
+ 'cd test_wc &&
+ svn_cmd propset svn:eol-style CR empty &&
+ svn_cmd propset svn:eol-style CR crlf &&
+ svn_cmd propset svn:eol-style CR ne_crlf &&
+ svn_cmd commit -m "propset CR on crlf files" &&
+ cd ..'
+
+test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
+ 'git svn fetch &&
+ git pull . ${remotes_git_svn} &&
+ svn_cmd co "$svnrepo" new_wc'
+
+for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf
+do
+ test_expect_success "Comparing $i" "cmp $i new_wc/$i"
+done
+
+
+cd test_wc
+ printf '$Id$\rHello\rWorld\r' > cr
+ printf '$Id$\rHello\rWorld' > ne_cr
+ a_cr=`printf '$Id$\r\nHello\r\nWorld\r\n' | git hash-object --stdin`
+ a_ne_cr=`printf '$Id$\r\nHello\r\nWorld' | git hash-object --stdin`
+ test_expect_success 'Set CRLF on cr files' \
+ 'svn_cmd propset svn:eol-style CRLF cr &&
+ svn_cmd propset svn:eol-style CRLF ne_cr &&
+ svn_cmd propset svn:keywords Id cr &&
+ svn_cmd propset svn:keywords Id ne_cr &&
+ svn_cmd commit -m "propset CRLF on cr files"'
+cd ..
+test_expect_success 'fetch and pull latest from svn' \
+ 'git svn fetch && git pull . ${remotes_git_svn}'
+
+b_cr="`git hash-object cr`"
+b_ne_cr="`git hash-object ne_cr`"
+
+test_expect_success 'CRLF + $Id$' "test '$a_cr' = '$b_cr'"
+test_expect_success 'CRLF + $Id$ (no newline)' "test '$a_ne_cr' = '$b_ne_cr'"
+
+cat > show-ignore.expect <<\EOF
+
+# /
+/no-such-file*
+
+# /deeply/
+/deeply/no-such-file*
+
+# /deeply/nested/
+/deeply/nested/no-such-file*
+
+# /deeply/nested/directory/
+/deeply/nested/directory/no-such-file*
+EOF
+
+test_expect_success 'test show-ignore' "
+ cd test_wc &&
+ mkdir -p deeply/nested/directory &&
+ touch deeply/nested/directory/.keep &&
+ svn_cmd add deeply &&
+ svn_cmd up &&
+ svn_cmd propset -R svn:ignore '
+no-such-file*
+' .
+ svn_cmd commit -m 'propset svn:ignore'
+ cd .. &&
+ git svn show-ignore > show-ignore.got &&
+ cmp show-ignore.expect show-ignore.got
+ "
+
+cat >create-ignore.expect <<\EOF
+/no-such-file*
+EOF
+
+cat >create-ignore-index.expect <<\EOF
+100644 8c52e5dfcd0a8b6b6bcfe6b41b89bcbf493718a5 0 .gitignore
+100644 8c52e5dfcd0a8b6b6bcfe6b41b89bcbf493718a5 0 deeply/.gitignore
+100644 8c52e5dfcd0a8b6b6bcfe6b41b89bcbf493718a5 0 deeply/nested/.gitignore
+100644 8c52e5dfcd0a8b6b6bcfe6b41b89bcbf493718a5 0 deeply/nested/directory/.gitignore
+EOF
+
+test_expect_success 'test create-ignore' "
+ git svn fetch && git pull . ${remotes_git_svn} &&
+ git svn create-ignore &&
+ cmp ./.gitignore create-ignore.expect &&
+ cmp ./deeply/.gitignore create-ignore.expect &&
+ cmp ./deeply/nested/.gitignore create-ignore.expect &&
+ cmp ./deeply/nested/directory/.gitignore create-ignore.expect &&
+ git ls-files -s | grep gitignore | cmp - create-ignore-index.expect
+ "
+
+cat >prop.expect <<\EOF
+
+no-such-file*
+
+EOF
+cat >prop2.expect <<\EOF
+8
+EOF
+
+# This test can be improved: since all the svn:ignore contain the same
+# pattern, it can pass even though the propget did not execute on the
+# right directory.
+test_expect_success 'test propget' "
+ git svn propget svn:ignore . | cmp - prop.expect &&
+ cd deeply &&
+ git svn propget svn:ignore . | cmp - ../prop.expect &&
+ git svn propget svn:entry:committed-rev nested/directory/.keep \
+ | cmp - ../prop2.expect &&
+ git svn propget svn:ignore .. | cmp - ../prop.expect &&
+ git svn propget svn:ignore nested/ | cmp - ../prop.expect &&
+ git svn propget svn:ignore ./nested | cmp - ../prop.expect &&
+ git svn propget svn:ignore .././deeply/nested | cmp - ../prop.expect
+ "
+
+cat >prop.expect <<\EOF
+Properties on '.':
+ svn:entry:committed-date
+ svn:entry:committed-rev
+ svn:entry:last-author
+ svn:entry:uuid
+ svn:ignore
+EOF
+cat >prop2.expect <<\EOF
+Properties on 'nested/directory/.keep':
+ svn:entry:committed-date
+ svn:entry:committed-rev
+ svn:entry:last-author
+ svn:entry:uuid
+EOF
+
+test_expect_success 'test proplist' "
+ git svn proplist . | cmp - prop.expect &&
+ git svn proplist nested/directory/.keep | cmp - prop2.expect
+ "
+
+test_done
diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh
new file mode 100755
index 0000000..028fb19
--- /dev/null
+++ b/t/t9102-git-svn-deep-rmdir.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+test_description='git svn rmdir'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+ mkdir import &&
+ cd import &&
+ mkdir -p deeply/nested/directory/number/1 &&
+ mkdir -p deeply/nested/directory/number/2 &&
+ echo foo > deeply/nested/directory/number/1/file &&
+ echo foo > deeply/nested/directory/number/2/another &&
+ svn_cmd import -m "import for git svn" . "$svnrepo" &&
+ cd ..
+ '
+
+test_expect_success 'mirror via git svn' '
+ git svn init "$svnrepo" &&
+ git svn fetch &&
+ git checkout -f -b test-rmdir ${remotes_git_svn}
+ '
+
+test_expect_success 'Try a commit on rmdir' '
+ git rm -f deeply/nested/directory/number/2/another &&
+ git commit -a -m "remove another" &&
+ git svn set-tree --rmdir HEAD &&
+ svn_cmd ls -R "$svnrepo" | grep ^deeply/nested/directory/number/1
+ '
+
+
+test_done
diff --git a/t/t9103-git-svn-tracked-directory-removed.sh b/t/t9103-git-svn-tracked-directory-removed.sh
new file mode 100755
index 0000000..3413164
--- /dev/null
+++ b/t/t9103-git-svn-tracked-directory-removed.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git svn tracking removed top-level path'
+. ./lib-git-svn.sh
+
+test_expect_success 'make history for tracking' '
+ mkdir import &&
+ mkdir import/trunk &&
+ echo hello >> import/trunk/README &&
+ svn_cmd import -m initial import "$svnrepo" &&
+ rm -rf import &&
+ svn_cmd co "$svnrepo"/trunk trunk &&
+ echo bye bye >> trunk/README &&
+ svn_cmd rm -m "gone" "$svnrepo"/trunk &&
+ rm -rf trunk &&
+ mkdir trunk &&
+ echo "new" > trunk/FOLLOWME &&
+ svn_cmd import -m "new trunk" trunk "$svnrepo"/trunk
+'
+
+test_expect_success 'clone repo with git' '
+ git svn clone -s "$svnrepo" x &&
+ test -f x/FOLLOWME &&
+ test ! -f x/README
+'
+
+test_expect_success 'make sure r2 still has old file' "
+ cd x &&
+ test -n \"\$(git svn find-rev r1)\" &&
+ git reset --hard \$(git svn find-rev r1) &&
+ test -f README &&
+ test ! -f FOLLOWME &&
+ test x\$(git svn find-rev r2) = x
+"
+
+test_done
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
new file mode 100755
index 0000000..bbfd7f4
--- /dev/null
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -0,0 +1,213 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='git svn fetching'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+ mkdir import &&
+ cd import &&
+ mkdir -p trunk &&
+ echo hello > trunk/readme &&
+ svn_cmd import -m "initial" . "$svnrepo" &&
+ cd .. &&
+ svn_cmd co "$svnrepo" wc &&
+ cd wc &&
+ echo world >> trunk/readme &&
+ poke trunk/readme &&
+ svn_cmd commit -m "another commit" &&
+ svn_cmd up &&
+ svn_cmd mv trunk thunk &&
+ echo goodbye >> thunk/readme &&
+ poke thunk/readme &&
+ svn_cmd commit -m "bye now" &&
+ cd ..
+ '
+
+test_expect_success 'init and fetch a moved directory' '
+ git svn init --minimize-url -i thunk "$svnrepo"/thunk &&
+ git svn fetch -i thunk &&
+ test "`git rev-parse --verify refs/remotes/thunk@2`" \
+ = "`git rev-parse --verify refs/remotes/thunk~1`" &&
+ test "`git cat-file blob refs/remotes/thunk:readme |\
+ sed -n -e "3p"`" = goodbye &&
+ test -z "`git config --get svn-remote.svn.fetch \
+ "^trunk:refs/remotes/thunk@2$"`"
+ '
+
+test_expect_success 'init and fetch from one svn-remote' '
+ git config svn-remote.svn.url "$svnrepo" &&
+ git config --add svn-remote.svn.fetch \
+ trunk:refs/remotes/svn/trunk &&
+ git config --add svn-remote.svn.fetch \
+ thunk:refs/remotes/svn/thunk &&
+ git svn fetch -i svn/thunk &&
+ test "`git rev-parse --verify refs/remotes/svn/trunk`" \
+ = "`git rev-parse --verify refs/remotes/svn/thunk~1`" &&
+ test "`git cat-file blob refs/remotes/svn/thunk:readme |\
+ sed -n -e "3p"`" = goodbye
+ '
+
+test_expect_success 'follow deleted parent' '
+ (svn_cmd cp -m "resurrecting trunk as junk" \
+ "$svnrepo"/trunk@2 "$svnrepo"/junk ||
+ svn cp -m "resurrecting trunk as junk" \
+ -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
+ git config --add svn-remote.svn.fetch \
+ junk:refs/remotes/svn/junk &&
+ git svn fetch -i svn/thunk &&
+ git svn fetch -i svn/junk &&
+ test -z "`git diff svn/junk svn/trunk`" &&
+ test "`git merge-base svn/junk svn/trunk`" \
+ = "`git rev-parse svn/trunk`"
+ '
+
+test_expect_success 'follow larger parent' '
+ mkdir -p import/trunk/thunk/bump/thud &&
+ echo hi > import/trunk/thunk/bump/thud/file &&
+ svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
+ svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
+ git svn init --minimize-url -i larger \
+ "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
+ git svn fetch -i larger &&
+ git rev-parse --verify refs/remotes/larger &&
+ git rev-parse --verify \
+ refs/remotes/larger-parent/trunk/thunk/bump/thud &&
+ test "`git merge-base \
+ refs/remotes/larger-parent/trunk/thunk/bump/thud \
+ refs/remotes/larger`" = \
+ "`git rev-parse refs/remotes/larger`"
+ true
+ '
+
+test_expect_success 'follow higher-level parent' '
+ svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
+ svn co "$svnrepo"/blob blob &&
+ cd blob &&
+ echo hi > hi &&
+ svn add hi &&
+ svn commit -m "hihi" &&
+ cd ..
+ svn mkdir -m "new glob at top level" "$svnrepo"/glob &&
+ svn mv -m "move blob down a level" "$svnrepo"/blob "$svnrepo"/glob/blob &&
+ git svn init --minimize-url -i blob "$svnrepo"/glob/blob &&
+ git svn fetch -i blob
+ '
+
+test_expect_success 'follow deleted directory' '
+ svn_cmd mv -m "bye!" "$svnrepo"/glob/blob/hi "$svnrepo"/glob/blob/bye &&
+ svn_cmd rm -m "remove glob" "$svnrepo"/glob &&
+ git svn init --minimize-url -i glob "$svnrepo"/glob &&
+ git svn fetch -i glob &&
+ test "`git cat-file blob refs/remotes/glob:blob/bye`" = hi &&
+ test "`git ls-tree refs/remotes/glob | wc -l `" -eq 1
+ '
+
+# ref: r9270 of the Subversion repository: (http://svn.collab.net/repos/svn)
+# in trunk/subversion/bindings/swig/perl
+test_expect_success 'follow-parent avoids deleting relevant info' '
+ mkdir -p import/trunk/subversion/bindings/swig/perl/t &&
+ for i in a b c ; do \
+ echo $i > import/trunk/subversion/bindings/swig/perl/$i.pm &&
+ echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t; \
+ done &&
+ echo "bad delete test" > \
+ import/trunk/subversion/bindings/swig/perl/t/larger-parent &&
+ echo "bad delete test 2" > \
+ import/trunk/subversion/bindings/swig/perl/another-larger &&
+ cd import &&
+ svn import -m "r9270 test" . "$svnrepo"/r9270 &&
+ cd .. &&
+ svn_cmd co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 &&
+ cd r9270 &&
+ svn mkdir native &&
+ svn mv t native/t &&
+ for i in a b c; do svn mv $i.pm native/$i.pm; done &&
+ echo z >> native/t/c.t &&
+ poke native/t/c.t &&
+ svn commit -m "reorg test" &&
+ cd .. &&
+ git svn init --minimize-url -i r9270-t \
+ "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl/native/t &&
+ git svn fetch -i r9270-t &&
+ test `git rev-list r9270-t | wc -l` -eq 2 &&
+ test "`git ls-tree --name-only r9270-t~1`" = \
+ "`git ls-tree --name-only r9270-t`"
+ '
+
+test_expect_success "track initial change if it was only made to parent" '
+ svn_cmd cp -m "wheee!" "$svnrepo"/r9270/trunk "$svnrepo"/r9270/drunk &&
+ git svn init --minimize-url -i r9270-d \
+ "$svnrepo"/r9270/drunk/subversion/bindings/swig/perl/native/t &&
+ git svn fetch -i r9270-d &&
+ test `git rev-list r9270-d | wc -l` -eq 3 &&
+ test "`git ls-tree --name-only r9270-t`" = \
+ "`git ls-tree --name-only r9270-d`" &&
+ test "`git rev-parse r9270-t`" = \
+ "`git rev-parse r9270-d~1`"
+ '
+
+test_expect_success "follow-parent is atomic" '
+ (
+ cd wc &&
+ svn_cmd up &&
+ svn_cmd mkdir stunk &&
+ echo "trunk stunk" > stunk/readme &&
+ svn_cmd add stunk/readme &&
+ svn_cmd ci -m "trunk stunk" &&
+ echo "stunk like junk" >> stunk/readme &&
+ svn_cmd ci -m "really stunk" &&
+ echo "stink stank stunk" >> stunk/readme &&
+ svn_cmd ci -m "even the grinch agrees"
+ ) &&
+ svn_cmd copy -m "stunk flunked" "$svnrepo"/stunk "$svnrepo"/flunk &&
+ { svn cp -m "early stunk flunked too" \
+ "$svnrepo"/stunk@17 "$svnrepo"/flunked ||
+ svn_cmd cp -m "early stunk flunked too" \
+ -r17 "$svnrepo"/stunk "$svnrepo"/flunked; } &&
+ git svn init --minimize-url -i stunk "$svnrepo"/stunk &&
+ git svn fetch -i stunk &&
+ git update-ref refs/remotes/flunk@18 refs/remotes/stunk~2 &&
+ git update-ref -d refs/remotes/stunk &&
+ git config --unset svn-remote.svn.fetch stunk &&
+ mkdir -p "$GIT_DIR"/svn/refs/remotes/flunk@18 &&
+ rev_map=$(cd "$GIT_DIR"/svn/refs/remotes/stunk && ls .rev_map*) &&
+ dd if="$GIT_DIR"/svn/refs/remotes/stunk/$rev_map \
+ of="$GIT_DIR"/svn/refs/remotes/flunk@18/$rev_map bs=24 count=1 &&
+ rm -rf "$GIT_DIR"/svn/refs/remotes/stunk &&
+ git svn init --minimize-url -i flunk "$svnrepo"/flunk &&
+ git svn fetch -i flunk &&
+ git svn init --minimize-url -i stunk "$svnrepo"/stunk &&
+ git svn fetch -i stunk &&
+ git svn init --minimize-url -i flunked "$svnrepo"/flunked &&
+ git svn fetch -i flunked
+ test "`git rev-parse --verify refs/remotes/flunk@18`" \
+ = "`git rev-parse --verify refs/remotes/stunk`" &&
+ test "`git rev-parse --verify refs/remotes/flunk~1`" \
+ = "`git rev-parse --verify refs/remotes/stunk`" &&
+ test "`git rev-parse --verify refs/remotes/flunked~1`" \
+ = "`git rev-parse --verify refs/remotes/stunk~1`"
+ '
+
+test_expect_success "track multi-parent paths" '
+ svn_cmd cp -m "resurrect /glob" "$svnrepo"/r9270 "$svnrepo"/glob &&
+ git svn multi-fetch &&
+ test `git cat-file commit refs/remotes/glob | \
+ grep "^parent " | wc -l` -eq 2
+ '
+
+test_expect_success "multi-fetch continues to work" "
+ git svn multi-fetch
+ "
+
+test_expect_success "multi-fetch works off a 'clean' repository" '
+ rm -r "$GIT_DIR/svn" "$GIT_DIR/refs/remotes" "$GIT_DIR/logs" &&
+ mkdir "$GIT_DIR/svn" &&
+ git svn multi-fetch
+ '
+
+test_debug 'gitk --all &'
+
+test_done
diff --git a/t/t9105-git-svn-commit-diff.sh b/t/t9105-git-svn-commit-diff.sh
new file mode 100755
index 0000000..dd48e9c
--- /dev/null
+++ b/t/t9105-git-svn-commit-diff.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+test_description='git svn commit-diff'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+ mkdir import &&
+ cd import &&
+ echo hello > readme &&
+ svn_cmd import -m "initial" . "$svnrepo" &&
+ cd .. &&
+ echo hello > readme &&
+ git update-index --add readme &&
+ git commit -a -m "initial" &&
+ echo world >> readme &&
+ git commit -a -m "another"
+ '
+
+head=`git rev-parse --verify HEAD^0`
+prev=`git rev-parse --verify HEAD^1`
+
+# the internals of the commit-diff command are the same as the regular
+# commit, so only a basic test of functionality is needed since we've
+# already tested commit extensively elsewhere
+
+test_expect_success 'test the commit-diff command' '
+ test -n "$prev" && test -n "$head" &&
+ git svn commit-diff -r1 "$prev" "$head" "$svnrepo" &&
+ svn_cmd co "$svnrepo" wc &&
+ cmp readme wc/readme
+ '
+
+test_expect_success 'commit-diff to a sub-directory (with git svn config)' '
+ svn_cmd import -m "sub-directory" import "$svnrepo"/subdir &&
+ git svn init --minimize-url "$svnrepo"/subdir &&
+ git svn fetch &&
+ git svn commit-diff -r3 "$prev" "$head" &&
+ svn_cmd cat "$svnrepo"/subdir/readme > readme.2 &&
+ cmp readme readme.2
+ '
+
+test_done
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
new file mode 100755
index 0000000..12f21b7
--- /dev/null
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+test_description='git svn commit-diff clobber'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+ mkdir import &&
+ cd import &&
+ echo initial > file &&
+ svn_cmd import -m "initial" . "$svnrepo" &&
+ cd .. &&
+ echo initial > file &&
+ git update-index --add file &&
+ git commit -a -m "initial"
+ '
+test_expect_success 'commit change from svn side' '
+ svn_cmd co "$svnrepo" t.svn &&
+ cd t.svn &&
+ echo second line from svn >> file &&
+ poke file &&
+ svn_cmd commit -m "second line from svn" &&
+ cd .. &&
+ rm -rf t.svn
+ '
+
+test_expect_success 'commit conflicting change from git' '
+ echo second line from git >> file &&
+ git commit -a -m "second line from git" &&
+ test_must_fail git svn commit-diff -r1 HEAD~1 HEAD "$svnrepo"
+'
+
+test_expect_success 'commit complementing change from git' '
+ git reset --hard HEAD~1 &&
+ echo second line from svn >> file &&
+ git commit -a -m "second line from svn" &&
+ echo third line from git >> file &&
+ git commit -a -m "third line from git" &&
+ git svn commit-diff -r2 HEAD~1 HEAD "$svnrepo"
+ '
+
+test_expect_success 'dcommit fails to commit because of conflict' '
+ git svn init "$svnrepo" &&
+ git svn fetch &&
+ git reset --hard refs/${remotes_git_svn} &&
+ svn_cmd co "$svnrepo" t.svn &&
+ cd t.svn &&
+ echo fourth line from svn >> file &&
+ poke file &&
+ svn_cmd commit -m "fourth line from svn" &&
+ cd .. &&
+ rm -rf t.svn &&
+ echo "fourth line from git" >> file &&
+ git commit -a -m "fourth line from git" &&
+ test_must_fail git svn dcommit
+ '
+
+test_expect_success 'dcommit does the svn equivalent of an index merge' "
+ git reset --hard refs/${remotes_git_svn} &&
+ echo 'index merge' > file2 &&
+ git update-index --add file2 &&
+ git commit -a -m 'index merge' &&
+ echo 'more changes' >> file2 &&
+ git update-index file2 &&
+ git commit -a -m 'more changes' &&
+ git svn dcommit
+ "
+
+test_expect_success 'commit another change from svn side' '
+ svn_cmd co "$svnrepo" t.svn &&
+ cd t.svn &&
+ echo third line from svn >> file &&
+ poke file &&
+ svn_cmd commit -m "third line from svn" &&
+ cd .. &&
+ rm -rf t.svn
+ '
+
+test_expect_success 'multiple dcommit from git svn will not clobber svn' "
+ git reset --hard refs/${remotes_git_svn} &&
+ echo new file >> new-file &&
+ git update-index --add new-file &&
+ git commit -a -m 'new file' &&
+ echo clobber > file &&
+ git commit -a -m 'clobber' &&
+ test_must_fail git svn dcommit
+ "
+
+
+test_expect_success 'check that rebase really failed' '
+ test -d .git/rebase-apply
+'
+
+test_expect_success 'resolve, continue the rebase and dcommit' "
+ echo clobber and I really mean it > file &&
+ git update-index file &&
+ git rebase --continue &&
+ git svn dcommit
+ "
+
+test_done
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
new file mode 100755
index 0000000..901b8e0
--- /dev/null
+++ b/t/t9107-git-svn-migrate.sh
@@ -0,0 +1,121 @@
+#!/bin/sh
+# Copyright (c) 2006 Eric Wong
+test_description='git svn metadata migrations from previous versions'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup old-looking metadata' '
+ cp "$GIT_DIR"/config "$GIT_DIR"/config-old-git-svn &&
+ mkdir import &&
+ cd import &&
+ for i in trunk branches/a branches/b \
+ tags/0.1 tags/0.2 tags/0.3; do
+ mkdir -p $i && \
+ echo hello >> $i/README || exit 1
+ done && \
+ svn_cmd import -m test . "$svnrepo"
+ cd .. &&
+ git svn init "$svnrepo" &&
+ git svn fetch &&
+ rm -rf "$GIT_DIR"/svn &&
+ git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} &&
+ git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} &&
+ git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn}
+ '
+
+head=`git rev-parse --verify refs/heads/git-svn-HEAD^0`
+test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'"
+
+test_expect_success 'initialize old-style (v0) git svn layout' '
+ mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info &&
+ echo "$svnrepo" > "$GIT_DIR"/git-svn/info/url &&
+ echo "$svnrepo" > "$GIT_DIR"/svn/info/url &&
+ git svn migrate &&
+ ! test -d "$GIT_DIR"/git svn &&
+ git rev-parse --verify refs/${remotes_git_svn}^0 &&
+ git rev-parse --verify refs/remotes/svn^0 &&
+ test "$(git config --get svn-remote.svn.url)" = "$svnrepo" &&
+ test `git config --get svn-remote.svn.fetch` = \
+ ":refs/${remotes_git_svn}"
+ '
+
+test_expect_success 'initialize a multi-repository repo' '
+ git svn init "$svnrepo" -T trunk -t tags -b branches &&
+ git config --get-all svn-remote.svn.fetch > fetch.out &&
+ grep "^trunk:refs/remotes/trunk$" fetch.out &&
+ test -n "`git config --get svn-remote.svn.branches \
+ "^branches/\*:refs/remotes/\*$"`" &&
+ test -n "`git config --get svn-remote.svn.tags \
+ "^tags/\*:refs/remotes/tags/\*$"`" &&
+ git config --unset svn-remote.svn.branches \
+ "^branches/\*:refs/remotes/\*$" &&
+ git config --unset svn-remote.svn.tags \
+ "^tags/\*:refs/remotes/tags/\*$" &&
+ git config --add svn-remote.svn.fetch "branches/a:refs/remotes/a" &&
+ git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" &&
+ for i in tags/0.1 tags/0.2 tags/0.3; do
+ git config --add svn-remote.svn.fetch \
+ $i:refs/remotes/$i || exit 1; done &&
+ git config --get-all svn-remote.svn.fetch > fetch.out &&
+ grep "^trunk:refs/remotes/trunk$" fetch.out &&
+ grep "^branches/a:refs/remotes/a$" fetch.out &&
+ grep "^branches/b:refs/remotes/b$" fetch.out &&
+ grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
+ grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
+ grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
+ grep "^:refs/${remotes_git_svn}" fetch.out
+ '
+
+# refs should all be different, but the trees should all be the same:
+test_expect_success 'multi-fetch works on partial urls + paths' "
+ git svn multi-fetch &&
+ for i in trunk a b tags/0.1 tags/0.2 tags/0.3; do
+ git rev-parse --verify refs/remotes/\$i^0 >> refs.out || exit 1;
+ done &&
+ test -z \"\`sort < refs.out | uniq -d\`\" &&
+ for i in trunk a b tags/0.1 tags/0.2 tags/0.3; do
+ for j in trunk a b tags/0.1 tags/0.2 tags/0.3; do
+ if test \$j != \$i; then continue; fi
+ test -z \"\`git diff refs/remotes/\$i \
+ refs/remotes/\$j\`\" ||exit 1; done; done
+ "
+
+test_expect_success 'migrate --minimize on old inited layout' '
+ git config --unset-all svn-remote.svn.fetch &&
+ git config --unset-all svn-remote.svn.url &&
+ rm -rf "$GIT_DIR"/svn &&
+ for i in `cat fetch.out`; do
+ path=`expr $i : "\([^:]*\):.*$"`
+ ref=`expr $i : "[^:]*:\(refs/remotes/.*\)$"`
+ if test -z "$ref"; then continue; fi
+ if test -n "$path"; then path="/$path"; fi
+ ( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
+ echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1;
+ done &&
+ git svn migrate --minimize &&
+ test -z "`git config -l | grep "^svn-remote\.git-svn\."`" &&
+ git config --get-all svn-remote.svn.fetch > fetch.out &&
+ grep "^trunk:refs/remotes/trunk$" fetch.out &&
+ grep "^branches/a:refs/remotes/a$" fetch.out &&
+ grep "^branches/b:refs/remotes/b$" fetch.out &&
+ grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
+ grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
+ grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
+ grep "^:refs/${remotes_git_svn}" fetch.out
+ '
+
+test_expect_success ".rev_db auto-converted to .rev_map.UUID" '
+ git svn fetch -i trunk &&
+ test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+ expect="$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_map.*)" &&
+ test -n "$expect" &&
+ rev_db="$(echo $expect | sed -e "s,_map,_db,")" &&
+ convert_to_rev_db "$expect" "$rev_db" &&
+ rm -f "$expect" &&
+ test -f "$rev_db" &&
+ git svn fetch -i trunk &&
+ test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+ test ! -e "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db &&
+ test -f "$expect"
+ '
+
+test_done
diff --git a/t/t9108-git-svn-glob.sh b/t/t9108-git-svn-glob.sh
new file mode 100755
index 0000000..d732d31
--- /dev/null
+++ b/t/t9108-git-svn-glob.sh
@@ -0,0 +1,111 @@
+#!/bin/sh
+# Copyright (c) 2007 Eric Wong
+test_description='git svn globbing refspecs'
+. ./lib-git-svn.sh
+
+cat > expect.end <<EOF
+the end
+hi
+start a new branch
+initial
+EOF
+
+test_expect_success 'test refspec globbing' '
+ mkdir -p trunk/src/a trunk/src/b trunk/doc &&
+ echo "hello world" > trunk/src/a/readme &&
+ echo "goodbye world" > trunk/src/b/readme &&
+ svn_cmd import -m "initial" trunk "$svnrepo"/trunk &&
+ svn_cmd co "$svnrepo" tmp &&
+ (
+ cd tmp &&
+ mkdir branches tags &&
+ svn_cmd add branches tags &&
+ svn_cmd cp trunk branches/start &&
+ svn_cmd commit -m "start a new branch" &&
+ svn_cmd up &&
+ echo "hi" >> branches/start/src/b/readme &&
+ poke branches/start/src/b/readme &&
+ echo "hey" >> branches/start/src/a/readme &&
+ poke branches/start/src/a/readme &&
+ svn_cmd commit -m "hi" &&
+ svn_cmd up &&
+ svn_cmd cp branches/start tags/end &&
+ echo "bye" >> tags/end/src/b/readme &&
+ poke tags/end/src/b/readme &&
+ echo "aye" >> tags/end/src/a/readme &&
+ poke tags/end/src/a/readme &&
+ svn_cmd commit -m "the end" &&
+ echo "byebye" >> tags/end/src/b/readme &&
+ poke tags/end/src/b/readme &&
+ svn_cmd commit -m "nothing to see here"
+ ) &&
+ git config --add svn-remote.svn.url "$svnrepo" &&
+ git config --add svn-remote.svn.fetch \
+ "trunk/src/a:refs/remotes/trunk" &&
+ git config --add svn-remote.svn.branches \
+ "branches/*/src/a:refs/remotes/branches/*" &&
+ git config --add svn-remote.svn.tags\
+ "tags/*/src/a:refs/remotes/tags/*" &&
+ git svn multi-fetch &&
+ git log --pretty=oneline refs/remotes/tags/end | \
+ sed -e "s/^.\{41\}//" > output.end &&
+ test_cmp expect.end output.end &&
+ test "`git rev-parse refs/remotes/tags/end~1`" = \
+ "`git rev-parse refs/remotes/branches/start`" &&
+ test "`git rev-parse refs/remotes/branches/start~2`" = \
+ "`git rev-parse refs/remotes/trunk`" &&
+ test_must_fail git rev-parse refs/remotes/tags/end@3
+ '
+
+echo try to try > expect.two
+echo nothing to see here >> expect.two
+cat expect.end >> expect.two
+
+test_expect_success 'test left-hand-side only globbing' '
+ git config --add svn-remote.two.url "$svnrepo" &&
+ git config --add svn-remote.two.fetch trunk:refs/remotes/two/trunk &&
+ git config --add svn-remote.two.branches \
+ "branches/*:refs/remotes/two/branches/*" &&
+ git config --add svn-remote.two.tags \
+ "tags/*:refs/remotes/two/tags/*" &&
+ (
+ cd tmp &&
+ echo "try try" >> tags/end/src/b/readme &&
+ poke tags/end/src/b/readme &&
+ svn_cmd commit -m "try to try"
+ ) &&
+ git svn fetch two &&
+ test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
+ test `git rev-list refs/remotes/two/branches/start | wc -l` -eq 3 &&
+ test `git rev-parse refs/remotes/two/branches/start~2` = \
+ `git rev-parse refs/remotes/two/trunk` &&
+ test `git rev-parse refs/remotes/two/tags/end~3` = \
+ `git rev-parse refs/remotes/two/branches/start` &&
+ git log --pretty=oneline refs/remotes/two/tags/end | \
+ sed -e "s/^.\{41\}//" > output.two &&
+ test_cmp expect.two output.two
+ '
+
+echo "Only one set of wildcard directories" \
+ "(e.g. '*' or '*/*/*') is supported: 'branches/*/t/*'" > expect.three
+echo "" >> expect.three
+
+test_expect_success 'test disallow multi-globs' '
+ git config --add svn-remote.three.url "$svnrepo" &&
+ git config --add svn-remote.three.fetch \
+ trunk:refs/remotes/three/trunk &&
+ git config --add svn-remote.three.branches \
+ "branches/*/t/*:refs/remotes/three/branches/*" &&
+ git config --add svn-remote.three.tags \
+ "tags/*/*:refs/remotes/three/tags/*" &&
+ (
+ cd tmp &&
+ echo "try try" >> tags/end/src/b/readme &&
+ poke tags/end/src/b/readme &&
+ svn_cmd commit -m "try to try"
+ ) &&
+ test_must_fail git svn fetch three 2> stderr.three &&
+ test_cmp expect.three stderr.three
+ '
+
+test_done
diff --git a/t/t9109-git-svn-multi-glob.sh b/t/t9109-git-svn-multi-glob.sh
new file mode 100755
index 0000000..c318f9f
--- /dev/null
+++ b/t/t9109-git-svn-multi-glob.sh
@@ -0,0 +1,160 @@
+#!/bin/sh
+# Copyright (c) 2007 Eric Wong
+test_description='git svn globbing refspecs'
+. ./lib-git-svn.sh
+
+cat > expect.end <<EOF
+the end
+hi
+start a new branch
+initial
+EOF
+
+test_expect_success 'test refspec globbing' '
+ mkdir -p trunk/src/a trunk/src/b trunk/doc &&
+ echo "hello world" > trunk/src/a/readme &&
+ echo "goodbye world" > trunk/src/b/readme &&
+ svn_cmd import -m "initial" trunk "$svnrepo"/trunk &&
+ svn_cmd co "$svnrepo" tmp &&
+ (
+ cd tmp &&
+ mkdir branches branches/v1 tags &&
+ svn_cmd add branches tags &&
+ svn_cmd cp trunk branches/v1/start &&
+ svn_cmd commit -m "start a new branch" &&
+ svn_cmd up &&
+ echo "hi" >> branches/v1/start/src/b/readme &&
+ poke branches/v1/start/src/b/readme &&
+ echo "hey" >> branches/v1/start/src/a/readme &&
+ poke branches/v1/start/src/a/readme &&
+ svn_cmd commit -m "hi" &&
+ svn_cmd up &&
+ svn_cmd cp branches/v1/start tags/end &&
+ echo "bye" >> tags/end/src/b/readme &&
+ poke tags/end/src/b/readme &&
+ echo "aye" >> tags/end/src/a/readme &&
+ poke tags/end/src/a/readme &&
+ svn_cmd commit -m "the end" &&
+ echo "byebye" >> tags/end/src/b/readme &&
+ poke tags/end/src/b/readme &&
+ svn_cmd commit -m "nothing to see here"
+ ) &&
+ git config --add svn-remote.svn.url "$svnrepo" &&
+ git config --add svn-remote.svn.fetch \
+ "trunk/src/a:refs/remotes/trunk" &&
+ git config --add svn-remote.svn.branches \
+ "branches/*/*/src/a:refs/remotes/branches/*/*" &&
+ git config --add svn-remote.svn.tags\
+ "tags/*/src/a:refs/remotes/tags/*" &&
+ git svn multi-fetch &&
+ git log --pretty=oneline refs/remotes/tags/end | \
+ sed -e "s/^.\{41\}//" > output.end &&
+ test_cmp expect.end output.end &&
+ test "`git rev-parse refs/remotes/tags/end~1`" = \
+ "`git rev-parse refs/remotes/branches/v1/start`" &&
+ test "`git rev-parse refs/remotes/branches/v1/start~2`" = \
+ "`git rev-parse refs/remotes/trunk`" &&
+ test_must_fail git rev-parse refs/remotes/tags/end@3
+ '
+
+echo try to try > expect.two
+echo nothing to see here >> expect.two
+cat expect.end >> expect.two
+
+test_expect_success 'test left-hand-side only globbing' '
+ git config --add svn-remote.two.url "$svnrepo" &&
+ git config --add svn-remote.two.fetch trunk:refs/remotes/two/trunk &&
+ git config --add svn-remote.two.branches \
+ "branches/*/*:refs/remotes/two/branches/*/*" &&
+ git config --add svn-remote.two.tags \
+ "tags/*:refs/remotes/two/tags/*" &&
+ (
+ cd tmp &&
+ echo "try try" >> tags/end/src/b/readme &&
+ poke tags/end/src/b/readme &&
+ svn_cmd commit -m "try to try"
+ ) &&
+ git svn fetch two &&
+ test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
+ test `git rev-list refs/remotes/two/branches/v1/start | wc -l` -eq 3 &&
+ test `git rev-parse refs/remotes/two/branches/v1/start~2` = \
+ `git rev-parse refs/remotes/two/trunk` &&
+ test `git rev-parse refs/remotes/two/tags/end~3` = \
+ `git rev-parse refs/remotes/two/branches/v1/start` &&
+ git log --pretty=oneline refs/remotes/two/tags/end | \
+ sed -e "s/^.\{41\}//" > output.two &&
+ test_cmp expect.two output.two
+ '
+cat > expect.four <<EOF
+adios
+adding more
+Changed 2 in v2/start
+Another versioned branch
+initial
+EOF
+
+test_expect_success 'test another branch' '
+ (
+ cd tmp &&
+ mkdir branches/v2 &&
+ svn_cmd add branches/v2 &&
+ svn_cmd cp trunk branches/v2/start &&
+ svn_cmd commit -m "Another versioned branch" &&
+ svn_cmd up &&
+ echo "hello" >> branches/v2/start/src/b/readme &&
+ poke branches/v2/start/src/b/readme &&
+ echo "howdy" >> branches/v2/start/src/a/readme &&
+ poke branches/v2/start/src/a/readme &&
+ svn_cmd commit -m "Changed 2 in v2/start" &&
+ svn_cmd up &&
+ svn_cmd cp branches/v2/start tags/next &&
+ echo "bye" >> tags/next/src/b/readme &&
+ poke tags/next/src/b/readme &&
+ echo "aye" >> tags/next/src/a/readme &&
+ poke tags/next/src/a/readme &&
+ svn_cmd commit -m "adding more" &&
+ echo "byebye" >> tags/next/src/b/readme &&
+ poke tags/next/src/b/readme &&
+ svn_cmd commit -m "adios"
+ ) &&
+ git config --add svn-remote.four.url "$svnrepo" &&
+ git config --add svn-remote.four.fetch trunk:refs/remotes/four/trunk &&
+ git config --add svn-remote.four.branches \
+ "branches/*/*:refs/remotes/four/branches/*/*" &&
+ git config --add svn-remote.four.tags \
+ "tags/*:refs/remotes/four/tags/*" &&
+ git svn fetch four &&
+ test `git rev-list refs/remotes/four/tags/next | wc -l` -eq 5 &&
+ test `git rev-list refs/remotes/four/branches/v2/start | wc -l` -eq 3 &&
+ test `git rev-parse refs/remotes/four/branches/v2/start~2` = \
+ `git rev-parse refs/remotes/four/trunk` &&
+ test `git rev-parse refs/remotes/four/tags/next~2` = \
+ `git rev-parse refs/remotes/four/branches/v2/start` &&
+ git log --pretty=oneline refs/remotes/four/tags/next | \
+ sed -e "s/^.\{41\}//" > output.four &&
+ test_cmp expect.four output.four
+ '
+
+echo "Only one set of wildcard directories" \
+ "(e.g. '*' or '*/*/*') is supported: 'branches/*/t/*'" > expect.three
+echo "" >> expect.three
+
+test_expect_success 'test disallow multiple globs' '
+ git config --add svn-remote.three.url "$svnrepo" &&
+ git config --add svn-remote.three.fetch \
+ trunk:refs/remotes/three/trunk &&
+ git config --add svn-remote.three.branches \
+ "branches/*/t/*:refs/remotes/three/branches/*/*" &&
+ git config --add svn-remote.three.tags \
+ "tags/*:refs/remotes/three/tags/*" &&
+ (
+ cd tmp &&
+ echo "try try" >> tags/end/src/b/readme &&
+ poke tags/end/src/b/readme &&
+ svn_cmd commit -m "try to try"
+ ) &&
+ test_must_fail git svn fetch three 2> stderr.three &&
+ test_cmp expect.three stderr.three
+ '
+
+test_done
diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh
new file mode 100755
index 0000000..a06e4c5
--- /dev/null
+++ b/t/t9110-git-svn-use-svm-props.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git svn useSvmProps test'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svm repo' '
+ svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9110/svm.dump &&
+ git svn init --minimize-url -R arr -i bar "$svnrepo"/mirror/arr &&
+ git svn init --minimize-url -R argh -i dir "$svnrepo"/mirror/argh &&
+ git svn init --minimize-url -R argh -i e \
+ "$svnrepo"/mirror/argh/a/b/c/d/e &&
+ git config svn.useSvmProps true &&
+ git svn fetch --all
+ '
+
+uuid=161ce429-a9dd-4828-af4a-52023f968c89
+
+bar_url=http://mayonaise/svnrepo/bar
+test_expect_success 'verify metadata for /bar' "
+ git cat-file commit refs/remotes/bar | \
+ grep '^${git_svn_id}: $bar_url@12 $uuid$' &&
+ git cat-file commit refs/remotes/bar~1 | \
+ grep '^${git_svn_id}: $bar_url@11 $uuid$' &&
+ git cat-file commit refs/remotes/bar~2 | \
+ grep '^${git_svn_id}: $bar_url@10 $uuid$' &&
+ git cat-file commit refs/remotes/bar~3 | \
+ grep '^${git_svn_id}: $bar_url@9 $uuid$' &&
+ git cat-file commit refs/remotes/bar~4 | \
+ grep '^${git_svn_id}: $bar_url@6 $uuid$' &&
+ git cat-file commit refs/remotes/bar~5 | \
+ grep '^${git_svn_id}: $bar_url@1 $uuid$'
+ "
+
+e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e
+test_expect_success 'verify metadata for /dir/a/b/c/d/e' "
+ git cat-file commit refs/remotes/e | \
+ grep '^${git_svn_id}: $e_url@1 $uuid$'
+ "
+
+dir_url=http://mayonaise/svnrepo/dir
+test_expect_success 'verify metadata for /dir' "
+ git cat-file commit refs/remotes/dir | \
+ grep '^${git_svn_id}: $dir_url@2 $uuid$' &&
+ git cat-file commit refs/remotes/dir~1 | \
+ grep '^${git_svn_id}: $dir_url@1 $uuid$'
+ "
+
+test_expect_success 'find commit based on SVN revision number' "
+ git svn find-rev r12 |
+ grep `git rev-parse HEAD`
+ "
+
+test_expect_success 'empty rebase' "
+ git svn rebase
+ "
+
+test_done
diff --git a/t/t9110/svm.dump b/t/t9110/svm.dump
new file mode 100644
index 0000000..cc799c2
--- /dev/null
+++ b/t/t9110/svm.dump
@@ -0,0 +1,511 @@
+SVN-fs-dump-format-version: 2
+
+UUID: de5973c6-545d-41da-aded-c265f9039e74
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2007-02-17T06:54:59.793104Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 200
+Content-length: 200
+
+K 7
+svn:log
+V 40
+SVM: initializing mirror for /mirror/arr
+K 10
+svn:author
+V 3
+svm
+K 11
+svm:headrev
+V 39
+161ce429-a9dd-4828-af4a-52023f968c89:0
+
+K 8
+svn:date
+V 27
+2007-02-17T06:55:00.121647Z
+PROPS-END
+
+Node-path:
+Node-kind: dir
+Node-action: change
+Prop-content-length: 44
+Content-length: 44
+
+K 10
+svm:mirror
+V 12
+/mirror/arr
+
+PROPS-END
+
+
+Node-path: mirror
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: mirror/arr
+Node-kind: dir
+Node-action: add
+Prop-content-length: 116
+Content-length: 116
+
+K 10
+svm:source
+V 29
+http://mayonaise/svnrepo!/bar
+K 8
+svm:uuid
+V 36
+161ce429-a9dd-4828-af4a-52023f968c89
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 182
+Content-length: 182
+
+K 7
+svn:log
+V 18
+import for git-svn
+K 10
+svn:author
+V 7
+svnsync
+K 11
+svm:headrev
+V 39
+161ce429-a9dd-4828-af4a-52023f968c89:1
+
+K 8
+svn:date
+V 27
+2007-02-17T05:10:52.108847Z
+PROPS-END
+
+Node-path: mirror/arr
+Node-kind: dir
+Node-action: change
+Prop-content-length: 116
+Content-length: 116
+
+K 10
+svm:source
+V 29
+http://mayonaise/svnrepo!/bar
+K 8
+svm:uuid
+V 36
+161ce429-a9dd-4828-af4a-52023f968c89
+PROPS-END
+
+
+Node-path: mirror/arr/zzz
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: 33b02bc15ce9557d2dd8484d58f95ac4
+Content-length: 14
+
+PROPS-END
+zzz
+
+
+Revision-number: 3
+Prop-content-length: 230
+Content-length: 230
+
+K 7
+svn:log
+V 66
+new symlink is added to a file that was also just made executable
+
+K 10
+svn:author
+V 7
+svnsync
+K 11
+svm:headrev
+V 39
+161ce429-a9dd-4828-af4a-52023f968c89:6
+
+K 8
+svn:date
+V 27
+2007-02-17T05:11:01.686891Z
+PROPS-END
+
+Node-path: mirror/arr/zzz
+Node-kind: file
+Node-action: change
+Prop-content-length: 36
+Text-content-length: 4
+Text-content-md5: 33b02bc15ce9557d2dd8484d58f95ac4
+Content-length: 40
+
+K 14
+svn:executable
+V 1
+*
+PROPS-END
+zzz
+
+
+Revision-number: 4
+Prop-content-length: 192
+Content-length: 192
+
+K 7
+svn:log
+V 28
+/bar/d should be in the log
+
+K 10
+svn:author
+V 7
+svnsync
+K 11
+svm:headrev
+V 39
+161ce429-a9dd-4828-af4a-52023f968c89:9
+
+K 8
+svn:date
+V 27
+2007-02-17T05:11:07.686552Z
+PROPS-END
+
+Node-path: mirror/arr/d
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: 0bee89b07a248e27c83fc3d5951213c1
+Content-length: 14
+
+PROPS-END
+abc
+
+
+Revision-number: 5
+Prop-content-length: 185
+Content-length: 185
+
+K 7
+svn:log
+V 20
+add a new directory
+
+K 10
+svn:author
+V 7
+svnsync
+K 11
+svm:headrev
+V 40
+161ce429-a9dd-4828-af4a-52023f968c89:10
+
+K 8
+svn:date
+V 27
+2007-02-17T05:11:08.405953Z
+PROPS-END
+
+Node-path: mirror/arr/newdir
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: mirror/arr/newdir/dir
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: 9cd599a3523898e6a12e13ec787da50a
+Content-length: 14
+
+PROPS-END
+new
+
+
+Revision-number: 6
+Prop-content-length: 196
+Content-length: 196
+
+K 7
+svn:log
+V 31
+modify a file in new directory
+
+K 10
+svn:author
+V 7
+svnsync
+K 11
+svm:headrev
+V 40
+161ce429-a9dd-4828-af4a-52023f968c89:11
+
+K 8
+svn:date
+V 27
+2007-02-17T05:11:09.126645Z
+PROPS-END
+
+Node-path: mirror/arr/newdir/dir
+Node-kind: file
+Node-action: change
+Text-content-length: 8
+Text-content-md5: a950e20332358e523a5e9d571e47fa64
+Content-length: 8
+
+new
+foo
+
+
+Revision-number: 7
+Prop-content-length: 179
+Content-length: 179
+
+K 7
+svn:log
+V 14
+update /bar/d
+
+K 10
+svn:author
+V 7
+svnsync
+K 11
+svm:headrev
+V 40
+161ce429-a9dd-4828-af4a-52023f968c89:12
+
+K 8
+svn:date
+V 27
+2007-02-17T05:11:09.846221Z
+PROPS-END
+
+Node-path: mirror/arr/d
+Node-kind: file
+Node-action: change
+Text-content-length: 4
+Text-content-md5: 7abb78de7f2756ca8b511cbc879fd5e7
+Content-length: 4
+
+cba
+
+
+Revision-number: 8
+Prop-content-length: 201
+Content-length: 201
+
+K 7
+svn:log
+V 41
+SVM: initializing mirror for /mirror/argh
+K 10
+svn:author
+V 3
+svm
+K 11
+svm:headrev
+V 39
+161ce429-a9dd-4828-af4a-52023f968c89:0
+
+K 8
+svn:date
+V 27
+2007-02-17T06:56:03.703677Z
+PROPS-END
+
+Node-path:
+Node-kind: dir
+Node-action: change
+Prop-content-length: 57
+Content-length: 57
+
+K 10
+svm:mirror
+V 25
+/mirror/argh
+/mirror/arr
+
+PROPS-END
+
+
+Node-path: mirror/argh
+Node-kind: dir
+Node-action: add
+Prop-content-length: 116
+Content-length: 116
+
+K 10
+svm:source
+V 29
+http://mayonaise/svnrepo!/dir
+K 8
+svm:uuid
+V 36
+161ce429-a9dd-4828-af4a-52023f968c89
+PROPS-END
+
+
+Revision-number: 9
+Prop-content-length: 182
+Content-length: 182
+
+K 7
+svn:log
+V 18
+import for git-svn
+K 10
+svn:author
+V 7
+svnsync
+K 11
+svm:headrev
+V 39
+161ce429-a9dd-4828-af4a-52023f968c89:1
+
+K 8
+svn:date
+V 27
+2007-02-17T05:10:52.108847Z
+PROPS-END
+
+Node-path: mirror/argh
+Node-kind: dir
+Node-action: change
+Prop-content-length: 116
+Content-length: 116
+
+K 10
+svm:source
+V 29
+http://mayonaise/svnrepo!/dir
+K 8
+svm:uuid
+V 36
+161ce429-a9dd-4828-af4a-52023f968c89
+PROPS-END
+
+
+Node-path: mirror/argh/a
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: mirror/argh/a/b
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: mirror/argh/a/b/c
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: mirror/argh/a/b/c/d
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: mirror/argh/a/b/c/d/e
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: mirror/argh/a/b/c/d/e/file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 9
+Text-content-md5: 3fd46fe46fcdcf062c802ca60dc826d5
+Content-length: 19
+
+PROPS-END
+deep dir
+
+
+Revision-number: 10
+Prop-content-length: 197
+Content-length: 197
+
+K 7
+svn:log
+V 33
+try a deep --rmdir with a commit
+
+K 10
+svn:author
+V 7
+svnsync
+K 11
+svm:headrev
+V 39
+161ce429-a9dd-4828-af4a-52023f968c89:2
+
+K 8
+svn:date
+V 27
+2007-02-17T05:10:54.847015Z
+PROPS-END
+
+Node-path: mirror/argh/file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 9
+Node-copyfrom-path: mirror/argh/a/b/c/d/e/file
+Text-content-length: 9
+Text-content-md5: 3fd46fe46fcdcf062c802ca60dc826d5
+Content-length: 9
+
+deep dir
+
+
+Node-path: mirror/argh/a
+Node-action: delete
+
+
diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh
new file mode 100755
index 0000000..bd081c2
--- /dev/null
+++ b/t/t9111-git-svn-use-svnsync-props.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git svn useSvnsyncProps test'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svnsync repo' '
+ svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9111/svnsync.dump &&
+ git svn init --minimize-url -R arr -i bar "$svnrepo"/bar &&
+ git svn init --minimize-url -R argh -i dir "$svnrepo"/dir &&
+ git svn init --minimize-url -R argh -i e "$svnrepo"/dir/a/b/c/d/e &&
+ git config svn.useSvnsyncProps true &&
+ git svn fetch --all
+ '
+
+uuid=161ce429-a9dd-4828-af4a-52023f968c89
+
+bar_url=http://mayonaise/svnrepo/bar
+test_expect_success 'verify metadata for /bar' "
+ git cat-file commit refs/remotes/bar | \
+ grep '^${git_svn_id}: $bar_url@12 $uuid$' &&
+ git cat-file commit refs/remotes/bar~1 | \
+ grep '^${git_svn_id}: $bar_url@11 $uuid$' &&
+ git cat-file commit refs/remotes/bar~2 | \
+ grep '^${git_svn_id}: $bar_url@10 $uuid$' &&
+ git cat-file commit refs/remotes/bar~3 | \
+ grep '^${git_svn_id}: $bar_url@9 $uuid$' &&
+ git cat-file commit refs/remotes/bar~4 | \
+ grep '^${git_svn_id}: $bar_url@6 $uuid$' &&
+ git cat-file commit refs/remotes/bar~5 | \
+ grep '^${git_svn_id}: $bar_url@1 $uuid$'
+ "
+
+e_url=http://mayonaise/svnrepo/dir/a/b/c/d/e
+test_expect_success 'verify metadata for /dir/a/b/c/d/e' "
+ git cat-file commit refs/remotes/e | \
+ grep '^${git_svn_id}: $e_url@1 $uuid$'
+ "
+
+dir_url=http://mayonaise/svnrepo/dir
+test_expect_success 'verify metadata for /dir' "
+ git cat-file commit refs/remotes/dir | \
+ grep '^${git_svn_id}: $dir_url@2 $uuid$' &&
+ git cat-file commit refs/remotes/dir~1 | \
+ grep '^${git_svn_id}: $dir_url@1 $uuid$'
+ "
+
+test_done
diff --git a/t/t9111/svnsync.dump b/t/t9111/svnsync.dump
new file mode 100644
index 0000000..499fa95
--- /dev/null
+++ b/t/t9111/svnsync.dump
@@ -0,0 +1,560 @@
+SVN-fs-dump-format-version: 2
+
+UUID: b4bfe35e-f256-4096-874c-08c5639ecad7
+
+Revision-number: 0
+Prop-content-length: 240
+Content-length: 240
+
+K 18
+svn:sync-from-uuid
+V 36
+161ce429-a9dd-4828-af4a-52023f968c89
+K 10
+svn:author
+V 7
+svnsync
+K 24
+svn:sync-last-merged-rev
+V 2
+12
+K 8
+svn:date
+V 27
+2007-02-17T05:10:52.017552Z
+K 17
+svn:sync-from-url
+V 24
+http://mayonaise/svnrepo
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 18
+import for git-svn
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:10:52.108847Z
+PROPS-END
+
+Node-path: bar
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: bar/zzz
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: 33b02bc15ce9557d2dd8484d58f95ac4
+Content-length: 14
+
+PROPS-END
+zzz
+
+
+Node-path: dir
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: dir/a
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: dir/a/b
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: dir/a/b/c
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: dir/a/b/c/d
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: dir/a/b/c/d/e
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: dir/a/b/c/d/e/file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 9
+Text-content-md5: 3fd46fe46fcdcf062c802ca60dc826d5
+Content-length: 19
+
+PROPS-END
+deep dir
+
+
+Node-path: exec.sh
+Node-kind: file
+Node-action: add
+Prop-content-length: 35
+Text-content-length: 10
+Text-content-md5: 3e2b31c72181b87149ff995e7202c0e3
+Content-length: 45
+
+K 14
+svn:executable
+V 0
+
+PROPS-END
+#!/bin/sh
+
+
+Node-path: foo
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: d3b07384d113edec49eaa6238ad5ff00
+Content-length: 14
+
+PROPS-END
+foo
+
+
+Node-path: foo.link
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 8
+Text-content-md5: 1043146e49ef02cab12eef865cb34ff3
+Content-length: 41
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+link foo
+
+Revision-number: 2
+Prop-content-length: 135
+Content-length: 135
+
+K 7
+svn:log
+V 33
+try a deep --rmdir with a commit
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:10:54.847015Z
+PROPS-END
+
+Node-path: dir/file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: dir/a/b/c/d/e/file
+Text-content-length: 9
+Text-content-md5: 3fd46fe46fcdcf062c802ca60dc826d5
+Content-length: 9
+
+deep dir
+
+
+Node-path: dir/a
+Node-action: delete
+
+
+Node-path: file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: dir/a/b/c/d/e/file
+Text-content-length: 9
+Text-content-md5: 3fd46fe46fcdcf062c802ca60dc826d5
+Content-length: 9
+
+deep dir
+
+
+Revision-number: 3
+Prop-content-length: 136
+Content-length: 136
+
+K 7
+svn:log
+V 34
+remove executable bit from a file
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:10:58.232691Z
+PROPS-END
+
+Node-path: exec.sh
+Node-kind: file
+Node-action: change
+Prop-content-length: 10
+Text-content-length: 10
+Text-content-md5: 3e2b31c72181b87149ff995e7202c0e3
+Content-length: 20
+
+PROPS-END
+#!/bin/sh
+
+
+Revision-number: 4
+Prop-content-length: 131
+Content-length: 131
+
+K 7
+svn:log
+V 29
+add executable bit back file
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:10:59.666560Z
+PROPS-END
+
+Node-path: exec.sh
+Node-kind: file
+Node-action: change
+Prop-content-length: 36
+Text-content-length: 10
+Text-content-md5: 3e2b31c72181b87149ff995e7202c0e3
+Content-length: 46
+
+K 14
+svn:executable
+V 1
+*
+PROPS-END
+#!/bin/sh
+
+
+Revision-number: 5
+Prop-content-length: 154
+Content-length: 154
+
+K 7
+svn:log
+V 52
+executable file becomes a symlink to bar/zzz (file)
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:11:00.676495Z
+PROPS-END
+
+Node-path: exec.sh
+Node-kind: file
+Node-action: change
+Prop-content-length: 33
+Text-content-length: 12
+Text-content-md5: f138693371665cc117742508761d684d
+Content-length: 45
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+link bar/zzz
+
+Revision-number: 6
+Prop-content-length: 168
+Content-length: 168
+
+K 7
+svn:log
+V 66
+new symlink is added to a file that was also just made executable
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:11:01.686891Z
+PROPS-END
+
+Node-path: bar/zzz
+Node-kind: file
+Node-action: change
+Prop-content-length: 36
+Text-content-length: 4
+Text-content-md5: 33b02bc15ce9557d2dd8484d58f95ac4
+Content-length: 40
+
+K 14
+svn:executable
+V 1
+*
+PROPS-END
+zzz
+
+
+Node-path: exec-2.sh
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 5
+Node-copyfrom-path: exec.sh
+Text-content-length: 12
+Text-content-md5: f138693371665cc117742508761d684d
+Content-length: 12
+
+link bar/zzz
+
+Revision-number: 7
+Prop-content-length: 136
+Content-length: 136
+
+K 7
+svn:log
+V 34
+modify a symlink to become a file
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:11:02.677035Z
+PROPS-END
+
+Node-path: exec-2.sh
+Node-kind: file
+Node-action: change
+Prop-content-length: 10
+Text-content-length: 9
+Text-content-md5: 8e92eff9e911886cede27d420f89c735
+Content-length: 19
+
+PROPS-END
+git help
+
+
+Revision-number: 8
+Prop-content-length: 109
+Content-length: 109
+
+K 7
+svn:log
+V 8
+éïâˆ
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:11:03.676862Z
+PROPS-END
+
+Node-path: exec-2.sh
+Node-kind: file
+Node-action: change
+Text-content-length: 17
+Text-content-md5: 49881954063cf26ca48c212396a957ca
+Content-length: 17
+
+git help
+# hello
+
+
+Revision-number: 9
+Prop-content-length: 130
+Content-length: 130
+
+K 7
+svn:log
+V 28
+/bar/d should be in the log
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:11:07.686552Z
+PROPS-END
+
+Node-path: bar/d
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: 0bee89b07a248e27c83fc3d5951213c1
+Content-length: 14
+
+PROPS-END
+abc
+
+
+Revision-number: 10
+Prop-content-length: 122
+Content-length: 122
+
+K 7
+svn:log
+V 20
+add a new directory
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:11:08.405953Z
+PROPS-END
+
+Node-path: bar/newdir
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: bar/newdir/dir
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: 9cd599a3523898e6a12e13ec787da50a
+Content-length: 14
+
+PROPS-END
+new
+
+
+Revision-number: 11
+Prop-content-length: 133
+Content-length: 133
+
+K 7
+svn:log
+V 31
+modify a file in new directory
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:11:09.126645Z
+PROPS-END
+
+Node-path: bar/newdir/dir
+Node-kind: file
+Node-action: change
+Text-content-length: 8
+Text-content-md5: a950e20332358e523a5e9d571e47fa64
+Content-length: 8
+
+new
+foo
+
+
+Revision-number: 12
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 14
+update /bar/d
+
+K 10
+svn:author
+V 7
+svnsync
+K 8
+svn:date
+V 27
+2007-02-17T05:11:09.846221Z
+PROPS-END
+
+Node-path: bar/d
+Node-kind: file
+Node-action: change
+Text-content-length: 4
+Text-content-md5: 7abb78de7f2756ca8b511cbc879fd5e7
+Content-length: 4
+
+cba
diff --git a/t/t9112-git-svn-md5less-file.sh b/t/t9112-git-svn-md5less-file.sh
new file mode 100755
index 0000000..a61d671
--- /dev/null
+++ b/t/t9112-git-svn-md5less-file.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+test_description='test that git handles an svn repository with missing md5sums'
+
+. ./lib-git-svn.sh
+
+# Loading a node from a svn dumpfile without a Text-Content-Length
+# field causes svn to neglect to store or report an md5sum. (it will
+# calculate one if you had put Text-Content-Length: 0). This showed
+# up in a repository creted with cvs2svn.
+
+cat > dumpfile.svn <<EOF
+SVN-fs-dump-format-version: 1
+
+Revision-number: 1
+Prop-content-length: 98
+Content-length: 98
+
+K 7
+svn:log
+V 0
+
+K 10
+svn:author
+V 4
+test
+K 8
+svn:date
+V 27
+2007-05-06T12:37:01.153339Z
+PROPS-END
+
+Node-path: md5less-file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+EOF
+
+test_expect_success 'load svn dumpfile' 'svnadmin load "$rawsvnrepo" < dumpfile.svn'
+
+test_expect_success 'initialize git svn' 'git svn init "$svnrepo"'
+test_expect_success 'fetch revisions from svn' 'git svn fetch'
+test_done
diff --git a/t/t9113-git-svn-dcommit-new-file.sh b/t/t9113-git-svn-dcommit-new-file.sh
new file mode 100755
index 0000000..e8479ce
--- /dev/null
+++ b/t/t9113-git-svn-dcommit-new-file.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+# Don't run this test by default unless the user really wants it
+# I don't like the idea of taking a port and possibly leaving a
+# daemon running on a users system if the test fails.
+# Not all git users will need to interact with SVN.
+
+test_description='git svn dcommit new files over svn:// test'
+
+. ./lib-git-svn.sh
+
+require_svnserve
+
+test_expect_success 'start tracking an empty repo' '
+ svn_cmd mkdir -m "empty dir" "$svnrepo"/empty-dir &&
+ echo "[general]" > "$rawsvnrepo"/conf/svnserve.conf &&
+ echo anon-access = write >> "$rawsvnrepo"/conf/svnserve.conf &&
+ start_svnserve &&
+ git svn init svn://127.0.0.1:$SVNSERVE_PORT &&
+ git svn fetch
+ '
+
+test_expect_success 'create files in new directory with dcommit' "
+ mkdir git-new-dir &&
+ echo hello > git-new-dir/world &&
+ git update-index --add git-new-dir/world &&
+ git commit -m hello &&
+ start_svnserve &&
+ git svn dcommit
+ "
+
+test_done
diff --git a/t/t9114-git-svn-dcommit-merge.sh b/t/t9114-git-svn-dcommit-merge.sh
new file mode 100755
index 0000000..84f7c9b
--- /dev/null
+++ b/t/t9114-git-svn-dcommit-merge.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+# Based on a script by Joakim Tjernlund <joakim.tjernlund@transmode.se>
+
+test_description='git svn dcommit handles merges'
+
+. ./lib-git-svn.sh
+
+big_text_block () {
+cat << EOF
+#
+# (C) Copyright 2000 - 2005
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+EOF
+}
+
+test_expect_success 'setup svn repository' '
+ svn_cmd co "$svnrepo" mysvnwork &&
+ mkdir -p mysvnwork/trunk &&
+ cd mysvnwork &&
+ big_text_block >> trunk/README &&
+ svn_cmd add trunk &&
+ svn_cmd ci -m "first commit" trunk &&
+ cd ..
+ '
+
+test_expect_success 'setup git mirror and merge' '
+ git svn init "$svnrepo" -t tags -T trunk -b branches &&
+ git svn fetch &&
+ git checkout --track -b svn remotes/trunk &&
+ git checkout -b merge &&
+ echo new file > new_file &&
+ git add new_file &&
+ git commit -a -m "New file" &&
+ echo hello >> README &&
+ git commit -a -m "hello" &&
+ echo add some stuff >> new_file &&
+ git commit -a -m "add some stuff" &&
+ git checkout svn &&
+ mv -f README tmp &&
+ echo friend > README &&
+ cat tmp >> README &&
+ git commit -a -m "friend" &&
+ git pull . merge
+ '
+
+test_debug 'gitk --all & sleep 1'
+
+test_expect_success 'verify pre-merge ancestry' "
+ test x\`git rev-parse --verify refs/heads/svn^2\` = \
+ x\`git rev-parse --verify refs/heads/merge\` &&
+ git cat-file commit refs/heads/svn^ | grep '^friend$'
+ "
+
+test_expect_success 'git svn dcommit merges' "
+ git svn dcommit
+ "
+
+test_debug 'gitk --all & sleep 1'
+
+test_expect_success 'verify post-merge ancestry' "
+ test x\`git rev-parse --verify refs/heads/svn\` = \
+ x\`git rev-parse --verify refs/remotes/trunk \` &&
+ test x\`git rev-parse --verify refs/heads/svn^2\` = \
+ x\`git rev-parse --verify refs/heads/merge\` &&
+ git cat-file commit refs/heads/svn^ | grep '^friend$'
+ "
+
+test_expect_success 'verify merge commit message' "
+ git rev-list --pretty=raw -1 refs/heads/svn | \
+ grep \" Merge branch 'merge' into svn\"
+ "
+
+test_done
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
new file mode 100755
index 0000000..767799e
--- /dev/null
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+
+
+test_description='git svn dcommit can commit renames of files with ugly names'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load repository with strange names' '
+ svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9115/funky-names.dump &&
+ start_httpd gtk+
+ '
+
+test_expect_success 'init and fetch repository' '
+ git svn init "$svnrepo" &&
+ git svn fetch &&
+ git reset --hard git-svn
+ '
+
+test_expect_success 'create file in existing ugly and empty dir' '
+ mkdir -p "#{bad_directory_name}" &&
+ echo hi > "#{bad_directory_name}/ foo" &&
+ git update-index --add "#{bad_directory_name}/ foo" &&
+ git commit -m "new file in ugly parent" &&
+ git svn dcommit
+ '
+
+test_expect_success 'rename ugly file' '
+ git mv "#{bad_directory_name}/ foo" "file name with feces" &&
+ git commit -m "rename ugly file" &&
+ git svn dcommit
+ '
+
+test_expect_success 'rename pretty file' '
+ echo :x > pretty &&
+ git update-index --add pretty &&
+ git commit -m "pretty :x" &&
+ git svn dcommit &&
+ mkdir -p regular_dir_name &&
+ git mv pretty regular_dir_name/pretty &&
+ git commit -m "moved pretty file" &&
+ git svn dcommit
+ '
+
+test_expect_success 'rename pretty file into ugly one' '
+ git mv regular_dir_name/pretty "#{bad_directory_name}/ booboo" &&
+ git commit -m booboo &&
+ git svn dcommit
+ '
+
+test_expect_success 'add a file with plus signs' '
+ echo .. > +_+ &&
+ git update-index --add +_+ &&
+ git commit -m plus &&
+ mkdir gtk+ &&
+ git mv +_+ gtk+/_+_ &&
+ git commit -m plus_dir &&
+ git svn dcommit
+ '
+
+test_expect_success 'clone the repository to test rebase' '
+ git svn clone "$svnrepo" test-rebase &&
+ cd test-rebase &&
+ echo test-rebase > test-rebase &&
+ git add test-rebase &&
+ git commit -m test-rebase &&
+ cd ..
+ '
+
+test_expect_success 'make a commit to test rebase' '
+ echo test-rebase-main > test-rebase-main &&
+ git add test-rebase-main &&
+ git commit -m test-rebase-main &&
+ git svn dcommit
+ '
+
+test_expect_success 'git svn rebase works inside a fresh-cloned repository' '
+ cd test-rebase &&
+ git svn rebase &&
+ test -e test-rebase-main &&
+ test -e test-rebase
+ '
+
+stop_httpd
+
+test_done
diff --git a/t/t9115/funky-names.dump b/t/t9115/funky-names.dump
new file mode 100644
index 0000000..42422f7
--- /dev/null
+++ b/t/t9115/funky-names.dump
@@ -0,0 +1,103 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 819c44fe-2bcc-4066-88e4-985e2bc0b418
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2007-07-12T07:54:26.062914Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 152
+Content-length: 152
+
+K 7
+svn:log
+V 44
+what will those wacky people think of next?
+
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2007-07-12T08:00:05.011573Z
+PROPS-END
+
+Node-path: leading space
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: leading space file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 5
+Text-content-md5: e4fa20c67542cdc21271e08d329397ab
+Content-length: 15
+
+PROPS-END
+ugly
+
+
+Node-path: #{bad_directory_name}
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: #{cool_name}
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 18
+Text-content-md5: 87dac40ca337dfa3dcc8911388c3ddda
+Content-length: 28
+
+PROPS-END
+strange name here
+
+
+Node-path: dir name with spaces
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: file name with spaces
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 7
+Text-content-md5: c1f10cfd640618484a2a475c11410fd3
+Content-length: 17
+
+PROPS-END
+spaces
+
+
+Node-path: regular_dir_name
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
new file mode 100755
index 0000000..0374a74
--- /dev/null
+++ b/t/t9116-git-svn-log.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git svn log tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup repository and import' '
+ mkdir import &&
+ cd import &&
+ for i in trunk branches/a branches/b \
+ tags/0.1 tags/0.2 tags/0.3; do
+ mkdir -p $i && \
+ echo hello >> $i/README || exit 1
+ done && \
+ svn_cmd import -m test . "$svnrepo"
+ cd .. &&
+ git svn init "$svnrepo" -T trunk -b branches -t tags &&
+ git svn fetch &&
+ git reset --hard trunk &&
+ echo bye >> README &&
+ git commit -a -m bye &&
+ git svn dcommit &&
+ git reset --hard a &&
+ echo why >> FEEDME &&
+ git update-index --add FEEDME &&
+ git commit -m feedme &&
+ git svn dcommit &&
+ git reset --hard trunk &&
+ echo aye >> README &&
+ git commit -a -m aye &&
+ git svn dcommit &&
+ git reset --hard b &&
+ echo spy >> README &&
+ git commit -a -m spy &&
+ echo try >> README &&
+ git commit -a -m try &&
+ git svn dcommit
+ '
+
+test_expect_success 'run log' "
+ git reset --hard a &&
+ git svn log -r2 trunk | grep ^r2 &&
+ git svn log -r4 trunk | grep ^r4 &&
+ git svn log -r3 | grep ^r3
+ "
+
+test_expect_success 'run log against a from trunk' "
+ git reset --hard trunk &&
+ git svn log -r3 a | grep ^r3
+ "
+
+printf 'r1 \nr2 \nr4 \n' > expected-range-r1-r2-r4
+
+test_expect_success 'test ascending revision range' "
+ git reset --hard trunk &&
+ git svn log -r 1:4 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r1-r2-r4 -
+ "
+
+printf 'r4 \nr2 \nr1 \n' > expected-range-r4-r2-r1
+
+test_expect_success 'test descending revision range' "
+ git reset --hard trunk &&
+ git svn log -r 4:1 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4-r2-r1 -
+ "
+
+printf 'r1 \nr2 \n' > expected-range-r1-r2
+
+test_expect_success 'test ascending revision range with unreachable revision' "
+ git reset --hard trunk &&
+ git svn log -r 1:3 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r1-r2 -
+ "
+
+printf 'r2 \nr1 \n' > expected-range-r2-r1
+
+test_expect_success 'test descending revision range with unreachable revision' "
+ git reset --hard trunk &&
+ git svn log -r 3:1 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r2-r1 -
+ "
+
+printf 'r2 \n' > expected-range-r2
+
+test_expect_success 'test ascending revision range with unreachable upper boundary revision and 1 commit' "
+ git reset --hard trunk &&
+ git svn log -r 2:3 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r2 -
+ "
+
+test_expect_success 'test descending revision range with unreachable upper boundary revision and 1 commit' "
+ git reset --hard trunk &&
+ git svn log -r 3:2 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r2 -
+ "
+
+printf 'r4 \n' > expected-range-r4
+
+test_expect_success 'test ascending revision range with unreachable lower boundary revision and 1 commit' "
+ git reset --hard trunk &&
+ git svn log -r 3:4 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4 -
+ "
+
+test_expect_success 'test descending revision range with unreachable lower boundary revision and 1 commit' "
+ git reset --hard trunk &&
+ git svn log -r 4:3 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4 -
+ "
+
+printf -- '------------------------------------------------------------------------\n' > expected-separator
+
+test_expect_success 'test ascending revision range with unreachable boundary revisions and no commits' "
+ git reset --hard trunk &&
+ git svn log -r 5:6 | test_cmp expected-separator -
+ "
+
+test_expect_success 'test descending revision range with unreachable boundary revisions and no commits' "
+ git reset --hard trunk &&
+ git svn log -r 6:5 | test_cmp expected-separator -
+ "
+
+test_expect_success 'test ascending revision range with unreachable boundary revisions and 1 commit' "
+ git reset --hard trunk &&
+ git svn log -r 3:5 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4 -
+ "
+
+test_expect_success 'test descending revision range with unreachable boundary revisions and 1 commit' "
+ git reset --hard trunk &&
+ git svn log -r 5:3 | grep '^r[0-9]' | cut -d'|' -f1 | test_cmp expected-range-r4 -
+ "
+
+test_done
diff --git a/t/t9117-git-svn-init-clone.sh b/t/t9117-git-svn-init-clone.sh
new file mode 100755
index 0000000..b7ef9e2
--- /dev/null
+++ b/t/t9117-git-svn-init-clone.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git svn init/clone tests'
+
+. ./lib-git-svn.sh
+
+# setup, run inside tmp so we don't have any conflicts with $svnrepo
+set -e
+rm -r .git
+mkdir tmp
+cd tmp
+
+test_expect_success 'setup svnrepo' '
+ mkdir project project/trunk project/branches project/tags &&
+ echo foo > project/trunk/foo &&
+ svn_cmd import -m "$test_description" project "$svnrepo"/project &&
+ rm -rf project
+ '
+
+test_expect_success 'basic clone' '
+ test ! -d trunk &&
+ git svn clone "$svnrepo"/project/trunk &&
+ test -d trunk/.git/svn &&
+ test -e trunk/foo &&
+ rm -rf trunk
+ '
+
+test_expect_success 'clone to target directory' '
+ test ! -d target &&
+ git svn clone "$svnrepo"/project/trunk target &&
+ test -d target/.git/svn &&
+ test -e target/foo &&
+ rm -rf target
+ '
+
+test_expect_success 'clone with --stdlayout' '
+ test ! -d project &&
+ git svn clone -s "$svnrepo"/project &&
+ test -d project/.git/svn &&
+ test -e project/foo &&
+ rm -rf project
+ '
+
+test_expect_success 'clone to target directory with --stdlayout' '
+ test ! -d target &&
+ git svn clone -s "$svnrepo"/project target &&
+ test -d target/.git/svn &&
+ test -e target/foo &&
+ rm -rf target
+ '
+
+test_done
diff --git a/t/t9118-git-svn-funky-branch-names.sh b/t/t9118-git-svn-funky-branch-names.sh
new file mode 100755
index 0000000..63fc982
--- /dev/null
+++ b/t/t9118-git-svn-funky-branch-names.sh
@@ -0,0 +1,81 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+#
+
+test_description='git svn funky branch names'
+. ./lib-git-svn.sh
+
+# Abo-Uebernahme (Bug #994)
+scary_uri='Abo-Uebernahme%20%28Bug%20%23994%29'
+scary_ref='Abo-Uebernahme%20(Bug%20#994)'
+
+test_expect_success 'setup svnrepo' '
+ mkdir project project/trunk project/branches project/tags &&
+ echo foo > project/trunk/foo &&
+ svn_cmd import -m "$test_description" project "$svnrepo/pr ject" &&
+ rm -rf project &&
+ svn_cmd cp -m "fun" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/fun plugin" &&
+ svn_cmd cp -m "more fun!" "$svnrepo/pr ject/branches/fun plugin" \
+ "$svnrepo/pr ject/branches/more fun plugin!" &&
+ svn_cmd cp -m "scary" "$svnrepo/pr ject/branches/fun plugin" \
+ "$svnrepo/pr ject/branches/$scary_uri" &&
+ svn_cmd cp -m "leading dot" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/.leading_dot" &&
+ svn_cmd cp -m "trailing dot" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/trailing_dot." &&
+ svn_cmd cp -m "trailing .lock" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/trailing_dotlock.lock" &&
+ svn_cmd cp -m "reflog" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/not-a%40{0}reflog" &&
+ start_httpd
+ '
+
+test_expect_success 'test clone with funky branch names' '
+ git svn clone -s "$svnrepo/pr ject" project &&
+ (
+ cd project &&
+ git rev-parse "refs/remotes/fun%20plugin" &&
+ git rev-parse "refs/remotes/more%20fun%20plugin!" &&
+ git rev-parse "refs/remotes/$scary_ref" &&
+ git rev-parse "refs/remotes/%2Eleading_dot" &&
+ git rev-parse "refs/remotes/trailing_dot%2E" &&
+ git rev-parse "refs/remotes/trailing_dotlock%2Elock" &&
+ git rev-parse "refs/remotes/not-a%40{0}reflog"
+ )
+ '
+
+test_expect_success 'test dcommit to funky branch' "
+ (
+ cd project &&
+ git reset --hard 'refs/remotes/more%20fun%20plugin!' &&
+ echo hello >> foo &&
+ git commit -m 'hello' -- foo &&
+ git svn dcommit
+ )
+ "
+
+test_expect_success 'test dcommit to scary branch' '
+ (
+ cd project &&
+ git reset --hard "refs/remotes/$scary_ref" &&
+ echo urls are scary >> foo &&
+ git commit -m "eep" -- foo &&
+ git svn dcommit
+ )
+ '
+
+test_expect_success 'test dcommit to trailing_dotlock branch' '
+ (
+ cd project &&
+ git reset --hard "refs/remotes/trailing_dotlock%2Elock" &&
+ echo who names branches like this anyway? >> foo &&
+ git commit -m "bar" -- foo &&
+ git svn dcommit
+ )
+ '
+
+stop_httpd
+
+test_done
diff --git a/t/t9119-git-svn-info.sh b/t/t9119-git-svn-info.sh
new file mode 100755
index 0000000..5fb94fb
--- /dev/null
+++ b/t/t9119-git-svn-info.sh
@@ -0,0 +1,378 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 David D. Kilzer
+
+test_description='git svn info'
+
+. ./lib-git-svn.sh
+
+# Tested with: svn, version 1.4.4 (r25188)
+# Tested with: svn, version 1.6.[12345689]
+v=`svn_cmd --version | sed -n -e 's/^svn, version \(1\.[0-9]*\.[0-9]*\).*$/\1/p'`
+case $v in
+1.[456].*)
+ ;;
+*)
+ skip_all="skipping svn-info test (SVN version: $v not supported)"
+ test_done
+ ;;
+esac
+
+ptouch() {
+ perl -w -e '
+ use strict;
+ use POSIX qw(mktime);
+ die "ptouch requires exactly 2 arguments" if @ARGV != 2;
+ my $text_last_updated = shift @ARGV;
+ my $git_file = shift @ARGV;
+ die "\"$git_file\" does not exist" if ! -e $git_file;
+ if ($text_last_updated
+ =~ /(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/) {
+ my $mtime = mktime($6, $5, $4, $3, $2 - 1, $1 - 1900);
+ my $atime = $mtime;
+ utime $atime, $mtime, $git_file;
+ }
+ ' "`svn_cmd info $2 | grep '^Text Last Updated:'`" "$1"
+}
+
+quoted_svnrepo="$(echo $svnrepo | sed 's/ /%20/')"
+
+test_expect_success 'setup repository and import' '
+ mkdir info &&
+ cd info &&
+ echo FIRST > A &&
+ echo one > file &&
+ ln -s file symlink-file &&
+ mkdir directory &&
+ touch directory/.placeholder &&
+ ln -s directory symlink-directory &&
+ svn_cmd import -m "initial" . "$svnrepo" &&
+ cd .. &&
+ svn_cmd co "$svnrepo" svnwc &&
+ cd svnwc &&
+ echo foo > foo &&
+ svn_cmd add foo &&
+ svn_cmd commit -m "change outside directory" &&
+ svn_cmd update &&
+ cd .. &&
+ mkdir gitwc &&
+ cd gitwc &&
+ git svn init "$svnrepo" &&
+ git svn fetch &&
+ cd .. &&
+ ptouch gitwc/file svnwc/file &&
+ ptouch gitwc/directory svnwc/directory &&
+ ptouch gitwc/symlink-file svnwc/symlink-file &&
+ ptouch gitwc/symlink-directory svnwc/symlink-directory
+ '
+
+test_expect_success 'info' "
+ (cd svnwc; svn info) > expected.info &&
+ (cd gitwc; git svn info) > actual.info &&
+ test_cmp expected.info actual.info
+ "
+
+test_expect_success 'info --url' '
+ test "$(cd gitwc; git svn info --url)" = "$quoted_svnrepo"
+ '
+
+test_expect_success 'info .' "
+ (cd svnwc; svn info .) > expected.info-dot &&
+ (cd gitwc; git svn info .) > actual.info-dot &&
+ test_cmp expected.info-dot actual.info-dot
+ "
+
+test_expect_success 'info --url .' '
+ test "$(cd gitwc; git svn info --url .)" = "$quoted_svnrepo"
+ '
+
+test_expect_success 'info file' "
+ (cd svnwc; svn info file) > expected.info-file &&
+ (cd gitwc; git svn info file) > actual.info-file &&
+ test_cmp expected.info-file actual.info-file
+ "
+
+test_expect_success 'info --url file' '
+ test "$(cd gitwc; git svn info --url file)" = "$quoted_svnrepo/file"
+ '
+
+test_expect_success 'info directory' "
+ (cd svnwc; svn info directory) > expected.info-directory &&
+ (cd gitwc; git svn info directory) > actual.info-directory &&
+ test_cmp expected.info-directory actual.info-directory
+ "
+
+test_expect_success 'info inside directory' "
+ (cd svnwc/directory; svn info) > expected.info-inside-directory &&
+ (cd gitwc/directory; git svn info) > actual.info-inside-directory &&
+ test_cmp expected.info-inside-directory actual.info-inside-directory
+ "
+
+test_expect_success 'info --url directory' '
+ test "$(cd gitwc; git svn info --url directory)" = "$quoted_svnrepo/directory"
+ '
+
+test_expect_success 'info symlink-file' "
+ (cd svnwc; svn info symlink-file) > expected.info-symlink-file &&
+ (cd gitwc; git svn info symlink-file) > actual.info-symlink-file &&
+ test_cmp expected.info-symlink-file actual.info-symlink-file
+ "
+
+test_expect_success 'info --url symlink-file' '
+ test "$(cd gitwc; git svn info --url symlink-file)" \
+ = "$quoted_svnrepo/symlink-file"
+ '
+
+test_expect_success 'info symlink-directory' "
+ (cd svnwc; svn info symlink-directory) \
+ > expected.info-symlink-directory &&
+ (cd gitwc; git svn info symlink-directory) \
+ > actual.info-symlink-directory &&
+ test_cmp expected.info-symlink-directory actual.info-symlink-directory
+ "
+
+test_expect_success 'info --url symlink-directory' '
+ test "$(cd gitwc; git svn info --url symlink-directory)" \
+ = "$quoted_svnrepo/symlink-directory"
+ '
+
+test_expect_success 'info added-file' "
+ echo two > gitwc/added-file &&
+ cd gitwc &&
+ git add added-file &&
+ cd .. &&
+ cp gitwc/added-file svnwc/added-file &&
+ ptouch gitwc/added-file svnwc/added-file &&
+ cd svnwc &&
+ svn_cmd add added-file > /dev/null &&
+ cd .. &&
+ (cd svnwc; svn info added-file) > expected.info-added-file &&
+ (cd gitwc; git svn info added-file) > actual.info-added-file &&
+ test_cmp expected.info-added-file actual.info-added-file
+ "
+
+test_expect_success 'info --url added-file' '
+ test "$(cd gitwc; git svn info --url added-file)" \
+ = "$quoted_svnrepo/added-file"
+ '
+
+test_expect_success 'info added-directory' "
+ mkdir gitwc/added-directory svnwc/added-directory &&
+ ptouch gitwc/added-directory svnwc/added-directory &&
+ touch gitwc/added-directory/.placeholder &&
+ cd svnwc &&
+ svn_cmd add added-directory > /dev/null &&
+ cd .. &&
+ cd gitwc &&
+ git add added-directory &&
+ cd .. &&
+ (cd svnwc; svn info added-directory) \
+ > expected.info-added-directory &&
+ (cd gitwc; git svn info added-directory) \
+ > actual.info-added-directory &&
+ test_cmp expected.info-added-directory actual.info-added-directory
+ "
+
+test_expect_success 'info --url added-directory' '
+ test "$(cd gitwc; git svn info --url added-directory)" \
+ = "$quoted_svnrepo/added-directory"
+ '
+
+test_expect_success 'info added-symlink-file' "
+ cd gitwc &&
+ ln -s added-file added-symlink-file &&
+ git add added-symlink-file &&
+ cd .. &&
+ cd svnwc &&
+ ln -s added-file added-symlink-file &&
+ svn_cmd add added-symlink-file > /dev/null &&
+ cd .. &&
+ ptouch gitwc/added-symlink-file svnwc/added-symlink-file &&
+ (cd svnwc; svn info added-symlink-file) \
+ > expected.info-added-symlink-file &&
+ (cd gitwc; git svn info added-symlink-file) \
+ > actual.info-added-symlink-file &&
+ test_cmp expected.info-added-symlink-file \
+ actual.info-added-symlink-file
+ "
+
+test_expect_success 'info --url added-symlink-file' '
+ test "$(cd gitwc; git svn info --url added-symlink-file)" \
+ = "$quoted_svnrepo/added-symlink-file"
+ '
+
+test_expect_success 'info added-symlink-directory' "
+ cd gitwc &&
+ ln -s added-directory added-symlink-directory &&
+ git add added-symlink-directory &&
+ cd .. &&
+ cd svnwc &&
+ ln -s added-directory added-symlink-directory &&
+ svn_cmd add added-symlink-directory > /dev/null &&
+ cd .. &&
+ ptouch gitwc/added-symlink-directory svnwc/added-symlink-directory &&
+ (cd svnwc; svn info added-symlink-directory) \
+ > expected.info-added-symlink-directory &&
+ (cd gitwc; git svn info added-symlink-directory) \
+ > actual.info-added-symlink-directory &&
+ test_cmp expected.info-added-symlink-directory \
+ actual.info-added-symlink-directory
+ "
+
+test_expect_success 'info --url added-symlink-directory' '
+ test "$(cd gitwc; git svn info --url added-symlink-directory)" \
+ = "$quoted_svnrepo/added-symlink-directory"
+ '
+
+# The next few tests replace the "Text Last Updated" value with a
+# placeholder since git doesn't have a way to know the date that a
+# now-deleted file was last checked out locally. Internally it
+# simply reuses the Last Changed Date.
+
+test_expect_success 'info deleted-file' "
+ cd gitwc &&
+ git rm -f file > /dev/null &&
+ cd .. &&
+ cd svnwc &&
+ svn_cmd rm --force file > /dev/null &&
+ cd .. &&
+ (cd svnwc; svn info file) |
+ sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
+ > expected.info-deleted-file &&
+ (cd gitwc; git svn info file) |
+ sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
+ > actual.info-deleted-file &&
+ test_cmp expected.info-deleted-file actual.info-deleted-file
+ "
+
+test_expect_success 'info --url file (deleted)' '
+ test "$(cd gitwc; git svn info --url file)" \
+ = "$quoted_svnrepo/file"
+ '
+
+test_expect_success 'info deleted-directory' "
+ cd gitwc &&
+ git rm -r -f directory > /dev/null &&
+ cd .. &&
+ cd svnwc &&
+ svn_cmd rm --force directory > /dev/null &&
+ cd .. &&
+ (cd svnwc; svn info directory) |
+ sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
+ > expected.info-deleted-directory &&
+ (cd gitwc; git svn info directory) |
+ sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
+ > actual.info-deleted-directory &&
+ test_cmp expected.info-deleted-directory actual.info-deleted-directory
+ "
+
+test_expect_success 'info --url directory (deleted)' '
+ test "$(cd gitwc; git svn info --url directory)" \
+ = "$quoted_svnrepo/directory"
+ '
+
+test_expect_success 'info deleted-symlink-file' "
+ cd gitwc &&
+ git rm -f symlink-file > /dev/null &&
+ cd .. &&
+ cd svnwc &&
+ svn_cmd rm --force symlink-file > /dev/null &&
+ cd .. &&
+ (cd svnwc; svn info symlink-file) |
+ sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
+ > expected.info-deleted-symlink-file &&
+ (cd gitwc; git svn info symlink-file) |
+ sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
+ > actual.info-deleted-symlink-file &&
+ test_cmp expected.info-deleted-symlink-file \
+ actual.info-deleted-symlink-file
+ "
+
+test_expect_success 'info --url symlink-file (deleted)' '
+ test "$(cd gitwc; git svn info --url symlink-file)" \
+ = "$quoted_svnrepo/symlink-file"
+ '
+
+test_expect_success 'info deleted-symlink-directory' "
+ cd gitwc &&
+ git rm -f symlink-directory > /dev/null &&
+ cd .. &&
+ cd svnwc &&
+ svn_cmd rm --force symlink-directory > /dev/null &&
+ cd .. &&
+ (cd svnwc; svn info symlink-directory) |
+ sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
+ > expected.info-deleted-symlink-directory &&
+ (cd gitwc; git svn info symlink-directory) |
+ sed -e 's/^\(Text Last Updated:\).*/\1 TEXT-LAST-UPDATED-STRING/' \
+ > actual.info-deleted-symlink-directory &&
+ test_cmp expected.info-deleted-symlink-directory \
+ actual.info-deleted-symlink-directory
+ "
+
+test_expect_success 'info --url symlink-directory (deleted)' '
+ test "$(cd gitwc; git svn info --url symlink-directory)" \
+ = "$quoted_svnrepo/symlink-directory"
+ '
+
+# NOTE: git does not have the concept of replaced objects,
+# so we can't test for files in that state.
+
+test_expect_success 'info unknown-file' "
+ echo two > gitwc/unknown-file &&
+ (cd gitwc; test_must_fail git svn info unknown-file) \
+ 2> actual.info-unknown-file &&
+ grep unknown-file actual.info-unknown-file
+ "
+
+test_expect_success 'info --url unknown-file' '
+ echo two > gitwc/unknown-file &&
+ (cd gitwc; test_must_fail git svn info --url unknown-file) \
+ 2> actual.info-url-unknown-file &&
+ grep unknown-file actual.info-url-unknown-file
+ '
+
+test_expect_success 'info unknown-directory' "
+ mkdir gitwc/unknown-directory svnwc/unknown-directory &&
+ (cd gitwc; test_must_fail git svn info unknown-directory) \
+ 2> actual.info-unknown-directory &&
+ grep unknown-directory actual.info-unknown-directory
+ "
+
+test_expect_success 'info --url unknown-directory' '
+ (cd gitwc; test_must_fail git svn info --url unknown-directory) \
+ 2> actual.info-url-unknown-directory &&
+ grep unknown-directory actual.info-url-unknown-directory
+ '
+
+test_expect_success 'info unknown-symlink-file' "
+ cd gitwc &&
+ ln -s unknown-file unknown-symlink-file &&
+ cd .. &&
+ (cd gitwc; test_must_fail git svn info unknown-symlink-file) \
+ 2> actual.info-unknown-symlink-file &&
+ grep unknown-symlink-file actual.info-unknown-symlink-file
+ "
+
+test_expect_success 'info --url unknown-symlink-file' '
+ (cd gitwc; test_must_fail git svn info --url unknown-symlink-file) \
+ 2> actual.info-url-unknown-symlink-file &&
+ grep unknown-symlink-file actual.info-url-unknown-symlink-file
+ '
+
+test_expect_success 'info unknown-symlink-directory' "
+ cd gitwc &&
+ ln -s unknown-directory unknown-symlink-directory &&
+ cd .. &&
+ (cd gitwc; test_must_fail git svn info unknown-symlink-directory) \
+ 2> actual.info-unknown-symlink-directory &&
+ grep unknown-symlink-directory actual.info-unknown-symlink-directory
+ "
+
+test_expect_success 'info --url unknown-symlink-directory' '
+ (cd gitwc; test_must_fail git svn info --url unknown-symlink-directory) \
+ 2> actual.info-url-unknown-symlink-directory &&
+ grep unknown-symlink-directory actual.info-url-unknown-symlink-directory
+ '
+
+test_done
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
new file mode 100755
index 0000000..9d9ebd5
--- /dev/null
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Kevin Ballard
+#
+
+test_description='git svn clone with percent escapes'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svnrepo' '
+ mkdir project project/trunk project/branches project/tags &&
+ echo foo > project/trunk/foo &&
+ svn_cmd import -m "$test_description" project "$svnrepo/pr ject" &&
+ svn_cmd cp -m "branch" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/b" &&
+ svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/tags/v1" &&
+ rm -rf project &&
+ start_httpd
+'
+
+test_expect_success 'test clone with percent escapes' '
+ git svn clone "$svnrepo/pr%20ject" clone &&
+ cd clone &&
+ git rev-parse refs/${remotes_git_svn} &&
+ cd ..
+'
+
+# SVN works either way, so should we...
+
+test_expect_success 'svn checkout with percent escapes' '
+ svn_cmd checkout "$svnrepo/pr%20ject" svn.percent &&
+ svn_cmd checkout "$svnrepo/pr%20ject/trunk" svn.percent.trunk
+'
+
+test_expect_success 'svn checkout with space' '
+ svn_cmd checkout "$svnrepo/pr ject" svn.space &&
+ svn_cmd checkout "$svnrepo/pr ject/trunk" svn.space.trunk
+'
+
+test_expect_success 'test clone trunk with percent escapes and minimize-url' '
+ git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize &&
+ (
+ cd minimize &&
+ git rev-parse refs/${remotes_git_svn}
+ )
+'
+
+test_expect_success 'test clone trunk with percent escapes' '
+ git svn clone "$svnrepo/pr%20ject/trunk" trunk &&
+ (
+ cd trunk &&
+ git rev-parse refs/${remotes_git_svn}
+ )
+'
+
+test_expect_success 'test clone --stdlayout with percent escapes' '
+ git svn clone --stdlayout "$svnrepo/pr%20ject" percent &&
+ (
+ cd percent &&
+ git rev-parse refs/remotes/trunk^0 &&
+ git rev-parse refs/remotes/b^0 &&
+ git rev-parse refs/remotes/tags/v1^0
+ )
+'
+
+test_expect_success 'test clone -s with unescaped space' '
+ git svn clone -s "$svnrepo/pr ject" space &&
+ (
+ cd space &&
+ git rev-parse refs/remotes/trunk^0 &&
+ git rev-parse refs/remotes/b^0 &&
+ git rev-parse refs/remotes/tags/v1^0
+ )
+'
+
+stop_httpd
+
+test_done
diff --git a/t/t9121-git-svn-fetch-renamed-dir.sh b/t/t9121-git-svn-fetch-renamed-dir.sh
new file mode 100755
index 0000000..000cad3
--- /dev/null
+++ b/t/t9121-git-svn-fetch-renamed-dir.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Santhosh Kumar Mani
+
+
+test_description='git svn can fetch renamed directories'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load repository with renamed directory' '
+ svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9121/renamed-dir.dump
+ '
+
+test_expect_success 'init and fetch repository' '
+ git svn init "$svnrepo/newname" &&
+ git svn fetch
+ '
+
+test_done
+
diff --git a/t/t9121/renamed-dir.dump b/t/t9121/renamed-dir.dump
new file mode 100644
index 0000000..5f9127b
--- /dev/null
+++ b/t/t9121/renamed-dir.dump
@@ -0,0 +1,90 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 06b9b3ad-f546-4fbe-8328-fcb4e6ef5c3f
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-04-02T09:11:59.778557Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 14
+initial import
+K 10
+svn:author
+V 8
+santhosh
+K 8
+svn:date
+V 27
+2008-04-02T09:13:03.170863Z
+PROPS-END
+
+Node-path: name
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: name/a.txt
+Node-kind: file
+Node-action: add
+Prop-content-length: 71
+Text-content-length: 6
+Text-content-md5: b1946ac92492d2347c6235b4d2611184
+Content-length: 77
+
+K 13
+svn:mime-type
+V 10
+text/plain
+K 13
+svn:eol-style
+V 2
+LF
+PROPS-END
+hello
+
+
+Revision-number: 2
+Prop-content-length: 109
+Content-length: 109
+
+K 7
+svn:log
+V 7
+renamed
+K 10
+svn:author
+V 8
+santhosh
+K 8
+svn:date
+V 27
+2008-04-02T09:14:22.952186Z
+PROPS-END
+
+Node-path: newname
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: name
+
+
+Node-path: name
+Node-action: delete
+
+
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
new file mode 100755
index 0000000..30013b7
--- /dev/null
+++ b/t/t9122-git-svn-author.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='git svn authorship'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repository' '
+ svn_cmd checkout "$svnrepo" work.svn &&
+ (
+ cd work.svn &&
+ echo >file
+ svn_cmd add file
+ svn_cmd commit -m "first commit" file
+ )
+'
+
+test_expect_success 'interact with it via git svn' '
+ mkdir work.git &&
+ (
+ cd work.git &&
+ git svn init "$svnrepo"
+ git svn fetch &&
+
+ echo modification >file &&
+ test_tick &&
+ git commit -a -m second &&
+
+ test_tick &&
+ git svn dcommit &&
+
+ echo "further modification" >file &&
+ test_tick &&
+ git commit -a -m third &&
+
+ test_tick &&
+ git svn --add-author-from dcommit &&
+
+ echo "yet further modification" >file &&
+ test_tick &&
+ git commit -a -m fourth &&
+
+ test_tick &&
+ git svn --add-author-from --use-log-author dcommit &&
+
+ git log &&
+
+ git show -s HEAD^^ >../actual.2 &&
+ git show -s HEAD^ >../actual.3 &&
+ git show -s HEAD >../actual.4
+
+ ) &&
+
+ # Make sure that --add-author-from without --use-log-author
+ # did not affect the authorship information
+ myself=$(grep "^Author: " actual.2) &&
+ unaffected=$(grep "^Author: " actual.3) &&
+ test "z$myself" = "z$unaffected" &&
+
+ # Make sure lack of --add-author-from did not add cruft
+ ! grep "^ From: A U Thor " actual.2 &&
+
+ # Make sure --add-author-from added cruft
+ grep "^ From: A U Thor " actual.3 &&
+ grep "^ From: A U Thor " actual.4 &&
+
+ # Make sure --add-author-from with --use-log-author affected
+ # the authorship information
+ grep "^Author: A U Thor " actual.4 &&
+
+ # Make sure there are no commit messages with excess blank lines
+ test $(grep "^ " actual.2 | wc -l) = 3 &&
+ test $(grep "^ " actual.3 | wc -l) = 5 &&
+ test $(grep "^ " actual.4 | wc -l) = 5 &&
+
+ # Make sure there are no svn commit messages with excess blank lines
+ (
+ cd work.svn &&
+ svn_cmd up &&
+
+ test $(svn_cmd log -r2:2 | wc -l) = 5 &&
+ test $(svn_cmd log -r4:4 | wc -l) = 7
+ )
+'
+
+test_done
diff --git a/t/t9123-git-svn-rebuild-with-rewriteroot.sh b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
new file mode 100755
index 0000000..0455216
--- /dev/null
+++ b/t/t9123-git-svn-rebuild-with-rewriteroot.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Jan Krüger
+#
+
+test_description='git svn respects rewriteRoot during rebuild'
+
+. ./lib-git-svn.sh
+
+mkdir import
+cd import
+ touch foo
+ svn_cmd import -m 'import for git svn' . "$svnrepo" >/dev/null
+cd ..
+rm -rf import
+
+test_expect_success 'init, fetch and checkout repository' '
+ git svn init --rewrite-root=http://invalid.invalid/ "$svnrepo" &&
+ git svn fetch
+ git checkout -b mybranch ${remotes_git_svn}
+ '
+
+test_expect_success 'remove rev_map' '
+ rm "$GIT_SVN_DIR"/.rev_map.*
+ '
+
+test_expect_success 'rebuild rev_map' '
+ git svn rebase >/dev/null
+ '
+
+test_done
+
diff --git a/t/t9124-git-svn-dcommit-auto-props.sh b/t/t9124-git-svn-dcommit-auto-props.sh
new file mode 100755
index 0000000..d6b076f
--- /dev/null
+++ b/t/t9124-git-svn-dcommit-auto-props.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Brad King
+
+test_description='git svn dcommit honors auto-props'
+
+. ./lib-git-svn.sh
+
+generate_auto_props() {
+cat << EOF
+[miscellany]
+enable-auto-props=$1
+[auto-props]
+*.sh = svn:mime-type=application/x-shellscript; svn:eol-style=LF
+*.txt = svn:mime-type=text/plain; svn:eol-style = native
+EOF
+}
+
+test_expect_success 'initialize git svn' '
+ mkdir import &&
+ (
+ cd import &&
+ echo foo >foo &&
+ svn_cmd import -m "import for git svn" . "$svnrepo"
+ ) &&
+ rm -rf import &&
+ git svn init "$svnrepo"
+ git svn fetch
+'
+
+test_expect_success 'enable auto-props config' '
+ mkdir user &&
+ generate_auto_props yes >user/config
+'
+
+test_expect_success 'add files matching auto-props' '
+ echo "#!$SHELL_PATH" >exec1.sh &&
+ chmod +x exec1.sh &&
+ echo "hello" >hello.txt &&
+ echo bar >bar &&
+ git add exec1.sh hello.txt bar &&
+ git commit -m "files for enabled auto-props" &&
+ git svn dcommit --config-dir=user
+'
+
+test_expect_success 'disable auto-props config' '
+ generate_auto_props no >user/config
+'
+
+test_expect_success 'add files matching disabled auto-props' '
+ echo "#$SHELL_PATH" >exec2.sh &&
+ chmod +x exec2.sh &&
+ echo "world" >world.txt &&
+ echo zot >zot &&
+ git add exec2.sh world.txt zot &&
+ git commit -m "files for disabled auto-props" &&
+ git svn dcommit --config-dir=user
+'
+
+test_expect_success 'check resulting svn repository' '
+(
+ mkdir work &&
+ cd work &&
+ svn_cmd co "$svnrepo" &&
+ cd svnrepo &&
+
+ # Check properties from first commit.
+ test "x$(svn_cmd propget svn:executable exec1.sh)" = "x*" &&
+ test "x$(svn_cmd propget svn:mime-type exec1.sh)" = \
+ "xapplication/x-shellscript" &&
+ test "x$(svn_cmd propget svn:mime-type hello.txt)" = "xtext/plain" &&
+ test "x$(svn_cmd propget svn:eol-style hello.txt)" = "xnative" &&
+ test "x$(svn_cmd propget svn:mime-type bar)" = "x" &&
+
+ # Check properties from second commit.
+ test "x$(svn_cmd propget svn:executable exec2.sh)" = "x*" &&
+ test "x$(svn_cmd propget svn:mime-type exec2.sh)" = "x" &&
+ test "x$(svn_cmd propget svn:mime-type world.txt)" = "x" &&
+ test "x$(svn_cmd propget svn:eol-style world.txt)" = "x" &&
+ test "x$(svn_cmd propget svn:mime-type zot)" = "x"
+)
+'
+
+test_expect_success 'check renamed file' '
+ test -d user &&
+ generate_auto_props yes > user/config &&
+ git mv foo foo.sh &&
+ git commit -m "foo => foo.sh" &&
+ git svn dcommit --config-dir=user &&
+ (
+ cd work/svnrepo &&
+ svn_cmd up &&
+ test ! -e foo &&
+ test -e foo.sh &&
+ test "x$(svn_cmd propget svn:mime-type foo.sh)" = \
+ "xapplication/x-shellscript" &&
+ test "x$(svn_cmd propget svn:eol-style foo.sh)" = "xLF"
+ )
+'
+
+test_done
diff --git a/t/t9125-git-svn-multi-glob-branch-names.sh b/t/t9125-git-svn-multi-glob-branch-names.sh
new file mode 100755
index 0000000..c194186
--- /dev/null
+++ b/t/t9125-git-svn-multi-glob-branch-names.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+# Copyright (c) 2008 Marcus Griep
+
+test_description='git svn multi-glob branch names'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svnrepo' '
+ mkdir project project/trunk project/branches \
+ project/branches/v14.1 project/tags &&
+ echo foo > project/trunk/foo &&
+ svn_cmd import -m "$test_description" project "$svnrepo/project" &&
+ rm -rf project &&
+ svn_cmd cp -m "fun" "$svnrepo/project/trunk" \
+ "$svnrepo/project/branches/v14.1/beta" &&
+ svn_cmd cp -m "more fun!" "$svnrepo/project/branches/v14.1/beta" \
+ "$svnrepo/project/branches/v14.1/gold"
+ '
+
+test_expect_success 'test clone with multi-glob in branch names' '
+ git svn clone -T trunk -b branches/*/* -t tags \
+ "$svnrepo/project" project &&
+ cd project &&
+ git rev-parse "refs/remotes/v14.1/beta" &&
+ git rev-parse "refs/remotes/v14.1/gold" &&
+ cd ..
+ '
+
+test_expect_success 'test dcommit to multi-globbed branch' "
+ cd project &&
+ git reset --hard 'refs/remotes/v14.1/gold' &&
+ echo hello >> foo &&
+ git commit -m 'hello' -- foo &&
+ git svn dcommit &&
+ cd ..
+ "
+
+test_done
diff --git a/t/t9126-git-svn-follow-deleted-readded-directory.sh b/t/t9126-git-svn-follow-deleted-readded-directory.sh
new file mode 100755
index 0000000..edec640
--- /dev/null
+++ b/t/t9126-git-svn-follow-deleted-readded-directory.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Alec Berryman
+
+test_description='git svn fetch repository with deleted and readded directory'
+
+. ./lib-git-svn.sh
+
+# Don't run this by default; it opens up a port.
+require_svnserve
+
+test_expect_success 'load repository' '
+ svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9126/follow-deleted-readded.dump
+ '
+
+test_expect_success 'fetch repository' '
+ start_svnserve &&
+ git svn init svn://127.0.0.1:$SVNSERVE_PORT &&
+ git svn fetch
+ '
+
+test_done
diff --git a/t/t9126/follow-deleted-readded.dump b/t/t9126/follow-deleted-readded.dump
new file mode 100644
index 0000000..19da5d1
--- /dev/null
+++ b/t/t9126/follow-deleted-readded.dump
@@ -0,0 +1,201 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 1807dc6f-c693-4cda-9710-00e1be8c1f21
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-09-14T19:53:13.006748Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 12
+Create trunk
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:13.239689Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 119
+Content-length: 119
+
+K 7
+svn:log
+V 20
+Create trunk/project
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:13.548860Z
+PROPS-END
+
+Node-path: trunk/project
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 3
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 12
+add new file
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:15.433630Z
+PROPS-END
+
+Node-path: trunk/project/foo
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: d3b07384d113edec49eaa6238ad5ff00
+Content-length: 14
+
+PROPS-END
+foo
+
+
+Revision-number: 4
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 17
+change foo to bar
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:17.339884Z
+PROPS-END
+
+Node-path: trunk/project/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 4
+Text-content-md5: c157a79031e1c40f85931829bc5fc552
+Content-length: 4
+
+bar
+
+
+Revision-number: 5
+Prop-content-length: 114
+Content-length: 114
+
+K 7
+svn:log
+V 15
+don't like that
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:19.335001Z
+PROPS-END
+
+Node-path: trunk/project
+Node-action: delete
+
+
+Revision-number: 6
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 11
+reset trunk
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:19.845897Z
+PROPS-END
+
+Node-path: trunk/project
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 4
+Node-copyfrom-path: trunk/project
+
+
+Revision-number: 7
+Prop-content-length: 113
+Content-length: 113
+
+K 7
+svn:log
+V 14
+change to quux
+K 10
+svn:author
+V 4
+alec
+K 8
+svn:date
+V 27
+2008-09-14T19:53:21.367947Z
+PROPS-END
+
+Node-path: trunk/project/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 5
+Text-content-md5: d3b07a382ec010c01889250fce66fb13
+Content-length: 5
+
+quux
+
+
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
new file mode 100755
index 0000000..4aab8ec
--- /dev/null
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Deskin Miller
+#
+
+test_description='git svn partial-rebuild tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize svnrepo' '
+ mkdir import &&
+ (
+ cd import &&
+ mkdir trunk branches tags &&
+ cd trunk &&
+ echo foo > foo &&
+ cd .. &&
+ svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null &&
+ svn_cmd copy "$svnrepo"/trunk "$svnrepo"/branches/a \
+ -m "created branch a" &&
+ cd .. &&
+ rm -rf import &&
+ svn_cmd co "$svnrepo"/trunk trunk &&
+ cd trunk &&
+ echo bar >> foo &&
+ svn_cmd ci -m "updated trunk" &&
+ cd .. &&
+ svn_cmd co "$svnrepo"/branches/a a &&
+ cd a &&
+ echo baz >> a &&
+ svn_cmd add a &&
+ svn_cmd ci -m "updated a" &&
+ cd .. &&
+ git svn init --stdlayout "$svnrepo"
+ )
+'
+
+test_expect_success 'import an early SVN revision into git' '
+ git svn fetch -r1:2
+'
+
+test_expect_success 'make full git mirror of SVN' '
+ mkdir mirror &&
+ (
+ cd mirror &&
+ git init &&
+ git svn init --stdlayout "$svnrepo" &&
+ git svn fetch &&
+ cd ..
+ )
+'
+
+test_expect_success 'fetch from git mirror and partial-rebuild' '
+ git config --add remote.origin.url "file://$PWD/mirror/.git" &&
+ git config --add remote.origin.fetch refs/remotes/*:refs/remotes/* &&
+ git fetch origin &&
+ git svn fetch
+'
+
+test_done
diff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh
new file mode 100755
index 0000000..807e494
--- /dev/null
+++ b/t/t9128-git-svn-cmd-branch.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Deskin Miller
+#
+
+test_description='git svn partial-rebuild tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize svnrepo' '
+ mkdir import &&
+ (
+ cd import &&
+ mkdir trunk branches tags &&
+ cd trunk &&
+ echo foo > foo &&
+ cd .. &&
+ svn_cmd import -m "import for git-svn" . "$svnrepo" >/dev/null &&
+ cd .. &&
+ rm -rf import &&
+ svn_cmd co "$svnrepo"/trunk trunk &&
+ cd trunk &&
+ echo bar >> foo &&
+ svn_cmd ci -m "updated trunk" &&
+ cd .. &&
+ rm -rf trunk
+ )
+'
+
+test_expect_success 'import into git' '
+ git svn init --stdlayout "$svnrepo" &&
+ git svn fetch &&
+ git checkout remotes/trunk
+'
+
+test_expect_success 'git svn branch tests' '
+ git svn branch a &&
+ base=$(git rev-parse HEAD:) &&
+ test $base = $(git rev-parse remotes/a:) &&
+ git svn branch -m "created branch b blah" b &&
+ test $base = $(git rev-parse remotes/b:) &&
+ test_must_fail git branch -m "no branchname" &&
+ git svn branch -n c &&
+ test_must_fail git rev-parse remotes/c &&
+ test_must_fail git svn branch a &&
+ git svn branch -t tag1 &&
+ test $base = $(git rev-parse remotes/tags/tag1:) &&
+ git svn branch --tag tag2 &&
+ test $base = $(git rev-parse remotes/tags/tag2:) &&
+ git svn tag tag3 &&
+ test $base = $(git rev-parse remotes/tags/tag3:) &&
+ git svn tag -m "created tag4 foo" tag4 &&
+ test $base = $(git rev-parse remotes/tags/tag4:) &&
+ test_must_fail git svn tag -m "no tagname" &&
+ git svn tag -n tag5 &&
+ test_must_fail git rev-parse remotes/tags/tag5 &&
+ test_must_fail git svn tag tag1
+'
+
+test_expect_success 'branch uses correct svn-remote' '
+ (svn_cmd co "$svnrepo" svn &&
+ cd svn &&
+ mkdir mirror &&
+ svn_cmd add mirror &&
+ svn_cmd copy trunk mirror/ &&
+ svn_cmd copy tags mirror/ &&
+ svn_cmd copy branches mirror/ &&
+ svn_cmd ci -m "made mirror" ) &&
+ rm -rf svn &&
+ git svn init -s -R mirror --prefix=mirror/ "$svnrepo"/mirror &&
+ git svn fetch -R mirror &&
+ git checkout mirror/trunk &&
+ base=$(git rev-parse HEAD:) &&
+ git svn branch -m "branch in mirror" d &&
+ test $base = $(git rev-parse remotes/mirror/d:) &&
+ test_must_fail git rev-parse remotes/d
+'
+
+test_done
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
new file mode 100755
index 0000000..8cfdfe7
--- /dev/null
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Eric Wong
+
+test_description='git svn honors i18n.commitEncoding in config'
+
+. ./lib-git-svn.sh
+
+compare_git_head_with () {
+ nr=`wc -l < "$1"`
+ a=7
+ b=$(($a + $nr - 1))
+ git cat-file commit HEAD | sed -ne "$a,${b}p" >current &&
+ test_cmp current "$1"
+}
+
+a_utf8_locale=$(locale -a | sed -n '/\.[uU][tT][fF]-*8$/{
+ p
+ q
+}')
+
+if test -n "$a_utf8_locale"
+then
+ test_set_prereq UTF8
+else
+ say "# UTF-8 locale not available, some tests are skipped"
+fi
+
+compare_svn_head_with () {
+ # extract just the log message and strip out committer info.
+ # don't use --limit here since svn 1.1.x doesn't have it,
+ LC_ALL="$a_utf8_locale" svn log `git svn info --url` | perl -w -e '
+ use bytes;
+ $/ = ("-"x72) . "\n";
+ my @x = <STDIN>;
+ @x = split(/\n/, $x[1]);
+ splice(@x, 0, 2);
+ $x[-1] = "";
+ print join("\n", @x);
+ ' > current &&
+ test_cmp current "$1"
+}
+
+for H in ISO8859-1 eucJP ISO-2022-JP
+do
+ test_expect_success "$H setup" '
+ mkdir $H &&
+ svn_cmd import -m "$H test" $H "$svnrepo"/$H &&
+ git svn clone "$svnrepo"/$H $H
+ '
+done
+
+for H in ISO8859-1 eucJP ISO-2022-JP
+do
+ test_expect_success "$H commit on git side" '
+ (
+ cd $H &&
+ git config i18n.commitencoding $H &&
+ git checkout -b t refs/remotes/git-svn &&
+ echo $H >F &&
+ git add F &&
+ git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
+ E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") &&
+ test "z$E" = "z$H"
+ compare_git_head_with "$TEST_DIRECTORY"/t3900/$H.txt
+ )
+ '
+done
+
+for H in ISO8859-1 eucJP ISO-2022-JP
+do
+ test_expect_success "$H dcommit to svn" '
+ (
+ cd $H &&
+ git svn dcommit &&
+ git cat-file commit HEAD | grep git-svn-id: &&
+ E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") &&
+ test "z$E" = "z$H" &&
+ compare_git_head_with "$TEST_DIRECTORY"/t3900/$H.txt
+ )
+ '
+done
+
+test_expect_success UTF8 'ISO-8859-1 should match UTF-8 in svn' '
+ (
+ cd ISO8859-1 &&
+ compare_svn_head_with "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
+ )
+'
+
+for H in eucJP ISO-2022-JP
+do
+ test_expect_success UTF8 "$H should match UTF-8 in svn" '
+ (
+ cd $H &&
+ compare_svn_head_with "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
+ )
+ '
+done
+
+test_done
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh
new file mode 100755
index 0000000..134411e
--- /dev/null
+++ b/t/t9130-git-svn-authors-file.sh
@@ -0,0 +1,117 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Eric Wong
+#
+
+test_description='git svn authors file tests'
+
+. ./lib-git-svn.sh
+
+cat > svn-authors <<EOF
+aa = AAAAAAA AAAAAAA <aa@example.com>
+bb = BBBBBBB BBBBBBB <bb@example.com>
+EOF
+
+test_expect_success 'setup svnrepo' '
+ for i in aa bb cc dd
+ do
+ svn_cmd mkdir -m $i --username $i "$svnrepo"/$i
+ done
+ '
+
+test_expect_success 'start import with incomplete authors file' '
+ ! git svn clone --authors-file=svn-authors "$svnrepo" x
+ '
+
+test_expect_success 'imported 2 revisions successfully' '
+ (
+ cd x
+ test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 2 &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+ grep "^author BBBBBBB BBBBBBB <bb@example\.com> " &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+ grep "^author AAAAAAA AAAAAAA <aa@example\.com> "
+ )
+ '
+
+cat >> svn-authors <<EOF
+cc = CCCCCCC CCCCCCC <cc@example.com>
+dd = DDDDDDD DDDDDDD <dd@example.com>
+EOF
+
+test_expect_success 'continues to import once authors have been added' '
+ (
+ cd x
+ git svn fetch --authors-file=../svn-authors &&
+ test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 4 &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+ grep "^author DDDDDDD DDDDDDD <dd@example\.com> " &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+ grep "^author CCCCCCC CCCCCCC <cc@example\.com> "
+ )
+ '
+
+test_expect_success 'authors-file against globs' '
+ svn_cmd mkdir -m globs --username aa \
+ "$svnrepo"/aa/trunk "$svnrepo"/aa/branches "$svnrepo"/aa/tags &&
+ git svn clone --authors-file=svn-authors -s "$svnrepo"/aa aa-work &&
+ for i in bb ee cc
+ do
+ branch="aa/branches/$i"
+ svn_cmd mkdir -m "$branch" --username $i "$svnrepo/$branch"
+ done
+ '
+
+test_expect_success 'fetch fails on ee' '
+ ( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+ '
+
+tmp_config_get () {
+ GIT_CONFIG=.git/svn/.metadata git config --get "$1"
+}
+
+test_expect_success 'failure happened without negative side effects' '
+ (
+ cd aa-work &&
+ test 6 -eq "`tmp_config_get svn-remote.svn.branches-maxRev`" &&
+ test 6 -eq "`tmp_config_get svn-remote.svn.tags-maxRev`"
+ )
+ '
+
+cat >> svn-authors <<EOF
+ee = EEEEEEE EEEEEEE <ee@example.com>
+EOF
+
+test_expect_success 'fetch continues after authors-file is fixed' '
+ (
+ cd aa-work &&
+ git svn fetch --authors-file=../svn-authors &&
+ test 8 -eq "`tmp_config_get svn-remote.svn.branches-maxRev`" &&
+ test 8 -eq "`tmp_config_get svn-remote.svn.tags-maxRev`"
+ )
+ '
+
+test_expect_success 'fresh clone with svn.authors-file in config' '
+ (
+ rm -r "$GIT_DIR" &&
+ test x = x"$(git config svn.authorsfile)" &&
+ HOME="`pwd`" &&
+ export HOME &&
+ test_config="$HOME"/.gitconfig &&
+ unset GIT_CONFIG_NOGLOBAL &&
+ unset GIT_DIR &&
+ unset GIT_CONFIG &&
+ git config --global \
+ svn.authorsfile "$HOME"/svn-authors &&
+ test x"$HOME"/svn-authors = x"$(git config svn.authorsfile)" &&
+ git svn clone "$svnrepo" gitconfig.clone &&
+ cd gitconfig.clone &&
+ nr_ex=$(git log | grep "^Author:.*example.com" | wc -l) &&
+ nr_rev=$(git rev-list HEAD | wc -l) &&
+ test $nr_rev -eq $nr_ex
+ )
+'
+
+test_debug 'GIT_DIR=gitconfig.clone/.git git log'
+
+test_done
diff --git a/t/t9131-git-svn-empty-symlink.sh b/t/t9131-git-svn-empty-symlink.sh
new file mode 100755
index 0000000..9a24a65
--- /dev/null
+++ b/t/t9131-git-svn-empty-symlink.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+test_description='test that git handles an svn repository with empty symlinks'
+
+. ./lib-git-svn.sh
+test_expect_success 'load svn dumpfile' '
+ svnadmin load "$rawsvnrepo" <<EOF
+SVN-fs-dump-format-version: 2
+
+UUID: 60780f9a-7df5-43b4-83ab-60e2c0673ef7
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-11-26T07:17:27.590577Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 4
+test
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2008-11-26T07:18:03.511836Z
+PROPS-END
+
+Node-path: bar
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Content-length: 33
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+
+Revision-number: 2
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 13
+bar => doink
+
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2008-11-27T03:55:31.601672Z
+PROPS-END
+
+Node-path: bar
+Node-kind: file
+Node-action: change
+Text-content-length: 10
+Text-content-md5: 92ca4fe7a9721f877f765c252dcd66c9
+Content-length: 10
+
+link doink
+
+EOF
+'
+
+test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" x'
+test_expect_success 'enable broken symlink workaround' \
+ '(cd x && git config svn.brokenSymlinkWorkaround true)'
+test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar'
+test_expect_success 'get "bar" => symlink fix from svn' \
+ '(cd x && git svn rebase)'
+test_expect_success SYMLINKS '"bar" becomes a symlink' 'test -L x/bar'
+
+
+test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y'
+test_expect_success 'disable broken symlink workaround' \
+ '(cd y && git config svn.brokenSymlinkWorkaround false)'
+test_expect_success '"bar" is an empty file' 'test -f y/bar && ! test -s y/bar'
+test_expect_success 'get "bar" => symlink fix from svn' \
+ '(cd y && git svn rebase)'
+test_expect_success '"bar" does not become a symlink' '! test -L y/bar'
+
+# svn.brokenSymlinkWorkaround is unset
+test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" z'
+test_expect_success '"bar" is an empty file' 'test -f z/bar && ! test -s z/bar'
+test_expect_success 'get "bar" => symlink fix from svn' \
+ '(cd z && git svn rebase)'
+test_expect_success '"bar" does not become a symlink' '! test -L z/bar'
+
+
+test_done
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
new file mode 100755
index 0000000..6c4c90b
--- /dev/null
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='test that git handles an svn repository with empty symlinks'
+
+. ./lib-git-svn.sh
+test_expect_success 'load svn dumpfile' '
+ svnadmin load "$rawsvnrepo" <<EOF
+SVN-fs-dump-format-version: 2
+
+UUID: 60780f9a-7df5-43b4-83ab-60e2c0673ef7
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2008-11-26T07:17:27.590577Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 4
+test
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2008-11-26T07:18:03.511836Z
+PROPS-END
+
+Node-path: bar
+Node-kind: file
+Node-action: add
+Prop-content-length: 33
+Text-content-length: 4
+Text-content-md5: 912ec803b2ce49e4a541068d495ab570
+Content-length: 37
+
+K 11
+svn:special
+V 1
+*
+PROPS-END
+asdf
+
+Revision-number: 2
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 13
+bar => doink
+
+K 10
+svn:author
+V 12
+normalperson
+K 8
+svn:date
+V 27
+2008-11-27T03:55:31.601672Z
+PROPS-END
+
+Node-path: bar
+Node-kind: file
+Node-action: change
+Text-content-length: 10
+Text-content-md5: 92ca4fe7a9721f877f765c252dcd66c9
+Content-length: 10
+
+link doink
+
+EOF
+'
+
+test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" x'
+
+test_expect_success SYMLINKS '"bar" is a symlink that points to "asdf"' '
+ test -L x/bar &&
+ (cd x && test xasdf = x"`git cat-file blob HEAD:bar`")
+'
+
+test_expect_success 'get "bar" => symlink fix from svn' '
+ (cd x && git svn rebase)
+'
+
+test_expect_success SYMLINKS '"bar" remains a proper symlink' '
+ test -L x/bar &&
+ (cd x && test xdoink = x"`git cat-file blob HEAD:bar`")
+'
+
+test_done
diff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh
new file mode 100755
index 0000000..f3c30e6
--- /dev/null
+++ b/t/t9133-git-svn-nested-git-repo.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+
+test_description='git svn property tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup repo with a git repo inside it' '
+ svn_cmd co "$svnrepo" s &&
+ (
+ cd s &&
+ git init &&
+ test -f .git/HEAD &&
+ > .git/a &&
+ echo a > a &&
+ svn_cmd add .git a &&
+ svn_cmd commit -m "create a nested git repo" &&
+ svn_cmd up &&
+ echo hi >> .git/a &&
+ svn_cmd commit -m "modify .git/a" &&
+ svn_cmd up
+ )
+'
+
+test_expect_success 'clone an SVN repo containing a git repo' '
+ git svn clone "$svnrepo" g &&
+ echo a > expect &&
+ test_cmp expect g/a
+'
+
+test_expect_success 'SVN-side change outside of .git' '
+ (
+ cd s &&
+ echo b >> a &&
+ svn_cmd commit -m "SVN-side change outside of .git" &&
+ svn_cmd up &&
+ svn_cmd log -v | fgrep "SVN-side change outside of .git"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo' '
+ (
+ cd g &&
+ git svn rebase &&
+ echo a > expect &&
+ echo b >> expect &&
+ test_cmp a expect &&
+ rm expect
+ )
+'
+
+test_expect_success 'SVN-side change inside of .git' '
+ (
+ cd s &&
+ git add a &&
+ git commit -m "add a inside an SVN repo" &&
+ git log &&
+ svn_cmd add --force .git &&
+ svn_cmd commit -m "SVN-side change inside of .git" &&
+ svn_cmd up &&
+ svn_cmd log -v | fgrep "SVN-side change inside of .git"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo' '
+ (
+ cd g &&
+ git svn rebase &&
+ echo a > expect &&
+ echo b >> expect &&
+ test_cmp a expect &&
+ rm expect
+ )
+'
+
+test_expect_success 'SVN-side change in and out of .git' '
+ (
+ cd s &&
+ echo c >> a &&
+ git add a &&
+ git commit -m "add a inside an SVN repo" &&
+ svn_cmd commit -m "SVN-side change in and out of .git" &&
+ svn_cmd up &&
+ svn_cmd log -v | fgrep "SVN-side change in and out of .git"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo again' '
+ (
+ cd g &&
+ git svn rebase &&
+ echo a > expect &&
+ echo b >> expect &&
+ echo c >> expect &&
+ test_cmp a expect &&
+ rm expect
+ )
+'
+
+test_done
diff --git a/t/t9134-git-svn-ignore-paths.sh b/t/t9134-git-svn-ignore-paths.sh
new file mode 100755
index 0000000..09ff10c
--- /dev/null
+++ b/t/t9134-git-svn-ignore-paths.sh
@@ -0,0 +1,147 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Vitaly Shukela
+# Copyright (c) 2009 Eric Wong
+#
+
+test_description='git svn property tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+ svn_cmd co "$svnrepo" s &&
+ (
+ cd s &&
+ mkdir qqq www &&
+ echo test_qqq > qqq/test_qqq.txt &&
+ echo test_www > www/test_www.txt &&
+ svn_cmd add qqq &&
+ svn_cmd add www &&
+ svn_cmd commit -m "create some files" &&
+ svn_cmd up &&
+ echo hi >> www/test_www.txt &&
+ svn_cmd commit -m "modify www/test_www.txt" &&
+ svn_cmd up
+ )
+'
+
+test_expect_success 'clone an SVN repository with ignored www directory' '
+ git svn clone --ignore-paths="^www" "$svnrepo" g &&
+ echo test_qqq > expect &&
+ for i in g/*/*.txt; do cat $i >> expect2; done &&
+ test_cmp expect expect2
+'
+
+test_expect_success 'init+fetch an SVN repository with ignored www directory' '
+ git svn init "$svnrepo" c &&
+ ( cd c && git svn fetch --ignore-paths="^www" ) &&
+ rm expect2 &&
+ echo test_qqq > expect &&
+ for i in c/*/*.txt; do cat $i >> expect2; done &&
+ test_cmp expect expect2
+'
+
+test_expect_success 'verify ignore-paths config saved by clone' '
+ (
+ cd g &&
+ git config --get svn-remote.svn.ignore-paths | fgrep "www"
+ )
+'
+
+test_expect_success 'SVN-side change outside of www' '
+ (
+ cd s &&
+ echo b >> qqq/test_qqq.txt &&
+ svn_cmd commit -m "SVN-side change outside of www" &&
+ svn_cmd up &&
+ svn_cmd log -v | fgrep "SVN-side change outside of www"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo (config ignore)' '
+ (
+ cd g &&
+ git svn rebase &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'update git svn-cloned repo (option ignore)' '
+ (
+ cd c &&
+ git svn rebase --ignore-paths="^www" &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'SVN-side change inside of ignored www' '
+ (
+ cd s &&
+ echo zaq >> www/test_www.txt
+ svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
+ svn_cmd up &&
+ svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo (config ignore)' '
+ (
+ cd g &&
+ git svn rebase &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'update git svn-cloned repo (option ignore)' '
+ (
+ cd c &&
+ git svn rebase --ignore-paths="^www" &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'SVN-side change in and out of ignored www' '
+ (
+ cd s &&
+ echo cvf >> www/test_www.txt
+ echo ygg >> qqq/test_qqq.txt
+ svn_cmd commit -m "SVN-side change in and out of ignored www" &&
+ svn_cmd up &&
+ svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo again (config ignore)' '
+ (
+ cd g &&
+ git svn rebase &&
+ printf "test_qqq\nb\nygg\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'update git svn-cloned repo again (option ignore)' '
+ (
+ cd c &&
+ git svn rebase --ignore-paths="^www" &&
+ printf "test_qqq\nb\nygg\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_done
diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh
new file mode 100755
index 0000000..5280e5f
--- /dev/null
+++ b/t/t9135-git-svn-moved-branch-empty-file.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+test_description='test moved svn branch with missing empty files'
+
+. ./lib-git-svn.sh
+test_expect_success 'load svn dumpfile' '
+ svnadmin load "$rawsvnrepo" < "${TEST_DIRECTORY}/t9135/svn.dump"
+ '
+
+test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x'
+
+test_expect_success 'test that b1 exists and is empty' '
+ (
+ cd x &&
+ git reset --hard branch-c &&
+ test -f b1 &&
+ ! test -s b1
+ )
+ '
+
+test_done
diff --git a/t/t9135/svn.dump b/t/t9135/svn.dump
new file mode 100644
index 0000000..b51c0cc
--- /dev/null
+++ b/t/t9135/svn.dump
@@ -0,0 +1,192 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 1f80e919-e9e3-4d80-a3ae-d9f21095e27b
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2009-02-10T19:23:16.424027Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 20
+init standard layout
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:17.195072Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 18
+branch-b off trunk
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:19.160095Z
+PROPS-END
+
+Node-path: branches/branch-b
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Revision-number: 3
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 17
+add empty file b1
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:20.194568Z
+PROPS-END
+
+Node-path: branches/branch-b/b1
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 4
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 8
+branch-c
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:21.169100Z
+PROPS-END
+
+Node-path: branches/branch-c
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 3
+Node-copyfrom-path: trunk
+
+
+Revision-number: 5
+Prop-content-length: 126
+Content-length: 126
+
+K 7
+svn:log
+V 23
+oops, wrong branchpoint
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:21.253557Z
+PROPS-END
+
+Node-path: branches/branch-c
+Node-action: delete
+
+
+Revision-number: 6
+Prop-content-length: 127
+Content-length: 127
+
+K 7
+svn:log
+V 24
+branch-c off of branch-b
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:21.314659Z
+PROPS-END
+
+Node-path: branches/branch-c
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 5
+Node-copyfrom-path: branches/branch-b
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
diff --git a/t/t9136-git-svn-recreated-branch-empty-file.sh b/t/t9136-git-svn-recreated-branch-empty-file.sh
new file mode 100755
index 0000000..733d16e
--- /dev/null
+++ b/t/t9136-git-svn-recreated-branch-empty-file.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+test_description='test recreated svn branch with empty files'
+
+. ./lib-git-svn.sh
+test_expect_success 'load svn dumpfile' '
+ svnadmin load "$rawsvnrepo" < "${TEST_DIRECTORY}/t9136/svn.dump"
+ '
+
+test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x'
+
+test_done
diff --git a/t/t9136/svn.dump b/t/t9136/svn.dump
new file mode 100644
index 0000000..6b1ce0b
--- /dev/null
+++ b/t/t9136/svn.dump
@@ -0,0 +1,192 @@
+SVN-fs-dump-format-version: 2
+
+UUID: eecae021-8f16-48da-969d-79beb8ae6ea5
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2009-02-22T00:50:56.292890Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 106
+Content-length: 106
+
+K 7
+svn:log
+V 4
+init
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:57.192384Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: tags
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 105
+Content-length: 105
+
+K 7
+svn:log
+V 3
+1.0
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.124724Z
+PROPS-END
+
+Node-path: tags/1.0
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 3
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 9
+1.0.1-bad
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.151727Z
+PROPS-END
+
+Node-path: tags/1.0.1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: tags/1.0
+
+
+Revision-number: 4
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 9
+Wrong tag
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.167427Z
+PROPS-END
+
+Node-path: tags/1.0.1
+Node-action: delete
+
+
+Revision-number: 5
+Prop-content-length: 113
+Content-length: 113
+
+K 7
+svn:log
+V 10
+1.0-branch
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.184498Z
+PROPS-END
+
+Node-path: branches/1.0
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 4
+Node-copyfrom-path: tags/1.0
+
+
+Revision-number: 6
+Prop-content-length: 113
+Content-length: 113
+
+K 7
+svn:log
+V 10
+1.0.1-good
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-22T00:50:58.200695Z
+PROPS-END
+
+Node-path: tags/1.0.1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 5
+Node-copyfrom-path: branches/1.0
+
+
diff --git a/t/t9137-git-svn-dcommit-clobber-series.sh b/t/t9137-git-svn-dcommit-clobber-series.sh
new file mode 100755
index 0000000..636ca0a
--- /dev/null
+++ b/t/t9137-git-svn-dcommit-clobber-series.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Eric Wong
+test_description='git svn dcommit clobber series'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+ mkdir import &&
+ cd import &&
+ awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
+ svn_cmd import -m "initial" . "$svnrepo" &&
+ cd .. &&
+ git svn init "$svnrepo" &&
+ git svn fetch &&
+ test -e file
+ '
+
+test_expect_success '(supposedly) non-conflicting change from SVN' '
+ test x"`sed -n -e 58p < file`" = x58 &&
+ test x"`sed -n -e 61p < file`" = x61 &&
+ svn_cmd co "$svnrepo" tmp &&
+ cd tmp &&
+ perl -i.bak -p -e "s/^58$/5588/" file &&
+ perl -i.bak -p -e "s/^61$/6611/" file &&
+ poke file &&
+ test x"`sed -n -e 58p < file`" = x5588 &&
+ test x"`sed -n -e 61p < file`" = x6611 &&
+ svn_cmd commit -m "58 => 5588, 61 => 6611" &&
+ cd ..
+ '
+
+test_expect_success 'some unrelated changes to git' "
+ echo hi > life &&
+ git update-index --add life &&
+ git commit -m hi-life &&
+ echo bye >> life &&
+ git commit -m bye-life life
+ "
+
+test_expect_success 'change file but in unrelated area' "
+ test x\"\`sed -n -e 4p < file\`\" = x4 &&
+ test x\"\`sed -n -e 7p < file\`\" = x7 &&
+ perl -i.bak -p -e 's/^4\$/4444/' file &&
+ perl -i.bak -p -e 's/^7\$/7777/' file &&
+ test x\"\`sed -n -e 4p < file\`\" = x4444 &&
+ test x\"\`sed -n -e 7p < file\`\" = x7777 &&
+ git commit -m '4 => 4444, 7 => 7777' file &&
+ git svn dcommit &&
+ svn_cmd up tmp &&
+ cd tmp &&
+ test x\"\`sed -n -e 4p < file\`\" = x4444 &&
+ test x\"\`sed -n -e 7p < file\`\" = x7777 &&
+ test x\"\`sed -n -e 58p < file\`\" = x5588 &&
+ test x\"\`sed -n -e 61p < file\`\" = x6611
+ "
+
+test_expect_success 'attempt to dcommit with a dirty index' '
+ echo foo >>file &&
+ git add file &&
+ test_must_fail git svn dcommit
+'
+
+test_done
diff --git a/t/t9138-git-svn-authors-prog.sh b/t/t9138-git-svn-authors-prog.sh
new file mode 100755
index 0000000..83cc5fc
--- /dev/null
+++ b/t/t9138-git-svn-authors-prog.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong, Mark Lodato
+#
+
+test_description='git svn authors prog tests'
+
+. ./lib-git-svn.sh
+
+cat > svn-authors-prog <<'EOF'
+#!/usr/bin/perl
+$_ = shift;
+if (s/-sub$//) {
+ print "$_ <$_\@sub.example.com>\n";
+}
+else {
+ print "$_ <$_\@example.com>\n";
+}
+EOF
+chmod +x svn-authors-prog
+
+cat > svn-authors <<'EOF'
+ff = FFFFFFF FFFFFFF <fFf@other.example.com>
+EOF
+
+test_expect_success 'setup svnrepo' '
+ for i in aa bb cc-sub dd-sub ee-foo ff
+ do
+ svn mkdir -m $i --username $i "$svnrepo"/$i
+ done
+ '
+
+test_expect_success 'import authors with prog and file' '
+ git svn clone --authors-prog=./svn-authors-prog \
+ --authors-file=svn-authors "$svnrepo" x
+ '
+
+test_expect_success 'imported 6 revisions successfully' '
+ (
+ cd x
+ test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 6
+ )
+ '
+
+test_expect_success 'authors-prog ran correctly' '
+ (
+ cd x
+ git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+ grep "^author ee-foo <ee-foo@example\.com> " &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn~2 | \
+ grep "^author dd <dd@sub\.example\.com> " &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn~3 | \
+ grep "^author cc <cc@sub\.example\.com> " &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn~4 | \
+ grep "^author bb <bb@example\.com> " &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn~5 | \
+ grep "^author aa <aa@example\.com> "
+ )
+ '
+
+test_expect_success 'authors-file overrode authors-prog' '
+ (
+ cd x
+ git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+ grep "^author FFFFFFF FFFFFFF <fFf@other\.example\.com> "
+ )
+ '
+
+git --git-dir=x/.git config --unset svn.authorsfile
+git --git-dir=x/.git config --unset svn.authorsprog
+
+test_expect_success 'authors-prog handled special characters in username' '
+ svn mkdir -m bad --username "xyz; touch evil" "$svnrepo"/bad &&
+ (
+ cd x &&
+ git svn --authors-prog=../svn-authors-prog fetch &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn |
+ grep "^author xyz; touch evil <xyz; touch evil@example\.com> " &&
+ ! test -f evil
+ )
+'
+
+test_done
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
new file mode 100755
index 0000000..f337959
--- /dev/null
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn refuses to dcommit non-UTF8 messages'
+
+. ./lib-git-svn.sh
+
+# ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
+
+for H in ISO8859-1 eucJP
+do
+ test_expect_success "$H setup" '
+ mkdir $H &&
+ svn_cmd import -m "$H test" $H "$svnrepo"/$H &&
+ git svn clone "$svnrepo"/$H $H
+ '
+done
+
+for H in ISO8859-1 eucJP
+do
+ test_expect_success "$H commit on git side" '
+ (
+ cd $H &&
+ git config i18n.commitencoding $H &&
+ git checkout -b t refs/remotes/git-svn &&
+ echo $H >F &&
+ git add F &&
+ git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
+ E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") &&
+ test "z$E" = "z$H"
+ )
+ '
+done
+
+for H in ISO8859-1 eucJP
+do
+ test_expect_success "$H dcommit to svn" '
+ (
+ cd $H &&
+ git config --unset i18n.commitencoding &&
+ ! git svn dcommit
+ )
+ '
+done
+
+test_done
diff --git a/t/t9140-git-svn-reset.sh b/t/t9140-git-svn-reset.sh
new file mode 100755
index 0000000..0735526
--- /dev/null
+++ b/t/t9140-git-svn-reset.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Ben Jackson
+#
+
+test_description='git svn reset'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+ svn_cmd co "$svnrepo" s &&
+ (
+ cd s &&
+ mkdir vis &&
+ echo always visible > vis/vis.txt &&
+ svn_cmd add vis &&
+ svn_cmd commit -m "create visible files" &&
+ mkdir hid &&
+ echo initially hidden > hid/hid.txt &&
+ svn_cmd add hid &&
+ svn_cmd commit -m "create initially hidden files" &&
+ svn_cmd up &&
+ echo mod >> vis/vis.txt &&
+ svn_cmd commit -m "modify vis" &&
+ svn_cmd up
+ )
+'
+
+test_expect_success 'clone SVN repository with hidden directory' '
+ git svn init "$svnrepo" g &&
+ ( cd g && git svn fetch --ignore-paths="^hid" )
+'
+
+test_expect_success 'modify hidden file in SVN repo' '
+ ( cd s &&
+ echo mod hidden >> hid/hid.txt &&
+ svn_cmd commit -m "modify hid" &&
+ svn_cmd up
+ )
+'
+
+test_expect_success 'fetch fails on modified hidden file' '
+ ( cd g &&
+ git svn find-rev refs/remotes/git-svn > ../expect &&
+ ! git svn fetch 2> ../errors &&
+ git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
+ fgrep "not found in commit" errors &&
+ test_cmp expect expect2
+'
+
+test_expect_success 'reset unwinds back to r1' '
+ ( cd g &&
+ git svn reset -r1 &&
+ git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
+ echo 1 >expect &&
+ test_cmp expect expect2
+'
+
+test_expect_success 'refetch succeeds not ignoring any files' '
+ ( cd g &&
+ git svn fetch &&
+ git svn rebase &&
+ fgrep "mod hidden" hid/hid.txt
+ )
+'
+
+test_done
diff --git a/t/t9141-git-svn-multiple-branches.sh b/t/t9141-git-svn-multiple-branches.sh
new file mode 100755
index 0000000..3cd0671
--- /dev/null
+++ b/t/t9141-git-svn-multiple-branches.sh
@@ -0,0 +1,122 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Marc Branchaud
+#
+
+test_description='git svn multiple branch and tag paths in the svn repo'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svnrepo' '
+ mkdir project \
+ project/trunk \
+ project/b_one \
+ project/b_two \
+ project/tags_A \
+ project/tags_B &&
+ echo 1 > project/trunk/a.file &&
+ svn_cmd import -m "$test_description" project "$svnrepo/project" &&
+ rm -rf project &&
+ svn_cmd cp -m "Branch 1" "$svnrepo/project/trunk" \
+ "$svnrepo/project/b_one/first" &&
+ svn_cmd cp -m "Tag 1" "$svnrepo/project/trunk" \
+ "$svnrepo/project/tags_A/1.0" &&
+ svn_cmd co "$svnrepo/project" svn_project &&
+ ( cd svn_project &&
+ echo 2 > trunk/a.file &&
+ svn_cmd ci -m "Change 1" trunk/a.file &&
+ svn_cmd cp -m "Branch 2" "$svnrepo/project/trunk" \
+ "$svnrepo/project/b_one/second" &&
+ svn_cmd cp -m "Tag 2" "$svnrepo/project/trunk" \
+ "$svnrepo/project/tags_A/2.0" &&
+ echo 3 > trunk/a.file &&
+ svn_cmd ci -m "Change 2" trunk/a.file &&
+ svn_cmd cp -m "Branch 3" "$svnrepo/project/trunk" \
+ "$svnrepo/project/b_two/1" &&
+ svn_cmd cp -m "Tag 3" "$svnrepo/project/trunk" \
+ "$svnrepo/project/tags_A/3.0" &&
+ echo 4 > trunk/a.file &&
+ svn_cmd ci -m "Change 3" trunk/a.file &&
+ svn_cmd cp -m "Branch 4" "$svnrepo/project/trunk" \
+ "$svnrepo/project/b_two/2" &&
+ svn_cmd cp -m "Tag 4" "$svnrepo/project/trunk" \
+ "$svnrepo/project/tags_A/4.0" &&
+ svn_cmd up &&
+ echo 5 > b_one/first/a.file &&
+ svn_cmd ci -m "Change 4" b_one/first/a.file &&
+ svn_cmd cp -m "Tag 5" "$svnrepo/project/b_one/first" \
+ "$svnrepo/project/tags_B/v5" &&
+ echo 6 > b_one/second/a.file &&
+ svn_cmd ci -m "Change 5" b_one/second/a.file &&
+ svn_cmd cp -m "Tag 6" "$svnrepo/project/b_one/second" \
+ "$svnrepo/project/tags_B/v6" &&
+ echo 7 > b_two/1/a.file &&
+ svn_cmd ci -m "Change 6" b_two/1/a.file &&
+ svn_cmd cp -m "Tag 7" "$svnrepo/project/b_two/1" \
+ "$svnrepo/project/tags_B/v7" &&
+ echo 8 > b_two/2/a.file &&
+ svn_cmd ci -m "Change 7" b_two/2/a.file &&
+ svn_cmd cp -m "Tag 8" "$svnrepo/project/b_two/2" \
+ "$svnrepo/project/tags_B/v8"
+ )
+'
+
+test_expect_success 'clone multiple branch and tag paths' '
+ git svn clone -T trunk \
+ -b b_one/* --branches b_two/* \
+ -t tags_A/* --tags tags_B \
+ "$svnrepo/project" git_project &&
+ ( cd git_project &&
+ git rev-parse refs/remotes/first &&
+ git rev-parse refs/remotes/second &&
+ git rev-parse refs/remotes/1 &&
+ git rev-parse refs/remotes/2 &&
+ git rev-parse refs/remotes/tags/1.0 &&
+ git rev-parse refs/remotes/tags/2.0 &&
+ git rev-parse refs/remotes/tags/3.0 &&
+ git rev-parse refs/remotes/tags/4.0 &&
+ git rev-parse refs/remotes/tags/v5 &&
+ git rev-parse refs/remotes/tags/v6 &&
+ git rev-parse refs/remotes/tags/v7 &&
+ git rev-parse refs/remotes/tags/v8
+ )
+'
+
+test_expect_success 'Multiple branch or tag paths require -d' '
+ ( cd git_project &&
+ test_must_fail git svn branch -m "No new branch" Nope &&
+ test_must_fail git svn tag -m "No new tag" Tagless &&
+ test_must_fail git rev-parse refs/remotes/Nope &&
+ test_must_fail git rev-parse refs/remotes/tags/Tagless
+ ) &&
+ ( cd svn_project &&
+ svn_cmd up &&
+ test_must_fail test -d b_one/Nope &&
+ test_must_fail test -d b_two/Nope &&
+ test_must_fail test -d tags_A/Tagless &&
+ test_must_fail test -d tags_B/Tagless
+ )
+'
+
+test_expect_success 'create new branches and tags' '
+ ( cd git_project &&
+ git svn branch -m "New branch 1" -d b_one New1 ) &&
+ ( cd svn_project &&
+ svn_cmd up && test -e b_one/New1/a.file ) &&
+
+ ( cd git_project &&
+ git svn branch -m "New branch 2" -d b_two New2 ) &&
+ ( cd svn_project &&
+ svn_cmd up && test -e b_two/New2/a.file ) &&
+
+ ( cd git_project &&
+ git svn branch -t -m "New tag 1" -d tags_A Tag1 ) &&
+ ( cd svn_project &&
+ svn_cmd up && test -e tags_A/Tag1/a.file ) &&
+
+ ( cd git_project &&
+ git svn tag -m "New tag 2" -d tags_B Tag2 ) &&
+ ( cd svn_project &&
+ svn_cmd up && test -e tags_B/Tag2/a.file )
+'
+
+test_done
diff --git a/t/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh
new file mode 100755
index 0000000..1236acc
--- /dev/null
+++ b/t/t9142-git-svn-shallow-clone.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+
+test_description='git svn shallow clone'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+ svn_cmd mkdir -m "create standard layout" \
+ "$svnrepo"/trunk "$svnrepo"/branches "$svnrepo"/tags &&
+ svn_cmd cp -m "branch off trunk" \
+ "$svnrepo"/trunk "$svnrepo"/branches/a &&
+ svn_cmd co "$svnrepo"/branches/a &&
+ (
+ cd a &&
+ > foo &&
+ svn_cmd add foo &&
+ svn_cmd commit -m "add foo"
+ )
+'
+
+start_httpd
+
+test_expect_success 'clone trunk with "-r HEAD"' '
+ git svn clone -r HEAD "$svnrepo/trunk" g &&
+ ( cd g && git rev-parse --symbolic --verify HEAD )
+'
+
+stop_httpd
+
+test_done
diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh
new file mode 100755
index 0000000..337ea59
--- /dev/null
+++ b/t/t9143-git-svn-gc.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Robert Allan Zeh
+
+test_description='git svn gc basic tests'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup directories and test repo' '
+ mkdir import &&
+ mkdir tmp &&
+ echo "Sample text for Subversion repository." > import/test.txt &&
+ svn_cmd import -m "import for git svn" import "$svnrepo" > /dev/null
+ '
+
+test_expect_success 'checkout working copy from svn' \
+ 'svn_cmd co "$svnrepo" test_wc'
+
+test_expect_success 'set some properties to create an unhandled.log file' '
+ (
+ cd test_wc &&
+ svn_cmd propset foo bar test.txt &&
+ svn_cmd commit -m "property set"
+ )'
+
+test_expect_success 'Setup repo' 'git svn init "$svnrepo"'
+
+test_expect_success 'Fetch repo' 'git svn fetch'
+
+test_expect_success 'make backup copy of unhandled.log' '
+ cp .git/svn/refs/remotes/git-svn/unhandled.log tmp
+ '
+
+test_expect_success 'create leftover index' '> .git/svn/refs/remotes/git-svn/index'
+
+test_expect_success 'git svn gc runs' 'git svn gc'
+
+test_expect_success 'git svn index removed' '! test -f .git/svn/refs/remotes/git-svn/index'
+
+if perl -MCompress::Zlib -e 0 2>/dev/null
+then
+ test_expect_success 'git svn gc produces a valid gzip file' '
+ gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz
+ '
+else
+ say "# Perl Compress::Zlib unavailable, skipping gunzip test"
+fi
+
+test_expect_success 'git svn gc does not change unhandled.log files' '
+ test_cmp .git/svn/refs/remotes/git-svn/unhandled.log tmp/unhandled.log
+ '
+
+test_done
diff --git a/t/t9144-git-svn-old-rev_map.sh b/t/t9144-git-svn-old-rev_map.sh
new file mode 100755
index 0000000..7600a35
--- /dev/null
+++ b/t/t9144-git-svn-old-rev_map.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn old rev_map preservd'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository with old layout' '
+ mkdir i &&
+ (cd i && > a) &&
+ svn_cmd import -m- i "$svnrepo" &&
+ git svn init "$svnrepo" &&
+ git svn fetch &&
+ test -d .git/svn/refs/remotes/git-svn/ &&
+ ! test -e .git/svn/git-svn/ &&
+ mv .git/svn/refs/remotes/git-svn .git/svn/ &&
+ rm -r .git/svn/refs
+'
+
+test_expect_success 'old layout continues to work' '
+ svn_cmd import -m- i "$svnrepo/b" &&
+ git svn rebase &&
+ echo a >> b/a &&
+ git add b/a &&
+ git commit -m- -a &&
+ git svn dcommit &&
+ ! test -d .git/svn/refs/ &&
+ test -e .git/svn/git-svn/
+'
+
+test_done
diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh
new file mode 100755
index 0000000..16852d2
--- /dev/null
+++ b/t/t9145-git-svn-master-branch.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+test_description='git svn initial master branch is "trunk" if possible'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+ mkdir i &&
+ > i/a &&
+ svn_cmd import -m trunk i "$svnrepo/trunk" &&
+ svn_cmd import -m b/a i "$svnrepo/branches/a" &&
+ svn_cmd import -m b/b i "$svnrepo/branches/b"
+'
+
+test_expect_success 'git svn clone --stdlayout sets up trunk as master' '
+ git svn clone -s "$svnrepo" g &&
+ (
+ cd g &&
+ test x`git rev-parse --verify refs/remotes/trunk^0` = \
+ x`git rev-parse --verify refs/heads/master^0`
+ )
+'
+
+test_done
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
new file mode 100755
index 0000000..565365c
--- /dev/null
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -0,0 +1,142 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn creates empty directories'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+ for i in a b c d d/e d/e/f "weird file name"
+ do
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+ done
+'
+
+test_expect_success 'clone' 'git svn clone "$svnrepo" cloned'
+
+test_expect_success 'empty directories exist' '
+ (
+ cd cloned &&
+ for i in a b c d d/e d/e/f "weird file name"
+ do
+ if ! test -d "$i"
+ then
+ echo >&2 "$i does not exist"
+ exit 1
+ fi
+ done
+ )
+'
+
+test_expect_success 'more emptiness' '
+ svn_cmd mkdir -m "bang bang" "$svnrepo"/"! !"
+'
+
+test_expect_success 'git svn rebase creates empty directory' '
+ ( cd cloned && git svn rebase )
+ test -d cloned/"! !"
+'
+
+test_expect_success 'git svn mkdirs recreates empty directories' '
+ (
+ cd cloned &&
+ rm -r * &&
+ git svn mkdirs &&
+ for i in a b c d d/e d/e/f "weird file name" "! !"
+ do
+ if ! test -d "$i"
+ then
+ echo >&2 "$i does not exist"
+ exit 1
+ fi
+ done
+ )
+'
+
+test_expect_success 'git svn mkdirs -r works' '
+ (
+ cd cloned &&
+ rm -r * &&
+ git svn mkdirs -r7 &&
+ for i in a b c d d/e d/e/f "weird file name"
+ do
+ if ! test -d "$i"
+ then
+ echo >&2 "$i does not exist"
+ exit 1
+ fi
+ done
+
+ if test -d "! !"
+ then
+ echo >&2 "$i should not exist"
+ exit 1
+ fi
+
+ git svn mkdirs -r8 &&
+ if ! test -d "! !"
+ then
+ echo >&2 "$i not exist"
+ exit 1
+ fi
+ )
+'
+
+test_expect_success 'initialize trunk' '
+ for i in trunk trunk/a trunk/"weird file name"
+ do
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+ done
+'
+
+test_expect_success 'clone trunk' 'git svn clone -s "$svnrepo" trunk'
+
+test_expect_success 'empty directories in trunk exist' '
+ (
+ cd trunk &&
+ for i in a "weird file name"
+ do
+ if ! test -d "$i"
+ then
+ echo >&2 "$i does not exist"
+ exit 1
+ fi
+ done
+ )
+'
+
+test_expect_success 'remove a top-level directory from svn' '
+ svn_cmd rm -m "remove d" "$svnrepo"/d
+'
+
+test_expect_success 'removed top-level directory does not exist' '
+ git svn clone "$svnrepo" removed &&
+ test ! -e removed/d
+
+'
+unhandled=.git/svn/refs/remotes/git-svn/unhandled.log
+test_expect_success 'git svn gc-ed files work' '
+ (
+ cd removed &&
+ git svn gc &&
+ : Compress::Zlib may not be available &&
+ if test -f "$unhandled".gz
+ then
+ svn_cmd mkdir -m gz "$svnrepo"/gz &&
+ git reset --hard $(git rev-list HEAD | tail -1) &&
+ git svn rebase &&
+ test -f "$unhandled".gz &&
+ test -f "$unhandled" &&
+ for i in a b c "weird file name" gz "! !"
+ do
+ if ! test -d "$i"
+ then
+ echo >&2 "$i does not exist"
+ exit 1
+ fi
+ done
+ fi
+ )
+'
+
+test_done
diff --git a/t/t9150-svk-mergetickets.sh b/t/t9150-svk-mergetickets.sh
new file mode 100755
index 0000000..24c2421
--- /dev/null
+++ b/t/t9150-svk-mergetickets.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Sam Vilain
+#
+
+test_description='git-svn svk merge tickets'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svk depot' "
+ svnadmin load -q '$rawsvnrepo' \
+ < '$TEST_DIRECTORY/t9150/svk-merge.dump' &&
+ git svn init --minimize-url -R svkmerge \
+ --rewrite-root=http://svn.example.org \
+ -T trunk -b branches '$svnrepo' &&
+ git svn fetch --all
+ "
+
+uuid=b48289b2-9c08-4d72-af37-0358a40b9c15
+
+test_expect_success 'svk merges were represented coming in' "
+ [ `git cat-file commit HEAD | grep parent | wc -l` -eq 2 ]
+ "
+
+test_done
diff --git a/t/t9150/make-svk-dump b/t/t9150/make-svk-dump
new file mode 100644
index 0000000..2242f14
--- /dev/null
+++ b/t/t9150/make-svk-dump
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# this script sets up a Subversion repository for Makefile in the
+# first ever git merge, as if it were done with svk.
+#
+
+set -e
+
+svk depotmap foo ~/.svk/foo
+svk co /foo/ foo
+cd foo
+mkdir trunk
+mkdir branches
+svk add trunk branches
+svk commit -m "Setup trunk and branches"
+cd trunk
+
+git cat-file blob 6683463e:Makefile > Makefile
+svk add Makefile
+
+svk commit -m "ancestor"
+cd ..
+svk cp trunk branches/left
+
+svk commit -m "make left branch"
+cd branches/left/
+
+git cat-file blob 5873b67e:Makefile > Makefile
+svk commit -m "left update 1"
+
+cd ../../trunk
+git cat-file blob 75118b13:Makefile > Makefile
+svk commit -m "trunk update"
+
+cd ../branches/left
+git cat-file blob b5039db6:Makefile > Makefile
+svk commit -m "left update 2"
+
+cd ../../trunk
+svk sm /foo/branches/left
+# in theory we could delete the "left" branch here, but it's not
+# required so don't do it, in case people start getting ideas ;)
+svk commit -m "merge branch 'left' into 'trunk'"
+
+git cat-file blob b51ad431:Makefile > Makefile
+
+svk diff Makefile && echo "Hey! No differences, magic"
+
+cd ../..
+
+svnadmin dump ~/.svk/foo > svk-merge.dump
+
+svk co -d foo
+rm -rf foo
+svk depotmap -d /foo/
+rm -rf ~/.svk/foo
+
diff --git a/t/t9150/svk-merge.dump b/t/t9150/svk-merge.dump
new file mode 100644
index 0000000..42f70db
--- /dev/null
+++ b/t/t9150/svk-merge.dump
@@ -0,0 +1,616 @@
+SVN-fs-dump-format-version: 2
+
+UUID: b48289b2-9c08-4d72-af37-0358a40b9c15
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2009-10-19T23:44:03.722969Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 24
+Setup trunk and branches
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:04.927533Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 106
+Content-length: 106
+
+K 7
+svn:log
+V 8
+ancestor
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:05.835585Z
+PROPS-END
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2401
+Text-content-md5: bfd8ff778d1492dc6758567373176a89
+Content-length: 2411
+
+PROPS-END
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 3
+Prop-content-length: 115
+Content-length: 115
+
+K 7
+svn:log
+V 16
+make left branch
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:06.719737Z
+PROPS-END
+
+Node-path: branches/left
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk
+
+
+Revision-number: 4
+Prop-content-length: 112
+Content-length: 112
+
+K 7
+svn:log
+V 13
+left update 1
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:07.167666Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2465
+Text-content-md5: 16e38d9753b061731650561ce01b1195
+Content-length: 2465
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 5
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 12
+trunk update
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:07.619633Z
+PROPS-END
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2521
+Text-content-md5: 0668418a621333f4aa8b6632cd63e2a0
+Content-length: 2521
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 6
+Prop-content-length: 112
+Content-length: 112
+
+K 7
+svn:log
+V 13
+left update 2
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:08.067554Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2593
+Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Content-length: 2593
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 7
+Prop-content-length: 131
+Content-length: 131
+
+K 7
+svn:log
+V 32
+merge branch 'left' into 'trunk'
+K 10
+svn:author
+V 4
+samv
+K 8
+svn:date
+V 27
+2009-10-19T23:44:08.971801Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 83
+Content-length: 83
+
+K 9
+svk:merge
+V 53
+b48289b2-9c08-4d72-af37-0358a40b9c15:/branches/left:6
+PROPS-END
+
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2713
+Text-content-md5: 0afbe34f244cd662b1f97d708c687f90
+Content-length: 2713
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
new file mode 100755
index 0000000..250c651
--- /dev/null
+++ b/t/t9151-svn-mergeinfo.sh
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Copyright (c) 2007, 2009 Sam Vilain
+#
+
+test_description='git-svn svn mergeinfo properties'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svn dump' "
+ svnadmin load -q '$rawsvnrepo' \
+ < '$TEST_DIRECTORY/t9151/svn-mergeinfo.dump' &&
+ git svn init --minimize-url -R svnmerge \
+ --rewrite-root=http://svn.example.org \
+ -T trunk -b branches '$svnrepo' &&
+ git svn fetch --all
+ "
+
+test_expect_success 'all svn merges became git merge commits' '
+ unmarked=$(git rev-list --parents --all --grep=Merge |
+ grep -v " .* " | cut -f1 -d" ")
+ [ -z "$unmarked" ]
+ '
+
+test_expect_success 'cherry picks did not become git merge commits' '
+ bad_cherries=$(git rev-list --parents --all --grep=Cherry |
+ grep " .* " | cut -f1 -d" ")
+ [ -z "$bad_cherries" ]
+ '
+
+test_expect_success 'svn non-merge merge commits did not become git merge commits' '
+ bad_non_merges=$(git rev-list --parents --all --grep=non-merge |
+ grep " .* " | cut -f1 -d" ")
+ [ -z "$bad_non_merges" ]
+ '
+
+test_expect_success 'commit made to merged branch is reachable from the merge' '
+ before_commit=$(git rev-list --all --grep="trunk commit before merging trunk to b2")
+ merge_commit=$(git rev-list --all --grep="Merge trunk to b2")
+ not_reachable=$(git rev-list -1 $before_commit --not $merge_commit)
+ [ -z "$not_reachable" ]
+ '
+
+test_expect_success 'merging two branches in one commit is detected correctly' '
+ f1_commit=$(git rev-list --all --grep="make f1 branch from trunk")
+ f2_commit=$(git rev-list --all --grep="make f2 branch from trunk")
+ merge_commit=$(git rev-list --all --grep="Merge f1 and f2 to trunk")
+ not_reachable=$(git rev-list -1 $f1_commit $f2_commit --not $merge_commit)
+ [ -z "$not_reachable" ]
+ '
+
+test_expect_failure 'everything got merged in the end' '
+ unmerged=$(git rev-list --all --not master)
+ [ -z "$unmerged" ]
+ '
+
+test_done
diff --git a/t/t9151/.gitignore b/t/t9151/.gitignore
new file mode 100644
index 0000000..587c37d
--- /dev/null
+++ b/t/t9151/.gitignore
@@ -0,0 +1,2 @@
+foo
+foo.svn
diff --git a/t/t9151/make-svnmerge-dump b/t/t9151/make-svnmerge-dump
new file mode 100644
index 0000000..e1e138c
--- /dev/null
+++ b/t/t9151/make-svnmerge-dump
@@ -0,0 +1,305 @@
+#!/bin/sh
+#
+# this script sets up a Subversion repository for Makefile in the
+# first ever git merge, as if it were done with svnmerge (SVN 1.5+)
+#
+
+rm -rf foo.svn foo
+set -e
+
+mkdir foo.svn
+svnadmin create foo.svn
+svn co file://`pwd`/foo.svn foo
+
+commit() {
+ i=$(( $1 + 1 ))
+ shift;
+ svn commit -m "(r$i) $*" >/dev/null || exit 1
+ echo $i
+}
+
+say() {
+ echo " * $*"
+}
+
+i=0
+cd foo
+mkdir trunk
+mkdir branches
+mkdir tags
+svn add trunk branches tags
+i=$(commit $i "Setup trunk, branches, and tags")
+
+git cat-file blob 6683463e:Makefile > trunk/Makefile
+svn add trunk/Makefile
+
+say "Committing ANCESTOR"
+i=$(commit $i "ancestor")
+svn cp trunk branches/left
+
+say "Committing BRANCH POINT"
+i=$(commit $i "make left branch")
+svn cp trunk branches/right
+
+say "Committing other BRANCH POINT"
+i=$(commit $i "make right branch")
+
+say "Committing LEFT UPDATE"
+git cat-file blob 5873b67e:Makefile > branches/left/Makefile
+i=$(commit $i "left update 1")
+
+git cat-file blob 75118b13:Makefile > branches/right/Makefile
+say "Committing RIGHT UPDATE"
+pre_right_update_1=$i
+i=$(commit $i "right update 1")
+
+say "Making more commits on LEFT"
+git cat-file blob ff5ebe39:Makefile > branches/left/Makefile
+i=$(commit $i "left update 2")
+git cat-file blob b5039db6:Makefile > branches/left/Makefile
+i=$(commit $i "left update 3")
+
+say "Making a LEFT SUB-BRANCH"
+svn cp branches/left branches/left-sub
+sub_left_make=$i
+i=$(commit $i "make left sub-branch")
+
+say "Making a commit on LEFT SUB-BRANCH"
+echo "crunch" > branches/left-sub/README
+svn add branches/left-sub/README
+i=$(commit $i "left sub-branch update 1")
+
+say "Merging LEFT to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/left --accept postpone
+git cat-file blob b5039db6:Makefile > Makefile
+svn resolved Makefile
+i=$(commit $i "Merge left to trunk 1")
+cd ..
+
+say "Making more commits on LEFT and RIGHT"
+echo "touche" > branches/left/zlonk
+svn add branches/left/zlonk
+i=$(commit $i "left update 4")
+echo "thwacke" > branches/right/bang
+svn add branches/right/bang
+i=$(commit $i "right update 2")
+
+say "Squash merge of RIGHT tip 2 commits onto TRUNK"
+svn update
+cd trunk
+svn merge -r$pre_right_update_1:$i ../branches/right
+i=$(commit $i "Cherry-pick right 2 commits to trunk")
+cd ..
+
+say "Merging RIGHT to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/right --accept postpone
+git cat-file blob b51ad431:Makefile > Makefile
+svn resolved Makefile
+i=$(commit $i "Merge right to trunk 1")
+cd ..
+
+say "Making more commits on RIGHT and TRUNK"
+echo "whamm" > branches/right/urkkk
+svn add branches/right/urkkk
+i=$(commit $i "right update 3")
+echo "pow" > trunk/vronk
+svn add trunk/vronk
+i=$(commit $i "trunk update 1")
+
+say "Merging RIGHT to LEFT SUB-BRANCH"
+svn update
+cd branches/left-sub
+svn merge ../right --accept postpone
+git cat-file blob b51ad431:Makefile > Makefile
+svn resolved Makefile
+i=$(commit $i "Merge right to left sub-branch")
+cd ../..
+
+say "Making more commits on LEFT SUB-BRANCH and LEFT"
+echo "zowie" > branches/left-sub/wham_eth
+svn add branches/left-sub/wham_eth
+pre_sub_left_update_2=$i
+i=$(commit $i "left sub-branch update 2")
+sub_left_update_2=$i
+echo "eee_yow" > branches/left/glurpp
+svn add branches/left/glurpp
+i=$(commit $i "left update 5")
+
+say "Cherry pick LEFT SUB-BRANCH commit to LEFT"
+svn update
+cd branches/left
+svn merge -r$pre_sub_left_update_2:$sub_left_update_2 ../left-sub
+i=$(commit $i "Cherry-pick left sub-branch commit to left")
+cd ../..
+
+say "Merging LEFT SUB-BRANCH back to LEFT"
+svn update
+cd branches/left
+# it's only a merge because the previous merge cherry-picked the top commit
+svn merge -r$sub_left_make:$sub_left_update_2 ../left-sub --accept postpone
+i=$(commit $i "Merge left sub-branch to left")
+cd ../..
+
+say "Merging EVERYTHING to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/left --accept postpone
+svn resolved bang
+i=$(commit $i "Merge left to trunk 2")
+# this merge, svn happily updates the mergeinfo, but there is actually
+# nothing to merge. git-svn will not make a meaningless merge commit.
+svn merge ../branches/right --accept postpone
+i=$(commit $i "non-merge right to trunk 2")
+cd ..
+
+say "Branching b1 from trunk"
+svn update
+svn cp trunk branches/b1
+i=$(commit $i "make b1 branch from trunk")
+
+say "Branching b2 from trunk"
+svn update
+svn cp trunk branches/b2
+i=$(commit $i "make b2 branch from trunk")
+
+say "Make a commit to b2"
+svn update
+cd branches/b2
+echo "b2" > b2file
+svn add b2file
+i=$(commit $i "b2 update 1")
+cd ../..
+
+say "Make a commit to b1"
+svn update
+cd branches/b1
+echo "b1" > b1file
+svn add b1file
+i=$(commit $i "b1 update 1")
+cd ../..
+
+say "Merge b1 to trunk"
+svn update
+cd trunk
+svn merge ../branches/b1/ --accept postpone
+i=$(commit $i "Merge b1 to trunk")
+cd ..
+
+say "Make a commit to trunk before merging trunk to b2"
+svn update
+cd trunk
+echo "trunk" > trunkfile
+svn add trunkfile
+i=$(commit $i "trunk commit before merging trunk to b2")
+cd ..
+
+say "Merge trunk to b2"
+svn update
+cd branches/b2
+svn merge ../../trunk/ --accept postpone
+i=$(commit $i "Merge trunk to b2")
+cd ../..
+
+say "Merge b2 to trunk"
+svn update
+cd trunk
+svn merge ../branches/b2/ --accept postpone
+svn resolved b1file
+svn resolved trunkfile
+i=$(commit $i "Merge b2 to trunk")
+cd ..
+
+say "Creating f1 from trunk with a new file"
+svn update
+svn cp trunk branches/f1
+cd branches/f1
+echo "f1" > f1file
+svn add f1file
+cd ../..
+i=$(commit $i "make f1 branch from trunk with a new file")
+
+say "Creating f2 from trunk with a new file"
+svn update
+svn cp trunk branches/f2
+cd branches/f2
+echo "f2" > f2file
+svn add f2file
+cd ../..
+i=$(commit $i "make f2 branch from trunk with a new file")
+
+say "Merge f1 and f2 to trunk in one go"
+svn update
+cd trunk
+svn merge ../branches/f1/ --accept postpone
+svn merge ../branches/f2/ --accept postpone
+i=$(commit $i "Merge f1 and f2 to trunk")
+cd ..
+
+say "Adding subdirectory to LEFT"
+svn update
+cd branches/left
+mkdir subdir
+echo "Yeehaw" > subdir/cowboy
+svn add subdir
+i=$(commit $i "add subdirectory to left branch")
+cd ../../
+
+say "Merging LEFT to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/left --accept postpone
+i=$(commit $i "merge left to trunk")
+cd ..
+
+say "Make PARTIAL branch"
+svn update
+svn cp trunk/subdir branches/partial
+i=$(commit $i "make partial branch")
+
+say "Make a commit to PARTIAL"
+svn update
+cd branches/partial
+echo "racecar" > palindromes
+svn add palindromes
+i=$(commit $i "partial update")
+cd ../../
+
+say "Merge PARTIAL to TRUNK"
+svn update
+cd trunk/subdir
+svn merge ../../branches/partial --accept postpone
+i=$(commit $i "merge partial to trunk")
+cd ../../
+
+say "Tagging trunk"
+svn update
+svn cp trunk tags/v1.0
+i=$(commit $i "tagging v1.0")
+
+say "Branching BUGFIX from v1.0"
+svn update
+svn cp tags/v1.0 branches/bugfix
+i=$(commit $i "make bugfix branch from tag")
+
+say "Make a commit to BUGFIX"
+svn update
+cd branches/bugfix/
+echo "kayak" >> subdir/palindromes
+i=$(commit $i "commit to bugfix")
+cd ../../
+
+say "Merge BUGFIX to TRUNK"
+svn update
+cd trunk
+svn merge ../branches/bugfix/ --accept postpone
+i=$(commit $i "Merge BUGFIX to TRUNK")
+cd ..
+
+cd ..
+svnadmin dump foo.svn > svn-mergeinfo.dump
+
+rm -rf foo foo.svn
diff --git a/t/t9151/svn-mergeinfo.dump b/t/t9151/svn-mergeinfo.dump
new file mode 100644
index 0000000..47cafcf
--- /dev/null
+++ b/t/t9151/svn-mergeinfo.dump
@@ -0,0 +1,2388 @@
+SVN-fs-dump-format-version: 2
+
+UUID: d6191530-2693-4a8e-98e7-b194d4c3edd8
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2010-01-19T04:14:02.832406Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 134
+Content-length: 134
+
+K 7
+svn:log
+V 36
+(r1) Setup trunk, branches, and tags
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:03.055172Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: tags
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 111
+Content-length: 111
+
+K 7
+svn:log
+V 13
+(r2) ancestor
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:04.064506Z
+PROPS-END
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 2401
+Text-content-md5: bfd8ff778d1492dc6758567373176a89
+Text-content-sha1: 103205ce331f7d64086dba497574734f78439590
+Content-length: 2411
+
+PROPS-END
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 3
+Prop-content-length: 119
+Content-length: 119
+
+K 7
+svn:log
+V 21
+(r3) make left branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:06.040389Z
+PROPS-END
+
+Node-path: branches/left
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/Makefile
+Text-copy-source-md5: bfd8ff778d1492dc6758567373176a89
+Text-copy-source-sha1: 103205ce331f7d64086dba497574734f78439590
+
+
+Revision-number: 4
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 22
+(r4) make right branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:08.040905Z
+PROPS-END
+
+Node-path: branches/right
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/right/Makefile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 2
+Node-copyfrom-path: trunk/Makefile
+Text-copy-source-md5: bfd8ff778d1492dc6758567373176a89
+Text-copy-source-sha1: 103205ce331f7d64086dba497574734f78439590
+
+
+Revision-number: 5
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 18
+(r5) left update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:09.049169Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2465
+Text-content-md5: 16e38d9753b061731650561ce01b1195
+Text-content-sha1: 36da4b84ea9b64218ab48171dfc5c48ae025f38b
+Content-length: 2465
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 6
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 19
+(r6) right update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:10.049350Z
+PROPS-END
+
+Node-path: branches/right/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2521
+Text-content-md5: 0668418a621333f4aa8b6632cd63e2a0
+Text-content-sha1: 4f29afd038e52f45acb5ef8c41acfc70062a741a
+Content-length: 2521
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 7
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 18
+(r7) left update 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:11.049209Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2529
+Text-content-md5: f6b197cc3f2e89a83e545d4bb003de73
+Text-content-sha1: 2f656677cfec0bceec85e53036ffb63e25126f8e
+Content-length: 2529
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 8
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 18
+(r8) left update 3
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:12.049234Z
+PROPS-END
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2593
+Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Text-content-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10
+Content-length: 2593
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 9
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 25
+(r9) make left sub-branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:14.040894Z
+PROPS-END
+
+Node-path: branches/left-sub
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 3
+Node-copyfrom-path: branches/left
+
+
+Node-path: branches/left-sub/Makefile
+Node-kind: file
+Node-action: delete
+
+Node-path: branches/left-sub/Makefile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 8
+Node-copyfrom-path: branches/left/Makefile
+Text-copy-source-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Text-copy-source-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10
+
+
+
+
+Revision-number: 10
+Prop-content-length: 128
+Content-length: 128
+
+K 7
+svn:log
+V 30
+(r10) left sub-branch update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:15.049935Z
+PROPS-END
+
+Node-path: branches/left-sub/README
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 7
+Text-content-md5: fdbcfb6be9afe1121862143f226b51cf
+Text-content-sha1: 1d1f5ea4ceb584337ffe59b8980d92e3b78dfef4
+Content-length: 17
+
+PROPS-END
+crunch
+
+
+Revision-number: 11
+Prop-content-length: 125
+Content-length: 125
+
+K 7
+svn:log
+V 27
+(r11) Merge left to trunk 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:18.056594Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 54
+Content-length: 54
+
+K 13
+svn:mergeinfo
+V 19
+/branches/left:2-10
+PROPS-END
+
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2593
+Text-content-md5: 5ccff689fb290e00b85fe18ee50c54ba
+Text-content-sha1: a13de8e23f1483efca3e57b2b64b0ae6f740ce10
+Content-length: 2593
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Revision-number: 12
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 19
+(r12) left update 4
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:19.049620Z
+PROPS-END
+
+Node-path: branches/left/zlonk
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 7
+Text-content-md5: 8b9d8c7c2aaa6167e7d3407a773bbbba
+Text-content-sha1: 9716527ebd70a75c27625cacbeb2d897c6e86178
+Content-length: 17
+
+PROPS-END
+touche
+
+
+Revision-number: 13
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 20
+(r13) right update 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:20.049659Z
+PROPS-END
+
+Node-path: branches/right/bang
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 8
+Text-content-md5: 34c28f1d2dc6a9adeccc4265bf7516cb
+Text-content-sha1: 0bc5bb345c0e71d28f784f12e0bd2d384c283062
+Content-length: 18
+
+PROPS-END
+thwacke
+
+
+Revision-number: 14
+Prop-content-length: 140
+Content-length: 140
+
+K 7
+svn:log
+V 42
+(r14) Cherry-pick right 2 commits to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:23.041991Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 75
+Content-length: 75
+
+K 13
+svn:mergeinfo
+V 40
+/branches/left:2-10
+/branches/right:6-13
+PROPS-END
+
+
+Node-path: trunk/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2713
+Text-content-md5: 0afbe34f244cd662b1f97d708c687f90
+Text-content-sha1: 46d9377d783e67a9b581da110352e799517c8a14
+Content-length: 2713
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Node-path: trunk/bang
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 13
+Node-copyfrom-path: branches/right/bang
+Text-copy-source-md5: 34c28f1d2dc6a9adeccc4265bf7516cb
+Text-copy-source-sha1: 0bc5bb345c0e71d28f784f12e0bd2d384c283062
+
+
+Revision-number: 15
+Prop-content-length: 126
+Content-length: 126
+
+K 7
+svn:log
+V 28
+(r15) Merge right to trunk 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:26.054456Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 75
+Content-length: 75
+
+K 13
+svn:mergeinfo
+V 40
+/branches/left:2-10
+/branches/right:2-14
+PROPS-END
+
+
+Revision-number: 16
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 20
+(r16) right update 3
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:27.049955Z
+PROPS-END
+
+Node-path: branches/right/urkkk
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: 5889c8392e16251b0c80927607a03036
+Text-content-sha1: 3934264d277a0cf886b6b1c7f2b9e56da2525302
+Content-length: 16
+
+PROPS-END
+whamm
+
+
+Revision-number: 17
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 20
+(r17) trunk update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:28.049615Z
+PROPS-END
+
+Node-path: trunk/vronk
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: b2f80fa02a7f1364b9c29d3da44bf9f9
+Text-content-sha1: e994d980c0f2d7a3f76138bf96d57f36f9633828
+Content-length: 14
+
+PROPS-END
+pow
+
+
+Revision-number: 18
+Prop-content-length: 134
+Content-length: 134
+
+K 7
+svn:log
+V 36
+(r18) Merge right to left sub-branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:31.061460Z
+PROPS-END
+
+Node-path: branches/left-sub
+Node-kind: dir
+Node-action: change
+Prop-content-length: 55
+Content-length: 55
+
+K 13
+svn:mergeinfo
+V 20
+/branches/right:2-17
+PROPS-END
+
+
+Node-path: branches/left-sub/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2713
+Text-content-md5: 0afbe34f244cd662b1f97d708c687f90
+Text-content-sha1: 46d9377d783e67a9b581da110352e799517c8a14
+Content-length: 2713
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Node-path: branches/left-sub/bang
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 17
+Node-copyfrom-path: branches/right/bang
+Text-copy-source-md5: 34c28f1d2dc6a9adeccc4265bf7516cb
+Text-copy-source-sha1: 0bc5bb345c0e71d28f784f12e0bd2d384c283062
+
+
+Node-path: branches/left-sub/urkkk
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 17
+Node-copyfrom-path: branches/right/urkkk
+Text-copy-source-md5: 5889c8392e16251b0c80927607a03036
+Text-copy-source-sha1: 3934264d277a0cf886b6b1c7f2b9e56da2525302
+
+
+Revision-number: 19
+Prop-content-length: 128
+Content-length: 128
+
+K 7
+svn:log
+V 30
+(r19) left sub-branch update 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:32.049244Z
+PROPS-END
+
+Node-path: branches/left-sub/wham_eth
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: 757bcd5818572ef3f9580052617c1c8b
+Text-content-sha1: b165019b005c199237ba822c4404e771e93b654a
+Content-length: 16
+
+PROPS-END
+zowie
+
+
+Revision-number: 20
+Prop-content-length: 117
+Content-length: 117
+
+K 7
+svn:log
+V 19
+(r20) left update 5
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:33.049332Z
+PROPS-END
+
+Node-path: branches/left/glurpp
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 8
+Text-content-md5: 14a169f628e0bb59df9c2160649d0a30
+Text-content-sha1: ef7d929e52177767ecfcd28941f6b7f04b4131e3
+Content-length: 18
+
+PROPS-END
+eee_yow
+
+
+Revision-number: 21
+Prop-content-length: 146
+Content-length: 146
+
+K 7
+svn:log
+V 48
+(r21) Cherry-pick left sub-branch commit to left
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:36.041839Z
+PROPS-END
+
+Node-path: branches/left
+Node-kind: dir
+Node-action: change
+Prop-content-length: 56
+Content-length: 56
+
+K 13
+svn:mergeinfo
+V 21
+/branches/left-sub:19
+PROPS-END
+
+
+Node-path: branches/left/wham_eth
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 19
+Node-copyfrom-path: branches/left-sub/wham_eth
+Text-copy-source-md5: 757bcd5818572ef3f9580052617c1c8b
+Text-copy-source-sha1: b165019b005c199237ba822c4404e771e93b654a
+
+
+Revision-number: 22
+Prop-content-length: 133
+Content-length: 133
+
+K 7
+svn:log
+V 35
+(r22) Merge left sub-branch to left
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:39.045014Z
+PROPS-END
+
+Node-path: branches/left
+Node-kind: dir
+Node-action: change
+Prop-content-length: 79
+Content-length: 79
+
+K 13
+svn:mergeinfo
+V 44
+/branches/left-sub:4-19
+/branches/right:2-17
+PROPS-END
+
+
+Node-path: branches/left/Makefile
+Node-kind: file
+Node-action: change
+Text-content-length: 2713
+Text-content-md5: 0afbe34f244cd662b1f97d708c687f90
+Text-content-sha1: 46d9377d783e67a9b581da110352e799517c8a14
+Content-length: 2713
+
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DNSEC if you want git to care about sub-second file mtimes and ctimes.
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+CFLAGS=-g -O3 -Wall
+
+CC=gcc
+
+
+PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
+ cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
+ check-files ls-tree merge-base merge-cache
+
+all: $(PROG)
+
+install: $(PROG)
+ install $(PROG) $(HOME)/bin/
+
+LIBS= -lssl -lz
+
+init-db: init-db.o
+
+update-cache: update-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o update-cache update-cache.o read-cache.o $(LIBS)
+
+show-diff: show-diff.o read-cache.o
+ $(CC) $(CFLAGS) -o show-diff show-diff.o read-cache.o $(LIBS)
+
+write-tree: write-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o write-tree write-tree.o read-cache.o $(LIBS)
+
+read-tree: read-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o read-tree read-tree.o read-cache.o $(LIBS)
+
+commit-tree: commit-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o commit-tree commit-tree.o read-cache.o $(LIBS)
+
+cat-file: cat-file.o read-cache.o
+ $(CC) $(CFLAGS) -o cat-file cat-file.o read-cache.o $(LIBS)
+
+fsck-cache: fsck-cache.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o fsck-cache fsck-cache.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+checkout-cache: checkout-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o checkout-cache checkout-cache.o read-cache.o $(LIBS)
+
+diff-tree: diff-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o diff-tree diff-tree.o read-cache.o $(LIBS)
+
+rev-tree: rev-tree.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o rev-tree rev-tree.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+show-files: show-files.o read-cache.o
+ $(CC) $(CFLAGS) -o show-files show-files.o read-cache.o $(LIBS)
+
+check-files: check-files.o read-cache.o
+ $(CC) $(CFLAGS) -o check-files check-files.o read-cache.o $(LIBS)
+
+ls-tree: ls-tree.o read-cache.o
+ $(CC) $(CFLAGS) -o ls-tree ls-tree.o read-cache.o $(LIBS)
+
+merge-base: merge-base.o read-cache.o object.o commit.o tree.o blob.o
+ $(CC) $(CFLAGS) -o merge-base merge-base.o read-cache.o object.o commit.o tree.o blob.o $(LIBS)
+
+merge-cache: merge-cache.o read-cache.o
+ $(CC) $(CFLAGS) -o merge-cache merge-cache.o read-cache.o $(LIBS)
+
+read-cache.o: cache.h
+show-diff.o: cache.h
+
+clean:
+ rm -f *.o $(PROG)
+
+backup: clean
+ cd .. ; tar czvf dircache.tar.gz dir-cache
+
+
+Node-path: branches/left/README
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 18
+Node-copyfrom-path: branches/left-sub/README
+Text-copy-source-md5: fdbcfb6be9afe1121862143f226b51cf
+Text-copy-source-sha1: 1d1f5ea4ceb584337ffe59b8980d92e3b78dfef4
+
+
+Node-path: branches/left/bang
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 18
+Node-copyfrom-path: branches/left-sub/bang
+Text-copy-source-md5: 34c28f1d2dc6a9adeccc4265bf7516cb
+Text-copy-source-sha1: 0bc5bb345c0e71d28f784f12e0bd2d384c283062
+
+
+Node-path: branches/left/urkkk
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 18
+Node-copyfrom-path: branches/left-sub/urkkk
+Text-copy-source-md5: 5889c8392e16251b0c80927607a03036
+Text-copy-source-sha1: 3934264d277a0cf886b6b1c7f2b9e56da2525302
+
+
+Revision-number: 23
+Prop-content-length: 125
+Content-length: 125
+
+K 7
+svn:log
+V 27
+(r23) Merge left to trunk 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:42.052798Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 99
+Content-length: 99
+
+K 13
+svn:mergeinfo
+V 64
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-17
+PROPS-END
+
+
+Node-path: trunk/README
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/README
+Text-copy-source-md5: fdbcfb6be9afe1121862143f226b51cf
+Text-copy-source-sha1: 1d1f5ea4ceb584337ffe59b8980d92e3b78dfef4
+
+
+Node-path: trunk/glurpp
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/glurpp
+Text-copy-source-md5: 14a169f628e0bb59df9c2160649d0a30
+Text-copy-source-sha1: ef7d929e52177767ecfcd28941f6b7f04b4131e3
+
+
+Node-path: trunk/urkkk
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/urkkk
+Text-copy-source-md5: 5889c8392e16251b0c80927607a03036
+Text-copy-source-sha1: 3934264d277a0cf886b6b1c7f2b9e56da2525302
+
+
+Node-path: trunk/wham_eth
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/wham_eth
+Text-copy-source-md5: 757bcd5818572ef3f9580052617c1c8b
+Text-copy-source-sha1: b165019b005c199237ba822c4404e771e93b654a
+
+
+Node-path: trunk/zlonk
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 22
+Node-copyfrom-path: branches/left/zlonk
+Text-copy-source-md5: 8b9d8c7c2aaa6167e7d3407a773bbbba
+Text-copy-source-sha1: 9716527ebd70a75c27625cacbeb2d897c6e86178
+
+
+Revision-number: 24
+Prop-content-length: 130
+Content-length: 130
+
+K 7
+svn:log
+V 32
+(r24) non-merge right to trunk 2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-01-19T04:14:44.038434Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 99
+Content-length: 99
+
+K 13
+svn:mergeinfo
+V 64
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Revision-number: 25
+Prop-content-length: 129
+Content-length: 129
+
+K 7
+svn:log
+V 31
+(r25) make b1 branch from trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:18:56.084589Z
+PROPS-END
+
+Node-path: branches/b1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 24
+Node-copyfrom-path: trunk
+
+
+Revision-number: 26
+Prop-content-length: 129
+Content-length: 129
+
+K 7
+svn:log
+V 31
+(r26) make b2 branch from trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:18:59.076940Z
+PROPS-END
+
+Node-path: branches/b2
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 25
+Node-copyfrom-path: trunk
+
+
+Revision-number: 27
+Prop-content-length: 115
+Content-length: 115
+
+K 7
+svn:log
+V 17
+(r27) b2 update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:01.095762Z
+PROPS-END
+
+Node-path: branches/b2/b2file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 5edbdd57cba621eb3c6e601bf563b4dc
+Text-content-sha1: 9d4b38049776bd0a2074d67cad23f8eaed35a3b3
+Content-length: 13
+
+PROPS-END
+b2
+
+
+Revision-number: 28
+Prop-content-length: 115
+Content-length: 115
+
+K 7
+svn:log
+V 17
+(r28) b1 update 1
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:03.097465Z
+PROPS-END
+
+Node-path: branches/b1/b1file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 08778dfd9ac4f603231896aba7aad523
+Text-content-sha1: b551771aa4ad5b14123fc3bd98d89db2bc0edd4f
+Content-length: 13
+
+PROPS-END
+b1
+
+
+Revision-number: 29
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 23
+(r29) Merge b1 to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:06.073175Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 118
+Content-length: 118
+
+K 13
+svn:mergeinfo
+V 83
+/branches/b1:25-28
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/b1file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 28
+Node-copyfrom-path: branches/b1/b1file
+Text-copy-source-md5: 08778dfd9ac4f603231896aba7aad523
+Text-copy-source-sha1: b551771aa4ad5b14123fc3bd98d89db2bc0edd4f
+
+
+Revision-number: 30
+Prop-content-length: 143
+Content-length: 143
+
+K 7
+svn:log
+V 45
+(r30) trunk commit before merging trunk to b2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:08.096353Z
+PROPS-END
+
+Node-path: trunk/trunkfile
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 6
+Text-content-md5: edf45fe5c98c5367733b39bbb2bb20d9
+Text-content-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239
+Content-length: 16
+
+PROPS-END
+trunk
+
+
+Revision-number: 31
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 23
+(r31) Merge trunk to b2
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:11.081541Z
+PROPS-END
+
+Node-path: branches/b2
+Node-kind: dir
+Node-action: change
+Prop-content-length: 131
+Content-length: 131
+
+K 13
+svn:mergeinfo
+V 96
+/branches/b1:25-28
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+/trunk:26-30
+PROPS-END
+
+
+Node-path: branches/b2/b1file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 30
+Node-copyfrom-path: trunk/b1file
+Text-copy-source-md5: 08778dfd9ac4f603231896aba7aad523
+Text-copy-source-sha1: b551771aa4ad5b14123fc3bd98d89db2bc0edd4f
+
+
+Node-path: branches/b2/trunkfile
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 30
+Node-copyfrom-path: trunk/trunkfile
+Text-copy-source-md5: edf45fe5c98c5367733b39bbb2bb20d9
+Text-copy-source-sha1: 7361d1685e5c86dfc523620cfaf598f196f86239
+
+
+Revision-number: 32
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 23
+(r32) Merge b2 to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:14.117939Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 138
+Content-length: 138
+
+K 13
+svn:mergeinfo
+V 102
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/b2file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 31
+Node-copyfrom-path: branches/b2/b2file
+Text-copy-source-md5: 5edbdd57cba621eb3c6e601bf563b4dc
+Text-copy-source-sha1: 9d4b38049776bd0a2074d67cad23f8eaed35a3b3
+
+
+Revision-number: 33
+Prop-content-length: 145
+Content-length: 145
+
+K 7
+svn:log
+V 47
+(r33) make f1 branch from trunk with a new file
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:17.105832Z
+PROPS-END
+
+Node-path: branches/f1
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 32
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/f1/f1file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 2b1abc6b6c5c0018851f9f8e6475563b
+Text-content-sha1: aece6dfba588900e00d95601d22b4408d49580af
+Content-length: 13
+
+PROPS-END
+f1
+
+
+Revision-number: 34
+Prop-content-length: 145
+Content-length: 145
+
+K 7
+svn:log
+V 47
+(r34) make f2 branch from trunk with a new file
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:20.110057Z
+PROPS-END
+
+Node-path: branches/f2
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 33
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/f2/f2file
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 3
+Text-content-md5: 575c5638d60271457e54ab7d07309502
+Text-content-sha1: 1c49a440c352f3473efa9512255033b94dc7def0
+Content-length: 13
+
+PROPS-END
+f2
+
+
+Revision-number: 35
+Prop-content-length: 128
+Content-length: 128
+
+K 7
+svn:log
+V 30
+(r35) Merge f1 and f2 to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:24.081490Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 173
+Content-length: 173
+
+K 13
+svn:mergeinfo
+V 137
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/f1:33-34
+/branches/f2:34
+/branches/left:2-22
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/f1file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 34
+Node-copyfrom-path: branches/f1/f1file
+Text-copy-source-md5: 2b1abc6b6c5c0018851f9f8e6475563b
+Text-copy-source-sha1: aece6dfba588900e00d95601d22b4408d49580af
+
+
+Node-path: trunk/f2file
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 34
+Node-copyfrom-path: branches/f2/f2file
+Text-copy-source-md5: 575c5638d60271457e54ab7d07309502
+Text-copy-source-sha1: 1c49a440c352f3473efa9512255033b94dc7def0
+
+
+Revision-number: 36
+Prop-content-length: 135
+Content-length: 135
+
+K 7
+svn:log
+V 37
+(r36) add subdirectory to left branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:26.113516Z
+PROPS-END
+
+Node-path: branches/left/subdir
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: branches/left/subdir/cowboy
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 7
+Text-content-md5: f1d6530278ad409e68cc675476ad995f
+Text-content-sha1: 732d9e3e5c391ffd767a98b45ddcc848de778cea
+Content-length: 17
+
+PROPS-END
+Yeehaw
+
+
+Revision-number: 37
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 25
+(r37) merge left to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:29.073699Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 173
+Content-length: 173
+
+K 13
+svn:mergeinfo
+V 137
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/f1:33-34
+/branches/f2:34
+/branches/left:2-36
+/branches/left-sub:4-19
+/branches/right:2-22
+PROPS-END
+
+
+Node-path: trunk/subdir
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 36
+Node-copyfrom-path: branches/left/subdir
+
+
+Revision-number: 38
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 25
+(r38) make partial branch
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:32.072243Z
+PROPS-END
+
+Node-path: branches/partial
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 37
+Node-copyfrom-path: trunk/subdir
+
+
+Revision-number: 39
+Prop-content-length: 118
+Content-length: 118
+
+K 7
+svn:log
+V 20
+(r39) partial update
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:34.097961Z
+PROPS-END
+
+Node-path: branches/partial/palindromes
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 8
+Text-content-md5: 5d1c2024fb5efc4eef812856df1b080c
+Text-content-sha1: 5f8509ddd14c91a52864dd1447344e706f9bbc69
+Content-length: 18
+
+PROPS-END
+racecar
+
+
+Revision-number: 40
+Prop-content-length: 126
+Content-length: 126
+
+K 7
+svn:log
+V 28
+(r40) merge partial to trunk
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:37.080211Z
+PROPS-END
+
+Node-path: trunk/subdir
+Node-kind: dir
+Node-action: change
+Prop-content-length: 246
+Content-length: 246
+
+K 13
+svn:mergeinfo
+V 210
+/branches/b1/subdir:25-28
+/branches/b2/subdir:26-31
+/branches/f1/subdir:33-34
+/branches/f2/subdir:34
+/branches/left/subdir:2-36
+/branches/left-sub/subdir:4-19
+/branches/partial:38-39
+/branches/right/subdir:2-22
+PROPS-END
+
+
+Node-path: trunk/subdir/palindromes
+Node-kind: file
+Node-action: add
+Node-copyfrom-rev: 39
+Node-copyfrom-path: branches/partial/palindromes
+Text-copy-source-md5: 5d1c2024fb5efc4eef812856df1b080c
+Text-copy-source-sha1: 5f8509ddd14c91a52864dd1447344e706f9bbc69
+
+
+Revision-number: 41
+Prop-content-length: 116
+Content-length: 116
+
+K 7
+svn:log
+V 18
+(r41) tagging v1.0
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:40.083460Z
+PROPS-END
+
+Node-path: tags/v1.0
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 40
+Node-copyfrom-path: trunk
+
+
+Revision-number: 42
+Prop-content-length: 131
+Content-length: 131
+
+K 7
+svn:log
+V 33
+(r42) make bugfix branch from tag
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:43.118075Z
+PROPS-END
+
+Node-path: branches/bugfix
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 41
+Node-copyfrom-path: tags/v1.0
+
+
+Revision-number: 43
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 22
+(r43) commit to bugfix
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:45.079536Z
+PROPS-END
+
+Node-path: branches/bugfix/subdir/palindromes
+Node-kind: file
+Node-action: change
+Text-content-length: 14
+Text-content-md5: 3b12d98578a3f4320ba97e66da54fe5f
+Text-content-sha1: 672931c9e8ac2c408209efab2f015638b6d64042
+Content-length: 14
+
+racecar
+kayak
+
+
+Revision-number: 44
+Prop-content-length: 125
+Content-length: 125
+
+K 7
+svn:log
+V 27
+(r44) Merge BUGFIX to TRUNK
+K 10
+svn:author
+V 3
+adm
+K 8
+svn:date
+V 27
+2010-02-22T06:19:48.078914Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: change
+Prop-content-length: 210
+Content-length: 210
+
+K 13
+svn:mergeinfo
+V 174
+/branches/b1:25-28
+/branches/b2:26-31
+/branches/bugfix:42-43
+/branches/f1:33-34
+/branches/f2:34
+/branches/left:2-36
+/branches/left-sub:4-19
+/branches/right:2-22
+/tags/v1.0:41
+PROPS-END
+
+
+Node-path: trunk/subdir
+Node-kind: dir
+Node-action: change
+Prop-content-length: 297
+Content-length: 297
+
+K 13
+svn:mergeinfo
+V 261
+/branches/b1/subdir:25-28
+/branches/b2/subdir:26-31
+/branches/bugfix/subdir:42-43
+/branches/f1/subdir:33-34
+/branches/f2/subdir:34
+/branches/left/subdir:2-36
+/branches/left-sub/subdir:4-19
+/branches/partial:38-39
+/branches/right/subdir:2-22
+/tags/v1.0/subdir:41
+PROPS-END
+
+
+Node-path: trunk/subdir/palindromes
+Node-kind: file
+Node-action: change
+Text-content-length: 14
+Text-content-md5: 3b12d98578a3f4320ba97e66da54fe5f
+Text-content-sha1: 672931c9e8ac2c408209efab2f015638b6d64042
+Content-length: 14
+
+racecar
+kayak
+
+
diff --git a/t/t9152-svn-empty-dirs-after-gc.sh b/t/t9152-svn-empty-dirs-after-gc.sh
new file mode 100755
index 0000000..301e779
--- /dev/null
+++ b/t/t9152-svn-empty-dirs-after-gc.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Robert Zeh
+
+test_description='git svn creates empty directories, calls git gc, makes sure they are still empty'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+ for i in a b c d d/e d/e/f "weird file name"
+ do
+ svn_cmd mkdir -m "mkdir $i" "$svnrepo"/"$i"
+ done
+'
+
+test_expect_success 'clone' 'git svn clone "$svnrepo" cloned'
+
+test_expect_success 'git svn gc runs' '
+ (
+ cd cloned &&
+ git svn gc
+ )
+'
+
+test_expect_success 'git svn mkdirs recreates empty directories after git svn gc' '
+ (
+ cd cloned &&
+ rm -r * &&
+ git svn mkdirs &&
+ for i in a b c d d/e d/e/f "weird file name"
+ do
+ if ! test -d "$i"
+ then
+ echo >&2 "$i does not exist"
+ exit 1
+ fi
+ done
+ )
+'
+
+test_done
diff --git a/t/t9153-git-svn-rewrite-uuid.sh b/t/t9153-git-svn-rewrite-uuid.sh
new file mode 100755
index 0000000..88a2cfa
--- /dev/null
+++ b/t/t9153-git-svn-rewrite-uuid.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Jay Soffian
+#
+
+test_description='git svn --rewrite-uuid test'
+
+. ./lib-git-svn.sh
+
+uuid=6cc8ada4-5932-4b4a-8242-3534ed8a3232
+
+test_expect_success 'load svn repo' "
+ svnadmin load -q '$rawsvnrepo' < '$TEST_DIRECTORY/t9153/svn.dump' &&
+ git svn init --minimize-url --rewrite-uuid='$uuid' '$svnrepo' &&
+ git svn fetch
+ "
+
+test_expect_success 'verify uuid' "
+ git cat-file commit refs/remotes/git-svn~0 | \
+ grep '^${git_svn_id}: .*@2 $uuid$' &&
+ git cat-file commit refs/remotes/git-svn~1 | \
+ grep '^${git_svn_id}: .*@1 $uuid$'
+ "
+
+test_done
diff --git a/t/t9153/svn.dump b/t/t9153/svn.dump
new file mode 100644
index 0000000..0ddfe70
--- /dev/null
+++ b/t/t9153/svn.dump
@@ -0,0 +1,75 @@
+SVN-fs-dump-format-version: 2
+
+UUID: b4885626-c94f-4a6c-b179-00c030fc68e8
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2010-01-23T06:41:03.908576Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 109
+Content-length: 109
+
+K 7
+svn:log
+V 11
+initial foo
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T06:41:48.353776Z
+PROPS-END
+
+Node-path: foo
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: d3b07384d113edec49eaa6238ad5ff00
+Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
+Content-length: 14
+
+PROPS-END
+foo
+
+
+Revision-number: 2
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 12
+now with bar
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T06:42:14.214640Z
+PROPS-END
+
+Node-path: foo
+Node-kind: file
+Node-action: change
+Text-content-length: 8
+Text-content-md5: f47c75614087a8dd938ba4acff252494
+Text-content-sha1: 4e48e2c9a3d2ca8a708cb0cc545700544efb5021
+Content-length: 8
+
+foo
+bar
+
+
diff --git a/t/t9154-git-svn-fancy-glob.sh b/t/t9154-git-svn-fancy-glob.sh
new file mode 100755
index 0000000..a6a56a6
--- /dev/null
+++ b/t/t9154-git-svn-fancy-glob.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Jay Soffian
+#
+
+test_description='git svn fancy glob test'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'load svn repo' "
+ svnadmin load -q '$rawsvnrepo' < '$TEST_DIRECTORY/t9154/svn.dump' &&
+ git svn init --minimize-url -T trunk '$svnrepo' &&
+ git svn fetch
+ "
+
+test_expect_success 'add red branch' "
+ git config svn-remote.svn.branches 'branches/{red}:refs/remotes/*' &&
+ git svn fetch &&
+ git rev-parse refs/remotes/red &&
+ test_must_fail git rev-parse refs/remotes/green &&
+ test_must_fail git rev-parse refs/remotes/blue
+ "
+
+test_expect_success 'add green branch' "
+ GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev &&
+ git config svn-remote.svn.branches 'branches/{red,green}:refs/remotes/*' &&
+ git svn fetch &&
+ git rev-parse refs/remotes/red &&
+ git rev-parse refs/remotes/green &&
+ test_must_fail git rev-parse refs/remotes/blue
+ "
+
+test_expect_success 'add all branches' "
+ GIT_CONFIG=.git/svn/.metadata git config --unset svn-remote.svn.branches-maxRev &&
+ git config svn-remote.svn.branches 'branches/*:refs/remotes/*' &&
+ git svn fetch &&
+ git rev-parse refs/remotes/red &&
+ git rev-parse refs/remotes/green &&
+ git rev-parse refs/remotes/blue
+ "
+
+test_done
diff --git a/t/t9154/svn.dump b/t/t9154/svn.dump
new file mode 100644
index 0000000..3dfabb6
--- /dev/null
+++ b/t/t9154/svn.dump
@@ -0,0 +1,222 @@
+SVN-fs-dump-format-version: 2
+
+UUID: a18093a0-5f0b-44e0-8d88-8911ac7078db
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2010-01-23T07:40:25.660053Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 104
+Content-length: 104
+
+K 7
+svn:log
+V 7
+initial
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:41:33.636365Z
+PROPS-END
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk/foo
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 4
+Text-content-md5: d3b07384d113edec49eaa6238ad5ff00
+Text-content-sha1: f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
+Content-length: 14
+
+PROPS-END
+foo
+
+
+Revision-number: 2
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 12
+add branches
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:42:37.290694Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: branches/blue
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/green
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Node-path: branches/red
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+
+
+Revision-number: 3
+Prop-content-length: 108
+Content-length: 108
+
+K 7
+svn:log
+V 10
+red change
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:43:02.208918Z
+PROPS-END
+
+Node-path: branches/red/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 8
+Text-content-md5: 64c3c8cf7d0233ab7627623a68888bd1
+Text-content-sha1: 95a0492027876adfd3891ec71ee37b79ee44d640
+Content-length: 8
+
+foo
+red
+
+
+Revision-number: 4
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 12
+green change
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:43:15.746586Z
+PROPS-END
+
+Node-path: branches/green/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 10
+Text-content-md5: 0209b6450891abc033d5eaaa9d3a8023
+Text-content-sha1: 87fc3bef9faeec48c0cd61dfc9851db377fdccf7
+Content-length: 10
+
+foo
+green
+
+
+Revision-number: 5
+Prop-content-length: 109
+Content-length: 109
+
+K 7
+svn:log
+V 11
+blue change
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:43:29.364811Z
+PROPS-END
+
+Node-path: branches/blue/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 9
+Text-content-md5: 9fbe4c13d0bae86386ae5209b2e6b275
+Text-content-sha1: cc4575083459a16f9aaef796c4a2456d64691ba0
+Content-length: 9
+
+foo
+blue
+
+
+Revision-number: 6
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 12
+trunk change
+K 10
+svn:author
+V 3
+jay
+K 8
+svn:date
+V 27
+2010-01-23T07:44:01.313130Z
+PROPS-END
+
+Node-path: trunk/foo
+Node-kind: file
+Node-action: change
+Text-content-length: 10
+Text-content-md5: 1c4db977d7a57c3bae582aab87948516
+Text-content-sha1: 469c08df449e702cf2a1fe746244a9ef3f837fad
+Content-length: 10
+
+foo
+trunk
+
+
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
new file mode 100755
index 0000000..ee39b36
--- /dev/null
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -0,0 +1,341 @@
+#!/bin/sh
+#
+# Copyright (c) Robin Rosenberg
+#
+test_description='Test export of commits to CVS'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping git cvsexportcommit tests, perl not available'
+ test_done
+fi
+
+cvs >/dev/null 2>&1
+if test $? -ne 1
+then
+ skip_all='skipping git cvsexportcommit tests, cvs not found'
+ test_done
+fi
+
+CVSROOT=$(pwd)/cvsroot
+CVSWORK=$(pwd)/cvswork
+GIT_DIR=$(pwd)/.git
+export CVSROOT CVSWORK GIT_DIR
+
+rm -rf "$CVSROOT" "$CVSWORK"
+mkdir "$CVSROOT" &&
+cvs init &&
+cvs -Q co -d "$CVSWORK" . &&
+echo >empty &&
+git add empty &&
+git commit -q -a -m "Initial" 2>/dev/null ||
+exit 1
+
+check_entries () {
+ # $1 == directory, $2 == expected
+ grep '^/' "$1/CVS/Entries" | sort | cut -d/ -f2,3,5 >actual
+ if test -z "$2"
+ then
+ >expected
+ else
+ printf '%s\n' "$2" | tr '|' '\012' >expected
+ fi
+ test_cmp expected actual
+}
+
+test_expect_success \
+ 'New file' \
+ 'mkdir A B C D E F &&
+ echo hello1 >A/newfile1.txt &&
+ echo hello2 >B/newfile2.txt &&
+ cp "$TEST_DIRECTORY"/test9200a.png C/newfile3.png &&
+ cp "$TEST_DIRECTORY"/test9200a.png D/newfile4.png &&
+ git add A/newfile1.txt &&
+ git add B/newfile2.txt &&
+ git add C/newfile3.png &&
+ git add D/newfile4.png &&
+ git commit -a -m "Test: New file" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries A "newfile1.txt/1.1/" &&
+ check_entries B "newfile2.txt/1.1/" &&
+ check_entries C "newfile3.png/1.1/-kb" &&
+ check_entries D "newfile4.png/1.1/-kb" &&
+ test_cmp A/newfile1.txt ../A/newfile1.txt &&
+ test_cmp B/newfile2.txt ../B/newfile2.txt &&
+ test_cmp C/newfile3.png ../C/newfile3.png &&
+ test_cmp D/newfile4.png ../D/newfile4.png
+ )'
+
+test_expect_success \
+ 'Remove two files, add two and update two' \
+ 'echo Hello1 >>A/newfile1.txt &&
+ rm -f B/newfile2.txt &&
+ rm -f C/newfile3.png &&
+ echo Hello5 >E/newfile5.txt &&
+ cp "$TEST_DIRECTORY"/test9200b.png D/newfile4.png &&
+ cp "$TEST_DIRECTORY"/test9200a.png F/newfile6.png &&
+ git add E/newfile5.txt &&
+ git add F/newfile6.png &&
+ git commit -a -m "Test: Remove, add and update" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries A "newfile1.txt/1.2/" &&
+ check_entries B "" &&
+ check_entries C "" &&
+ check_entries D "newfile4.png/1.2/-kb" &&
+ check_entries E "newfile5.txt/1.1/" &&
+ check_entries F "newfile6.png/1.1/-kb" &&
+ test_cmp A/newfile1.txt ../A/newfile1.txt &&
+ test_cmp D/newfile4.png ../D/newfile4.png &&
+ test_cmp E/newfile5.txt ../E/newfile5.txt &&
+ test_cmp F/newfile6.png ../F/newfile6.png
+ )'
+
+# Should fail (but only on the git cvsexportcommit stage)
+test_expect_success \
+ 'Fail to change binary more than one generation old' \
+ 'cat F/newfile6.png >>D/newfile4.png &&
+ git commit -a -m "generatiion 1" &&
+ cat F/newfile6.png >>D/newfile4.png &&
+ git commit -a -m "generation 2" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ test_must_fail git cvsexportcommit -c $id
+ )'
+
+#test_expect_success \
+# 'Fail to remove binary file more than one generation old' \
+# 'git reset --hard HEAD^ &&
+# cat F/newfile6.png >>D/newfile4.png &&
+# git commit -a -m "generation 2 (again)" &&
+# rm -f D/newfile4.png &&
+# git commit -a -m "generation 3" &&
+# id=$(git rev-list --max-count=1 HEAD) &&
+# (cd "$CVSWORK" &&
+# test_must_fail git cvsexportcommit -c $id
+# )'
+
+# We reuse the state from two tests back here
+
+# This test is here because a patch for only binary files will
+# fail with gnu patch, so cvsexportcommit must handle that.
+test_expect_success \
+ 'Remove only binary files' \
+ 'git reset --hard HEAD^^ &&
+ rm -f D/newfile4.png &&
+ git commit -a -m "test: remove only a binary file" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries A "newfile1.txt/1.2/" &&
+ check_entries B "" &&
+ check_entries C "" &&
+ check_entries D "" &&
+ check_entries E "newfile5.txt/1.1/" &&
+ check_entries F "newfile6.png/1.1/-kb" &&
+ test_cmp A/newfile1.txt ../A/newfile1.txt &&
+ test_cmp E/newfile5.txt ../E/newfile5.txt &&
+ test_cmp F/newfile6.png ../F/newfile6.png
+ )'
+
+test_expect_success \
+ 'Remove only a text file' \
+ 'rm -f A/newfile1.txt &&
+ git commit -a -m "test: remove only a binary file" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries A "" &&
+ check_entries B "" &&
+ check_entries C "" &&
+ check_entries D "" &&
+ check_entries E "newfile5.txt/1.1/" &&
+ check_entries F "newfile6.png/1.1/-kb" &&
+ test_cmp E/newfile5.txt ../E/newfile5.txt &&
+ test_cmp F/newfile6.png ../F/newfile6.png
+ )'
+
+test_expect_success \
+ 'New file with spaces in file name' \
+ 'mkdir "G g" &&
+ echo ok then >"G g/with spaces.txt" &&
+ git add "G g/with spaces.txt" && \
+ cp "$TEST_DIRECTORY"/test9200a.png "G g/with spaces.png" && \
+ git add "G g/with spaces.png" &&
+ git commit -a -m "With spaces" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id &&
+ check_entries "G g" "with spaces.png/1.1/-kb|with spaces.txt/1.1/"
+ )'
+
+test_expect_success \
+ 'Update file with spaces in file name' \
+ 'echo Ok then >>"G g/with spaces.txt" &&
+ cat "$TEST_DIRECTORY"/test9200a.png >>"G g/with spaces.png" && \
+ git add "G g/with spaces.png" &&
+ git commit -a -m "Update with spaces" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c $id
+ check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
+ )'
+
+# Some filesystems mangle pathnames with UTF-8 characters --
+# check and skip
+if p="Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" &&
+ mkdir -p "tst/$p" &&
+ date >"tst/$p/day" &&
+ found=$(find tst -type f -print) &&
+ test "z$found" = "ztst/$p/day" &&
+ rm -fr tst
+then
+
+# This test contains UTF-8 characters
+test_expect_success \
+ 'File with non-ascii file name' \
+ 'mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
+ echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
+ git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
+ cp "$TEST_DIRECTORY"/test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+ git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+ git commit -a -m "Går det så går det" && \
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -v -c $id &&
+ check_entries \
+ "Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö" \
+ "gårdetsågårdet.png/1.1/-kb|gårdetsågårdet.txt/1.1/"
+ )'
+
+fi
+
+rm -fr tst
+
+test_expect_success \
+ 'Mismatching patch should fail' \
+ 'date >>"E/newfile5.txt" &&
+ git add "E/newfile5.txt" &&
+ git commit -a -m "Update one" &&
+ date >>"E/newfile5.txt" &&
+ git add "E/newfile5.txt" &&
+ git commit -a -m "Update two" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$CVSWORK" &&
+ test_must_fail git cvsexportcommit -c $id
+ )'
+
+if ! test "$(git config --bool core.filemode)" = false
+then
+ test_set_prereq FILEMODE
+fi
+
+test_expect_success FILEMODE \
+ 'Retain execute bit' \
+ 'mkdir G &&
+ echo executeon >G/on &&
+ chmod +x G/on &&
+ echo executeoff >G/off &&
+ git add G/on &&
+ git add G/off &&
+ git commit -a -m "Execute test" &&
+ (cd "$CVSWORK" &&
+ git cvsexportcommit -c HEAD
+ test -x G/on &&
+ ! test -x G/off
+ )'
+
+test_expect_success '-w option should work with relative GIT_DIR' '
+ mkdir W &&
+ echo foobar >W/file1.txt &&
+ echo bazzle >W/file2.txt &&
+ git add W/file1.txt &&
+ git add W/file2.txt &&
+ git commit -m "More updates" &&
+ id=$(git rev-list --max-count=1 HEAD) &&
+ (cd "$GIT_DIR" &&
+ GIT_DIR=. git cvsexportcommit -w "$CVSWORK" -c $id &&
+ check_entries "$CVSWORK/W" "file1.txt/1.1/|file2.txt/1.1/" &&
+ test_cmp "$CVSWORK/W/file1.txt" ../W/file1.txt &&
+ test_cmp "$CVSWORK/W/file2.txt" ../W/file2.txt
+ )
+'
+
+test_expect_success 'check files before directories' '
+
+ echo Notes > release-notes &&
+ git add release-notes &&
+ git commit -m "Add release notes" release-notes &&
+ id=$(git rev-parse HEAD) &&
+ git cvsexportcommit -w "$CVSWORK" -c $id &&
+
+ echo new > DS &&
+ echo new > E/DS &&
+ echo modified > release-notes &&
+ git add DS E/DS release-notes &&
+ git commit -m "Add two files with the same basename" &&
+ id=$(git rev-parse HEAD) &&
+ git cvsexportcommit -w "$CVSWORK" -c $id &&
+ check_entries "$CVSWORK/E" "DS/1.1/|newfile5.txt/1.1/" &&
+ check_entries "$CVSWORK" "DS/1.1/|release-notes/1.2/" &&
+ test_cmp "$CVSWORK/DS" DS &&
+ test_cmp "$CVSWORK/E/DS" E/DS &&
+ test_cmp "$CVSWORK/release-notes" release-notes
+
+'
+
+test_expect_success 're-commit a removed filename which remains in CVS attic' '
+
+ (cd "$CVSWORK" &&
+ echo >attic_gremlin &&
+ cvs -Q add attic_gremlin &&
+ cvs -Q ci -m "added attic_gremlin" &&
+ rm attic_gremlin &&
+ cvs -Q rm attic_gremlin &&
+ cvs -Q ci -m "removed attic_gremlin") &&
+
+ echo > attic_gremlin &&
+ git add attic_gremlin &&
+ git commit -m "Added attic_gremlin" &&
+ git cvsexportcommit -w "$CVSWORK" -c HEAD &&
+ (cd "$CVSWORK"; cvs -Q update -d) &&
+ test -f "$CVSWORK/attic_gremlin"
+'
+
+# the state of the CVS sandbox may be indeterminate for ' space'
+# after this test on some platforms / with some versions of CVS
+# consider adding new tests above this point
+test_expect_success 'commit a file with leading spaces in the name' '
+
+ echo space > " space" &&
+ git add " space" &&
+ git commit -m "Add a file with a leading space" &&
+ id=$(git rev-parse HEAD) &&
+ git cvsexportcommit -w "$CVSWORK" -c $id &&
+ check_entries "$CVSWORK" " space/1.1/|DS/1.1/|attic_gremlin/1.3/|release-notes/1.2/" &&
+ test_cmp "$CVSWORK/ space" " space"
+
+'
+
+test_expect_success 'use the same checkout for Git and CVS' '
+
+ (mkdir shared &&
+ cd shared &&
+ unset GIT_DIR &&
+ cvs co . &&
+ git init &&
+ git add " space" &&
+ git commit -m "fake initial commit" &&
+ echo Hello >> " space" &&
+ git commit -m "Another change" " space" &&
+ git cvsexportcommit -W -p -u -c HEAD &&
+ grep Hello " space" &&
+ git diff-files)
+
+'
+
+test_done
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
new file mode 100755
index 0000000..131f032
--- /dev/null
+++ b/t/t9300-fast-import.sh
@@ -0,0 +1,1585 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Shawn Pearce
+#
+
+test_description='test git fast-import utility'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+
+file2_data='file2
+second line of EOF'
+
+file3_data='EOF
+in 3rd file
+ END'
+
+file4_data=abcd
+file4_len=4
+
+file5_data='an inline file.
+ we should see it later.'
+
+file6_data='#!/bin/sh
+echo "$@"'
+
+###
+### series A
+###
+
+test_tick
+cat >input <<INPUT_END
+blob
+mark :2
+data <<EOF
+$file2_data
+EOF
+
+blob
+mark :3
+data <<END
+$file3_data
+END
+
+blob
+mark :4
+data $file4_len
+$file4_data
+commit refs/heads/master
+mark :5
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+initial
+COMMIT
+
+M 644 :2 file2
+M 644 :3 file3
+M 755 :4 file4
+
+tag series-A
+from :5
+data <<EOF
+An annotated tag without a tagger
+EOF
+
+INPUT_END
+test_expect_success \
+ 'A: create pack from stdin' \
+ 'git fast-import --export-marks=marks.out <input &&
+ git whatchanged master'
+test_expect_success \
+ 'A: verify pack' \
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+cat >expect <<EOF
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+initial
+EOF
+test_expect_success \
+ 'A: verify commit' \
+ 'git cat-file commit master | sed 1d >actual &&
+ test_cmp expect actual'
+
+cat >expect <<EOF
+100644 blob file2
+100644 blob file3
+100755 blob file4
+EOF
+test_expect_success \
+ 'A: verify tree' \
+ 'git cat-file -p master^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual'
+
+echo "$file2_data" >expect
+test_expect_success \
+ 'A: verify file2' \
+ 'git cat-file blob master:file2 >actual && test_cmp expect actual'
+
+echo "$file3_data" >expect
+test_expect_success \
+ 'A: verify file3' \
+ 'git cat-file blob master:file3 >actual && test_cmp expect actual'
+
+printf "$file4_data" >expect
+test_expect_success \
+ 'A: verify file4' \
+ 'git cat-file blob master:file4 >actual && test_cmp expect actual'
+
+cat >expect <<EOF
+object $(git rev-parse refs/heads/master)
+type commit
+tag series-A
+
+An annotated tag without a tagger
+EOF
+test_expect_success 'A: verify tag/series-A' '
+ git cat-file tag tags/series-A >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+:2 `git rev-parse --verify master:file2`
+:3 `git rev-parse --verify master:file3`
+:4 `git rev-parse --verify master:file4`
+:5 `git rev-parse --verify master^0`
+EOF
+test_expect_success \
+ 'A: verify marks output' \
+ 'test_cmp expect marks.out'
+
+test_expect_success \
+ 'A: verify marks import' \
+ 'git fast-import \
+ --import-marks=marks.out \
+ --export-marks=marks.new \
+ </dev/null &&
+ test_cmp expect marks.new'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/verify--import-marks
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+recreate from :5
+COMMIT
+
+from :5
+M 755 :2 copy-of-file2
+
+INPUT_END
+test_expect_success \
+ 'A: verify marks import does not crash' \
+ 'git fast-import --import-marks=marks.out <input &&
+ git whatchanged verify--import-marks'
+test_expect_success \
+ 'A: verify pack' \
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+cat >expect <<EOF
+:000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A copy-of-file2
+EOF
+git diff-tree -M -r master verify--import-marks >actual
+test_expect_success \
+ 'A: verify diff' \
+ 'compare_diff_raw expect actual &&
+ test `git rev-parse --verify master:file2` \
+ = `git rev-parse --verify verify--import-marks:copy-of-file2`'
+
+###
+### series B
+###
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/branch
+mark :1
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+corrupt
+COMMIT
+
+from refs/heads/master
+M 755 0000000000000000000000000000000000000001 zero1
+
+INPUT_END
+test_expect_success 'B: fail on invalid blob sha1' '
+ test_must_fail git fast-import <input
+'
+rm -f .git/objects/pack_* .git/objects/index_*
+
+cat >input <<INPUT_END
+commit .badbranchname
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+corrupt
+COMMIT
+
+from refs/heads/master
+
+INPUT_END
+test_expect_success 'B: fail on invalid branch name ".badbranchname"' '
+ test_must_fail git fast-import <input
+'
+rm -f .git/objects/pack_* .git/objects/index_*
+
+cat >input <<INPUT_END
+commit bad[branch]name
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+corrupt
+COMMIT
+
+from refs/heads/master
+
+INPUT_END
+test_expect_success 'B: fail on invalid branch name "bad[branch]name"' '
+ test_must_fail git fast-import <input
+'
+rm -f .git/objects/pack_* .git/objects/index_*
+
+cat >input <<INPUT_END
+commit TEMP_TAG
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+tag base
+COMMIT
+
+from refs/heads/master
+
+INPUT_END
+test_expect_success \
+ 'B: accept branch name "TEMP_TAG"' \
+ 'git fast-import <input &&
+ test -f .git/TEMP_TAG &&
+ test `git rev-parse master` = `git rev-parse TEMP_TAG^`'
+rm -f .git/TEMP_TAG
+
+###
+### series C
+###
+
+newf=`echo hi newf | git hash-object -w --stdin`
+oldf=`git rev-parse --verify master:file2`
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/branch
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+second
+COMMIT
+
+from refs/heads/master
+M 644 $oldf file2/oldf
+M 755 $newf file2/newf
+D file3
+
+INPUT_END
+test_expect_success \
+ 'C: incremental import create pack from stdin' \
+ 'git fast-import <input &&
+ git whatchanged branch'
+test_expect_success \
+ 'C: verify pack' \
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+test_expect_success \
+ 'C: validate reuse existing blob' \
+ 'test $newf = `git rev-parse --verify branch:file2/newf`
+ test $oldf = `git rev-parse --verify branch:file2/oldf`'
+
+cat >expect <<EOF
+parent `git rev-parse --verify master^0`
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+second
+EOF
+test_expect_success \
+ 'C: verify commit' \
+ 'git cat-file commit branch | sed 1d >actual &&
+ test_cmp expect actual'
+
+cat >expect <<EOF
+:000000 100755 0000000000000000000000000000000000000000 f1fb5da718392694d0076d677d6d0e364c79b0bc A file2/newf
+:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100 file2 file2/oldf
+:100644 000000 0d92e9f3374ae2947c23aa477cbc68ce598135f1 0000000000000000000000000000000000000000 D file3
+EOF
+git diff-tree -M -r master branch >actual
+test_expect_success \
+ 'C: validate rename result' \
+ 'compare_diff_raw expect actual'
+
+###
+### series D
+###
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/branch
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third
+COMMIT
+
+from refs/heads/branch^0
+M 644 inline newdir/interesting
+data <<EOF
+$file5_data
+EOF
+
+M 755 inline newdir/exec.sh
+data <<EOF
+$file6_data
+EOF
+
+INPUT_END
+test_expect_success \
+ 'D: inline data in commit' \
+ 'git fast-import <input &&
+ git whatchanged branch'
+test_expect_success \
+ 'D: verify pack' \
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+cat >expect <<EOF
+:000000 100755 0000000000000000000000000000000000000000 35a59026a33beac1569b1c7f66f3090ce9c09afc A newdir/exec.sh
+:000000 100644 0000000000000000000000000000000000000000 046d0371e9220107917db0d0e030628de8a1de9b A newdir/interesting
+EOF
+git diff-tree -M -r branch^ branch >actual
+test_expect_success \
+ 'D: validate new files added' \
+ 'compare_diff_raw expect actual'
+
+echo "$file5_data" >expect
+test_expect_success \
+ 'D: verify file5' \
+ 'git cat-file blob branch:newdir/interesting >actual &&
+ test_cmp expect actual'
+
+echo "$file6_data" >expect
+test_expect_success \
+ 'D: verify file6' \
+ 'git cat-file blob branch:newdir/exec.sh >actual &&
+ test_cmp expect actual'
+
+###
+### series E
+###
+
+cat >input <<INPUT_END
+commit refs/heads/branch
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> Tue Feb 6 11:22:18 2007 -0500
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> Tue Feb 6 12:35:02 2007 -0500
+data <<COMMIT
+RFC 2822 type date
+COMMIT
+
+from refs/heads/branch^0
+
+INPUT_END
+test_expect_success 'E: rfc2822 date, --date-format=raw' '
+ test_must_fail git fast-import --date-format=raw <input
+'
+test_expect_success \
+ 'E: rfc2822 date, --date-format=rfc2822' \
+ 'git fast-import --date-format=rfc2822 <input'
+test_expect_success \
+ 'E: verify pack' \
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+cat >expect <<EOF
+author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 1170778938 -0500
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1170783302 -0500
+
+RFC 2822 type date
+EOF
+test_expect_success \
+ 'E: verify commit' \
+ 'git cat-file commit branch | sed 1,2d >actual &&
+ test_cmp expect actual'
+
+###
+### series F
+###
+
+old_branch=`git rev-parse --verify branch^0`
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/branch
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+losing things already?
+COMMIT
+
+from refs/heads/branch~1
+
+reset refs/heads/other
+from refs/heads/branch
+
+INPUT_END
+test_expect_success \
+ 'F: non-fast-forward update skips' \
+ 'if git fast-import <input
+ then
+ echo BAD gfi did not fail
+ return 1
+ else
+ if test $old_branch = `git rev-parse --verify branch^0`
+ then
+ : branch unaffected and failure returned
+ return 0
+ else
+ echo BAD gfi changed branch $old_branch
+ return 1
+ fi
+ fi
+ '
+test_expect_success \
+ 'F: verify pack' \
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+cat >expect <<EOF
+tree `git rev-parse branch~1^{tree}`
+parent `git rev-parse branch~1`
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+losing things already?
+EOF
+test_expect_success \
+ 'F: verify other commit' \
+ 'git cat-file commit other >actual &&
+ test_cmp expect actual'
+
+###
+### series G
+###
+
+old_branch=`git rev-parse --verify branch^0`
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/branch
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+losing things already?
+COMMIT
+
+from refs/heads/branch~1
+
+INPUT_END
+test_expect_success \
+ 'G: non-fast-forward update forced' \
+ 'git fast-import --force <input'
+test_expect_success \
+ 'G: verify pack' \
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+test_expect_success \
+ 'G: branch changed, but logged' \
+ 'test $old_branch != `git rev-parse --verify branch^0` &&
+ test $old_branch = `git rev-parse --verify branch@{1}`'
+
+###
+### series H
+###
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/H
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third
+COMMIT
+
+from refs/heads/branch^0
+M 644 inline i-will-die
+data <<EOF
+this file will never exist.
+EOF
+
+deleteall
+M 644 inline h/e/l/lo
+data <<EOF
+$file5_data
+EOF
+
+INPUT_END
+test_expect_success \
+ 'H: deletall, add 1' \
+ 'git fast-import <input &&
+ git whatchanged H'
+test_expect_success \
+ 'H: verify pack' \
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+cat >expect <<EOF
+:100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D file2/newf
+:100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D file2/oldf
+:100755 000000 85df50785d62d3b05ab03d9cbf7e4a0b49449730 0000000000000000000000000000000000000000 D file4
+:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100 newdir/interesting h/e/l/lo
+:100755 000000 e74b7d465e52746be2b4bae983670711e6e66657 0000000000000000000000000000000000000000 D newdir/exec.sh
+EOF
+git diff-tree -M -r H^ H >actual
+test_expect_success \
+ 'H: validate old files removed, new files added' \
+ 'compare_diff_raw expect actual'
+
+echo "$file5_data" >expect
+test_expect_success \
+ 'H: verify file' \
+ 'git cat-file blob H:h/e/l/lo >actual &&
+ test_cmp expect actual'
+
+###
+### series I
+###
+
+cat >input <<INPUT_END
+commit refs/heads/export-boundary
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+we have a border. its only 40 characters wide.
+COMMIT
+
+from refs/heads/branch
+
+INPUT_END
+test_expect_success \
+ 'I: export-pack-edges' \
+ 'git fast-import --export-pack-edges=edges.list <input'
+
+cat >expect <<EOF
+.git/objects/pack/pack-.pack: `git rev-parse --verify export-boundary`
+EOF
+test_expect_success \
+ 'I: verify edge list' \
+ 'sed -e s/pack-.*pack/pack-.pack/ edges.list >actual &&
+ test_cmp expect actual'
+
+###
+### series J
+###
+
+cat >input <<INPUT_END
+commit refs/heads/J
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+create J
+COMMIT
+
+from refs/heads/branch
+
+reset refs/heads/J
+
+commit refs/heads/J
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+initialize J
+COMMIT
+
+INPUT_END
+test_expect_success \
+ 'J: reset existing branch creates empty commit' \
+ 'git fast-import <input'
+test_expect_success \
+ 'J: branch has 1 commit, empty tree' \
+ 'test 1 = `git rev-list J | wc -l` &&
+ test 0 = `git ls-tree J | wc -l`'
+
+###
+### series K
+###
+
+cat >input <<INPUT_END
+commit refs/heads/K
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+create K
+COMMIT
+
+from refs/heads/branch
+
+commit refs/heads/K
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+redo K
+COMMIT
+
+from refs/heads/branch^1
+
+INPUT_END
+test_expect_success \
+ 'K: reinit branch with from' \
+ 'git fast-import <input'
+test_expect_success \
+ 'K: verify K^1 = branch^1' \
+ 'test `git rev-parse --verify branch^1` \
+ = `git rev-parse --verify K^1`'
+
+###
+### series L
+###
+
+cat >input <<INPUT_END
+blob
+mark :1
+data <<EOF
+some data
+EOF
+
+blob
+mark :2
+data <<EOF
+other data
+EOF
+
+commit refs/heads/L
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+create L
+COMMIT
+
+M 644 :1 b.
+M 644 :1 b/other
+M 644 :1 ba
+
+commit refs/heads/L
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+update L
+COMMIT
+
+M 644 :2 b.
+M 644 :2 b/other
+M 644 :2 ba
+INPUT_END
+
+cat >expect <<EXPECT_END
+:100644 100644 4268632... 55d3a52... M b.
+:040000 040000 0ae5cac... 443c768... M b
+:100644 100644 4268632... 55d3a52... M ba
+EXPECT_END
+
+test_expect_success \
+ 'L: verify internal tree sorting' \
+ 'git fast-import <input &&
+ git diff-tree --abbrev --raw L^ L >output &&
+ test_cmp expect output'
+
+###
+### series M
+###
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/M1
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+file rename
+COMMIT
+
+from refs/heads/branch^0
+R file2/newf file2/n.e.w.f
+
+INPUT_END
+
+cat >expect <<EOF
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 file2/newf file2/n.e.w.f
+EOF
+test_expect_success \
+ 'M: rename file in same subdirectory' \
+ 'git fast-import <input &&
+ git diff-tree -M -r M1^ M1 >actual &&
+ compare_diff_raw expect actual'
+
+cat >input <<INPUT_END
+commit refs/heads/M2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+file rename
+COMMIT
+
+from refs/heads/branch^0
+R file2/newf i/am/new/to/you
+
+INPUT_END
+
+cat >expect <<EOF
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 file2/newf i/am/new/to/you
+EOF
+test_expect_success \
+ 'M: rename file to new subdirectory' \
+ 'git fast-import <input &&
+ git diff-tree -M -r M2^ M2 >actual &&
+ compare_diff_raw expect actual'
+
+cat >input <<INPUT_END
+commit refs/heads/M3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+file rename
+COMMIT
+
+from refs/heads/M2^0
+R i other/sub
+
+INPUT_END
+
+cat >expect <<EOF
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100 i/am/new/to/you other/sub/am/new/to/you
+EOF
+test_expect_success \
+ 'M: rename subdirectory to new subdirectory' \
+ 'git fast-import <input &&
+ git diff-tree -M -r M3^ M3 >actual &&
+ compare_diff_raw expect actual'
+
+###
+### series N
+###
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/N1
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+file copy
+COMMIT
+
+from refs/heads/branch^0
+C file2/newf file2/n.e.w.f
+
+INPUT_END
+
+cat >expect <<EOF
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file2/n.e.w.f
+EOF
+test_expect_success \
+ 'N: copy file in same subdirectory' \
+ 'git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N1^ N1 >actual &&
+ compare_diff_raw expect actual'
+
+cat >input <<INPUT_END
+commit refs/heads/N2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+clean directory copy
+COMMIT
+
+from refs/heads/branch^0
+C file2 file3
+
+commit refs/heads/N2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+modify directory copy
+COMMIT
+
+M 644 inline file3/file5
+data <<EOF
+$file5_data
+EOF
+
+INPUT_END
+
+cat >expect <<EOF
+:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5
+:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+EOF
+test_expect_success \
+ 'N: copy then modify subdirectory' \
+ 'git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N2^^ N2 >actual &&
+ compare_diff_raw expect actual'
+
+cat >input <<INPUT_END
+commit refs/heads/N3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+dirty directory copy
+COMMIT
+
+from refs/heads/branch^0
+M 644 inline file2/file5
+data <<EOF
+$file5_data
+EOF
+
+C file2 file3
+D file2/file5
+
+INPUT_END
+
+test_expect_success \
+ 'N: copy dirty subdirectory' \
+ 'git fast-import <input &&
+ test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
+
+###
+### series O
+###
+
+cat >input <<INPUT_END
+#we will
+commit refs/heads/O1
+# -- ignore all of this text
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+# $GIT_COMMITTER_NAME has inserted here for his benefit.
+data <<COMMIT
+dirty directory copy
+COMMIT
+
+# don't forget the import blank line!
+#
+# yes, we started from our usual base of branch^0.
+# i like branch^0.
+from refs/heads/branch^0
+# and we need to reuse file2/file5 from N3 above.
+M 644 inline file2/file5
+# otherwise the tree will be different
+data <<EOF
+$file5_data
+EOF
+
+# don't forget to copy file2 to file3
+C file2 file3
+#
+# or to delete file5 from file2.
+D file2/file5
+# are we done yet?
+
+INPUT_END
+
+test_expect_success \
+ 'O: comments are all skipped' \
+ 'git fast-import <input &&
+ test `git rev-parse N3` = `git rev-parse O1`'
+
+cat >input <<INPUT_END
+commit refs/heads/O2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+dirty directory copy
+COMMIT
+from refs/heads/branch^0
+M 644 inline file2/file5
+data <<EOF
+$file5_data
+EOF
+C file2 file3
+D file2/file5
+
+INPUT_END
+
+test_expect_success \
+ 'O: blank lines not necessary after data commands' \
+ 'git fast-import <input &&
+ test `git rev-parse N3` = `git rev-parse O2`'
+
+test_expect_success \
+ 'O: repack before next test' \
+ 'git repack -a -d'
+
+cat >input <<INPUT_END
+commit refs/heads/O3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+zstring
+COMMIT
+commit refs/heads/O3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+zof
+COMMIT
+checkpoint
+commit refs/heads/O3
+mark :5
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+zempty
+COMMIT
+checkpoint
+commit refs/heads/O3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+zcommits
+COMMIT
+reset refs/tags/O3-2nd
+from :5
+reset refs/tags/O3-3rd
+from :5
+INPUT_END
+
+cat >expect <<INPUT_END
+string
+of
+empty
+commits
+INPUT_END
+test_expect_success \
+ 'O: blank lines not necessary after other commands' \
+ 'git fast-import <input &&
+ test 8 = `find .git/objects/pack -type f | wc -l` &&
+ test `git rev-parse refs/tags/O3-2nd` = `git rev-parse O3^` &&
+ git log --reverse --pretty=oneline O3 | sed s/^.*z// >actual &&
+ test_cmp expect actual'
+
+cat >input <<INPUT_END
+commit refs/heads/O4
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+zstring
+COMMIT
+commit refs/heads/O4
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+zof
+COMMIT
+progress Two commits down, 2 to go!
+commit refs/heads/O4
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+zempty
+COMMIT
+progress Three commits down, 1 to go!
+commit refs/heads/O4
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+zcommits
+COMMIT
+progress I'm done!
+INPUT_END
+test_expect_success \
+ 'O: progress outputs as requested by input' \
+ 'git fast-import <input >actual &&
+ grep "progress " <input >expect &&
+ test_cmp expect actual'
+
+###
+### series P (gitlinks)
+###
+
+cat >input <<INPUT_END
+blob
+mark :1
+data 10
+test file
+
+reset refs/heads/sub
+commit refs/heads/sub
+mark :2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data 12
+sub_initial
+M 100644 :1 file
+
+blob
+mark :3
+data <<DATAEND
+[submodule "sub"]
+ path = sub
+ url = "`pwd`/sub"
+DATAEND
+
+commit refs/heads/subuse1
+mark :4
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data 8
+initial
+from refs/heads/master
+M 100644 :3 .gitmodules
+M 160000 :2 sub
+
+blob
+mark :5
+data 20
+test file
+more data
+
+commit refs/heads/sub
+mark :6
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data 11
+sub_second
+from :2
+M 100644 :5 file
+
+commit refs/heads/subuse1
+mark :7
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data 7
+second
+from :4
+M 160000 :6 sub
+
+INPUT_END
+
+test_expect_success \
+ 'P: supermodule & submodule mix' \
+ 'git fast-import <input &&
+ git checkout subuse1 &&
+ rm -rf sub && mkdir sub && cd sub &&
+ git init &&
+ git fetch --update-head-ok .. refs/heads/sub:refs/heads/master &&
+ git checkout master &&
+ cd .. &&
+ git submodule init &&
+ git submodule update'
+
+SUBLAST=$(git rev-parse --verify sub)
+SUBPREV=$(git rev-parse --verify sub^)
+
+cat >input <<INPUT_END
+blob
+mark :1
+data <<DATAEND
+[submodule "sub"]
+ path = sub
+ url = "`pwd`/sub"
+DATAEND
+
+commit refs/heads/subuse2
+mark :2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data 8
+initial
+from refs/heads/master
+M 100644 :1 .gitmodules
+M 160000 $SUBPREV sub
+
+commit refs/heads/subuse2
+mark :3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data 7
+second
+from :2
+M 160000 $SUBLAST sub
+
+INPUT_END
+
+test_expect_success \
+ 'P: verbatim SHA gitlinks' \
+ 'git branch -D sub &&
+ git gc && git prune &&
+ git fast-import <input &&
+ test $(git rev-parse --verify subuse2) = $(git rev-parse --verify subuse1)'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/subuse3
+mark :1
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+corrupt
+COMMIT
+
+from refs/heads/subuse2
+M 160000 inline sub
+data <<DATA
+$SUBPREV
+DATA
+
+INPUT_END
+
+test_expect_success 'P: fail on inline gitlink' '
+ test_must_fail git fast-import <input'
+
+test_tick
+cat >input <<INPUT_END
+blob
+mark :1
+data <<DATA
+$SUBPREV
+DATA
+
+commit refs/heads/subuse3
+mark :2
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+corrupt
+COMMIT
+
+from refs/heads/subuse2
+M 160000 :1 sub
+
+INPUT_END
+
+test_expect_success 'P: fail on blob mark in gitlink' '
+ test_must_fail git fast-import <input'
+
+###
+### series Q (notes)
+###
+
+note1_data="The first note for the first commit"
+note2_data="The first note for the second commit"
+note3_data="The first note for the third commit"
+note1b_data="The second note for the first commit"
+note1c_data="The third note for the first commit"
+note2b_data="The second note for the second commit"
+
+test_tick
+cat >input <<INPUT_END
+blob
+mark :2
+data <<EOF
+$file2_data
+EOF
+
+commit refs/heads/notes-test
+mark :3
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+first (:3)
+COMMIT
+
+M 644 :2 file2
+
+blob
+mark :4
+data $file4_len
+$file4_data
+commit refs/heads/notes-test
+mark :5
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+second (:5)
+COMMIT
+
+M 644 :4 file4
+
+commit refs/heads/notes-test
+mark :6
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third (:6)
+COMMIT
+
+M 644 inline file5
+data <<EOF
+$file5_data
+EOF
+
+M 755 inline file6
+data <<EOF
+$file6_data
+EOF
+
+blob
+mark :7
+data <<EOF
+$note1_data
+EOF
+
+blob
+mark :8
+data <<EOF
+$note2_data
+EOF
+
+commit refs/notes/foobar
+mark :9
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes (:9)
+COMMIT
+
+N :7 :3
+N :8 :5
+N inline :6
+data <<EOF
+$note3_data
+EOF
+
+commit refs/notes/foobar
+mark :10
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes (:10)
+COMMIT
+
+N inline :3
+data <<EOF
+$note1b_data
+EOF
+
+commit refs/notes/foobar2
+mark :11
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes (:11)
+COMMIT
+
+N inline :3
+data <<EOF
+$note1c_data
+EOF
+
+commit refs/notes/foobar
+mark :12
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+notes (:12)
+COMMIT
+
+deleteall
+N inline :5
+data <<EOF
+$note2b_data
+EOF
+
+INPUT_END
+
+test_expect_success \
+ 'Q: commit notes' \
+ 'git fast-import <input &&
+ git whatchanged notes-test'
+test_expect_success \
+ 'Q: verify pack' \
+ 'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
+
+commit1=$(git rev-parse notes-test~2)
+commit2=$(git rev-parse notes-test^)
+commit3=$(git rev-parse notes-test)
+
+cat >expect <<EOF
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+first (:3)
+EOF
+test_expect_success \
+ 'Q: verify first commit' \
+ 'git cat-file commit notes-test~2 | sed 1d >actual &&
+ test_cmp expect actual'
+
+cat >expect <<EOF
+parent $commit1
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+second (:5)
+EOF
+test_expect_success \
+ 'Q: verify second commit' \
+ 'git cat-file commit notes-test^ | sed 1d >actual &&
+ test_cmp expect actual'
+
+cat >expect <<EOF
+parent $commit2
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+third (:6)
+EOF
+test_expect_success \
+ 'Q: verify third commit' \
+ 'git cat-file commit notes-test | sed 1d >actual &&
+ test_cmp expect actual'
+
+cat >expect <<EOF
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+notes (:9)
+EOF
+test_expect_success \
+ 'Q: verify first notes commit' \
+ 'git cat-file commit refs/notes/foobar~2 | sed 1d >actual &&
+ test_cmp expect actual'
+
+cat >expect.unsorted <<EOF
+100644 blob $commit1
+100644 blob $commit2
+100644 blob $commit3
+EOF
+cat expect.unsorted | sort >expect
+test_expect_success \
+ 'Q: verify first notes tree' \
+ 'git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual'
+
+echo "$note1_data" >expect
+test_expect_success \
+ 'Q: verify first note for first commit' \
+ 'git cat-file blob refs/notes/foobar~2:$commit1 >actual && test_cmp expect actual'
+
+echo "$note2_data" >expect
+test_expect_success \
+ 'Q: verify first note for second commit' \
+ 'git cat-file blob refs/notes/foobar~2:$commit2 >actual && test_cmp expect actual'
+
+echo "$note3_data" >expect
+test_expect_success \
+ 'Q: verify first note for third commit' \
+ 'git cat-file blob refs/notes/foobar~2:$commit3 >actual && test_cmp expect actual'
+
+cat >expect <<EOF
+parent `git rev-parse --verify refs/notes/foobar~2`
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+notes (:10)
+EOF
+test_expect_success \
+ 'Q: verify second notes commit' \
+ 'git cat-file commit refs/notes/foobar^ | sed 1d >actual &&
+ test_cmp expect actual'
+
+cat >expect.unsorted <<EOF
+100644 blob $commit1
+100644 blob $commit2
+100644 blob $commit3
+EOF
+cat expect.unsorted | sort >expect
+test_expect_success \
+ 'Q: verify second notes tree' \
+ 'git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual'
+
+echo "$note1b_data" >expect
+test_expect_success \
+ 'Q: verify second note for first commit' \
+ 'git cat-file blob refs/notes/foobar^:$commit1 >actual && test_cmp expect actual'
+
+echo "$note2_data" >expect
+test_expect_success \
+ 'Q: verify first note for second commit' \
+ 'git cat-file blob refs/notes/foobar^:$commit2 >actual && test_cmp expect actual'
+
+echo "$note3_data" >expect
+test_expect_success \
+ 'Q: verify first note for third commit' \
+ 'git cat-file blob refs/notes/foobar^:$commit3 >actual && test_cmp expect actual'
+
+cat >expect <<EOF
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+notes (:11)
+EOF
+test_expect_success \
+ 'Q: verify third notes commit' \
+ 'git cat-file commit refs/notes/foobar2 | sed 1d >actual &&
+ test_cmp expect actual'
+
+cat >expect.unsorted <<EOF
+100644 blob $commit1
+EOF
+cat expect.unsorted | sort >expect
+test_expect_success \
+ 'Q: verify third notes tree' \
+ 'git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual'
+
+echo "$note1c_data" >expect
+test_expect_success \
+ 'Q: verify third note for first commit' \
+ 'git cat-file blob refs/notes/foobar2:$commit1 >actual && test_cmp expect actual'
+
+cat >expect <<EOF
+parent `git rev-parse --verify refs/notes/foobar^`
+author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+notes (:12)
+EOF
+test_expect_success \
+ 'Q: verify fourth notes commit' \
+ 'git cat-file commit refs/notes/foobar | sed 1d >actual &&
+ test_cmp expect actual'
+
+cat >expect.unsorted <<EOF
+100644 blob $commit2
+EOF
+cat expect.unsorted | sort >expect
+test_expect_success \
+ 'Q: verify fourth notes tree' \
+ 'git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
+ test_cmp expect actual'
+
+echo "$note2b_data" >expect
+test_expect_success \
+ 'Q: verify second note for second commit' \
+ 'git cat-file blob refs/notes/foobar:$commit2 >actual && test_cmp expect actual'
+
+###
+### series R (feature and option)
+###
+
+cat >input <<EOF
+feature no-such-feature-exists
+EOF
+
+test_expect_success 'R: abort on unsupported feature' '
+ test_must_fail git fast-import <input
+'
+
+cat >input <<EOF
+feature date-format=now
+EOF
+
+test_expect_success 'R: supported feature is accepted' '
+ git fast-import <input
+'
+
+cat >input << EOF
+blob
+data 3
+hi
+feature date-format=now
+EOF
+
+test_expect_success 'R: abort on receiving feature after data command' '
+ test_must_fail git fast-import <input
+'
+
+cat >input << EOF
+feature import-marks=git.marks
+feature import-marks=git2.marks
+EOF
+
+test_expect_success 'R: only one import-marks feature allowed per stream' '
+ test_must_fail git fast-import <input
+'
+
+cat >input << EOF
+feature export-marks=git.marks
+blob
+mark :1
+data 3
+hi
+
+EOF
+
+test_expect_success \
+ 'R: export-marks feature results in a marks file being created' \
+ 'cat input | git fast-import &&
+ grep :1 git.marks'
+
+test_expect_success \
+ 'R: export-marks options can be overriden by commandline options' \
+ 'cat input | git fast-import --export-marks=other.marks &&
+ grep :1 other.marks'
+
+cat >input << EOF
+feature import-marks=marks.out
+feature export-marks=marks.new
+EOF
+
+test_expect_success \
+ 'R: import to output marks works without any content' \
+ 'cat input | git fast-import &&
+ test_cmp marks.out marks.new'
+
+cat >input <<EOF
+feature import-marks=nonexistant.marks
+feature export-marks=marks.new
+EOF
+
+test_expect_success \
+ 'R: import marks prefers commandline marks file over the stream' \
+ 'cat input | git fast-import --import-marks=marks.out &&
+ test_cmp marks.out marks.new'
+
+
+cat >input <<EOF
+feature import-marks=nonexistant.marks
+feature export-marks=combined.marks
+EOF
+
+test_expect_success 'R: multiple --import-marks= should be honoured' '
+ head -n2 marks.out > one.marks &&
+ tail -n +3 marks.out > two.marks &&
+ git fast-import --import-marks=one.marks --import-marks=two.marks <input &&
+ test_cmp marks.out combined.marks
+'
+
+cat >input <<EOF
+feature relative-marks
+feature import-marks=relative.in
+feature export-marks=relative.out
+EOF
+
+test_expect_success 'R: feature relative-marks should be honoured' '
+ mkdir -p .git/info/fast-import/ &&
+ cp marks.new .git/info/fast-import/relative.in &&
+ git fast-import <input &&
+ test_cmp marks.new .git/info/fast-import/relative.out
+'
+
+cat >input <<EOF
+feature relative-marks
+feature import-marks=relative.in
+feature no-relative-marks
+feature export-marks=non-relative.out
+EOF
+
+test_expect_success 'R: feature no-relative-marks should be honoured' '
+ git fast-import <input &&
+ test_cmp marks.new non-relative.out
+'
+
+cat >input << EOF
+option git quiet
+blob
+data 3
+hi
+
+EOF
+
+touch empty
+
+test_expect_success 'R: quiet option results in no stats being output' '
+ cat input | git fast-import 2> output &&
+ test_cmp empty output
+'
+
+cat >input <<EOF
+option git non-existing-option
+EOF
+
+test_expect_success 'R: die on unknown option' '
+ test_must_fail git fast-import <input
+'
+
+test_expect_success 'R: unknown commandline options are rejected' '\
+ test_must_fail git fast-import --non-existing-option < /dev/null
+'
+
+cat >input <<EOF
+option non-existing-vcs non-existing-option
+EOF
+
+test_expect_success 'R: ignore non-git options' '
+ git fast-import <input
+'
+
+##
+## R: very large blobs
+##
+blobsize=$((2*1024*1024 + 53))
+test-genrandom bar $blobsize >expect
+cat >input <<INPUT_END
+commit refs/heads/big-file
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+R - big file
+COMMIT
+
+M 644 inline big1
+data $blobsize
+INPUT_END
+cat expect >>input
+cat >>input <<INPUT_END
+M 644 inline big2
+data $blobsize
+INPUT_END
+cat expect >>input
+echo >>input
+
+test_expect_success \
+ 'R: blob bigger than threshold' \
+ 'test_create_repo R &&
+ git --git-dir=R/.git fast-import --big-file-threshold=1 <input'
+test_expect_success \
+ 'R: verify created pack' \
+ ': >verify &&
+ for p in R/.git/objects/pack/*.pack;
+ do
+ git verify-pack -v $p >>verify || exit;
+ done'
+test_expect_success \
+ 'R: verify written objects' \
+ 'git --git-dir=R/.git cat-file blob big-file:big1 >actual &&
+ test_cmp expect actual &&
+ a=$(git --git-dir=R/.git rev-parse big-file:big1) &&
+ b=$(git --git-dir=R/.git rev-parse big-file:big2) &&
+ test $a = $b'
+test_expect_success \
+ 'R: blob appears only once' \
+ 'n=$(grep $a verify | wc -l) &&
+ test 1 = $n'
+
+test_done
diff --git a/t/t9301-fast-import-notes.sh b/t/t9301-fast-import-notes.sh
new file mode 100755
index 0000000..a5c99d8
--- /dev/null
+++ b/t/t9301-fast-import-notes.sh
@@ -0,0 +1,623 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Johan Herland
+#
+
+test_description='test git fast-import of notes objects'
+. ./test-lib.sh
+
+
+test_tick
+cat >input <<INPUT_END
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+first commit
+COMMIT
+
+M 644 inline foo
+data <<EOF
+file foo in first commit
+EOF
+
+M 755 inline bar
+data <<EOF
+file bar in first commit
+EOF
+
+M 644 inline baz/xyzzy
+data <<EOF
+file baz/xyzzy in first commit
+EOF
+
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+second commit
+COMMIT
+
+M 644 inline foo
+data <<EOF
+file foo in second commit
+EOF
+
+M 755 inline baz/xyzzy
+data <<EOF
+file baz/xyzzy in second commit
+EOF
+
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third commit
+COMMIT
+
+M 644 inline foo
+data <<EOF
+file foo in third commit
+EOF
+
+commit refs/heads/master
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+fourth commit
+COMMIT
+
+M 755 inline bar
+data <<EOF
+file bar in fourth commit
+EOF
+
+INPUT_END
+
+test_expect_success 'set up master branch' '
+
+ git fast-import <input &&
+ git whatchanged master
+'
+
+commit4=$(git rev-parse refs/heads/master)
+commit3=$(git rev-parse "$commit4^")
+commit2=$(git rev-parse "$commit4~2")
+commit1=$(git rev-parse "$commit4~3")
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+first notes commit
+COMMIT
+
+M 644 inline $commit1
+data <<EOF
+first note for first commit
+EOF
+
+M 755 inline $commit2
+data <<EOF
+first note for second commit
+EOF
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+ fourth commit
+ third commit
+ second commit
+ first note for second commit
+ first commit
+ first note for first commit
+EXPECT_END
+
+test_expect_success 'add notes with simple M command' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/test git log | grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+second notes commit
+COMMIT
+
+from refs/notes/test^0
+N inline $commit3
+data <<EOF
+first note for third commit
+EOF
+
+N inline $commit4
+data <<EOF
+first note for fourth commit
+EOF
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+ fourth commit
+ first note for fourth commit
+ third commit
+ first note for third commit
+ second commit
+ first note for second commit
+ first commit
+ first note for first commit
+EXPECT_END
+
+test_expect_success 'add notes with simple N command' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/test git log | grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+third notes commit
+COMMIT
+
+from refs/notes/test^0
+N inline $commit1
+data <<EOF
+second note for first commit
+EOF
+
+N inline $commit2
+data <<EOF
+second note for second commit
+EOF
+
+N inline $commit3
+data <<EOF
+second note for third commit
+EOF
+
+N inline $commit4
+data <<EOF
+second note for fourth commit
+EOF
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+ fourth commit
+ second note for fourth commit
+ third commit
+ second note for third commit
+ second commit
+ second note for second commit
+ first commit
+ second note for first commit
+EXPECT_END
+
+test_expect_success 'update existing notes with N command' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/test git log | grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+fourth notes commit
+COMMIT
+
+from refs/notes/test^0
+M 644 inline $(echo "$commit3" | sed "s|^..|&/|")
+data <<EOF
+prefix of note for third commit
+EOF
+
+M 644 inline $(echo "$commit4" | sed "s|^..|&/|")
+data <<EOF
+prefix of note for fourth commit
+EOF
+
+M 644 inline $(echo "$commit4" | sed "s|^\(..\)\(..\)|\1/\2/|")
+data <<EOF
+pre-prefix of note for fourth commit
+EOF
+
+N inline $commit1
+data <<EOF
+third note for first commit
+EOF
+
+N inline $commit2
+data <<EOF
+third note for second commit
+EOF
+
+N inline $commit3
+data <<EOF
+third note for third commit
+EOF
+
+N inline $commit4
+data <<EOF
+third note for fourth commit
+EOF
+
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+ fourth commit
+ pre-prefix of note for fourth commit
+ prefix of note for fourth commit
+ third note for fourth commit
+ third commit
+ prefix of note for third commit
+ third note for third commit
+ second commit
+ third note for second commit
+ first commit
+ third note for first commit
+EXPECT_END
+
+test_expect_success 'add concatentation notes with M command' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/test git log | grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+fifth notes commit
+COMMIT
+
+from refs/notes/test^0
+deleteall
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+ fourth commit
+ third commit
+ second commit
+ first commit
+EXPECT_END
+
+test_expect_success 'verify that deleteall also removes notes' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/test git log | grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_tick
+cat >input <<INPUT_END
+commit refs/notes/test
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+sixth notes commit
+COMMIT
+
+from refs/notes/test^0
+M 644 inline $commit1
+data <<EOF
+third note for first commit
+EOF
+
+M 644 inline $commit3
+data <<EOF
+third note for third commit
+EOF
+
+N inline $commit1
+data <<EOF
+fourth note for first commit
+EOF
+
+N inline $commit3
+data <<EOF
+fourth note for third commit
+EOF
+
+INPUT_END
+
+cat >expect <<EXPECT_END
+ fourth commit
+ third commit
+ fourth note for third commit
+ second commit
+ first commit
+ fourth note for first commit
+EXPECT_END
+
+test_expect_success 'verify that later N commands override earlier M commands' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/test git log | grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+# Write fast-import commands to create the given number of commits
+fast_import_commits () {
+ my_ref=$1
+ my_num_commits=$2
+ my_append_to_file=$3
+ my_i=0
+ while test $my_i -lt $my_num_commits
+ do
+ my_i=$(($my_i + 1))
+ test_tick
+ cat >>"$my_append_to_file" <<INPUT_END
+commit $my_ref
+mark :$my_i
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+commit #$my_i
+COMMIT
+
+M 644 inline file
+data <<EOF
+file contents in commit #$my_i
+EOF
+
+INPUT_END
+ done
+}
+
+# Write fast-import commands to create the given number of notes annotating
+# the commits created by fast_import_commits()
+fast_import_notes () {
+ my_notes_ref=$1
+ my_num_commits=$2
+ my_append_to_file=$3
+ my_note_append=$4
+ test_tick
+ cat >>"$my_append_to_file" <<INPUT_END
+commit $my_notes_ref
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+committing $my_num_commits notes
+COMMIT
+
+INPUT_END
+
+ my_i=0
+ while test $my_i -lt $my_num_commits
+ do
+ my_i=$(($my_i + 1))
+ cat >>"$my_append_to_file" <<INPUT_END
+N inline :$my_i
+data <<EOF
+note for commit #$my_i$my_note_append
+EOF
+
+INPUT_END
+ done
+}
+
+
+rm input expect
+num_commits=400
+# Create lots of commits
+fast_import_commits "refs/heads/many_commits" $num_commits input
+# Create one note per above commit
+fast_import_notes "refs/notes/many_notes" $num_commits input
+# Add a couple of non-notes as well
+test_tick
+cat >>input <<INPUT_END
+commit refs/notes/many_notes
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+committing some non-notes to the notes tree
+COMMIT
+
+M 755 inline foobar/non-note.txt
+data <<EOF
+This is not a note, but rather a regular file residing in a notes tree
+EOF
+
+M 644 inline deadbeef
+data <<EOF
+Non-note file
+EOF
+
+M 644 inline de/adbeef
+data <<EOF
+Another non-note file
+EOF
+
+INPUT_END
+# Finally create the expected output from all these notes and commits
+i=$num_commits
+while test $i -gt 0
+do
+ cat >>expect <<EXPECT_END
+ commit #$i
+ note for commit #$i
+EXPECT_END
+ i=$(($i - 1))
+done
+
+test_expect_success 'add lots of commits and notes' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/many_notes git log refs/heads/many_commits |
+ grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'verify that lots of notes trigger a fanout scheme' '
+
+ # None of the entries in the top-level notes tree should be a full SHA1
+ git ls-tree --name-only refs/notes/many_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
+
+cat >>expect_non-note2 << EOF
+Non-note file
+EOF
+
+cat >>expect_non-note3 << EOF
+Another non-note file
+EOF
+
+test_expect_success 'verify that non-notes are untouched by a fanout change' '
+
+ git cat-file -p refs/notes/many_notes:foobar/non-note.txt > actual &&
+ test_cmp expect_non-note1 actual &&
+ git cat-file -p refs/notes/many_notes:deadbeef > actual &&
+ test_cmp expect_non-note2 actual &&
+ git cat-file -p refs/notes/many_notes:de/adbeef > actual &&
+ test_cmp expect_non-note3 actual
+
+'
+remaining_notes=10
+test_tick
+cat >>input <<INPUT_END
+commit refs/notes/many_notes
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+removing all notes but $remaining_notes
+COMMIT
+from refs/notes/many_notes^0
+INPUT_END
+
+i=$remaining_notes
+while test $i -lt $num_commits
+do
+ i=$(($i + 1))
+ cat >>input <<INPUT_END
+N 0000000000000000000000000000000000000000 :$i
+INPUT_END
+done
+
+i=$num_commits
+rm expect
+while test $i -gt 0
+do
+ cat >>expect <<EXPECT_END
+ commit #$i
+EXPECT_END
+ if test $i -le $remaining_notes
+ then
+ cat >>expect <<EXPECT_END
+ note for commit #$i
+EXPECT_END
+ fi
+ i=$(($i - 1))
+done
+
+test_expect_success 'remove lots of notes' '
+
+ git fast-import <input &&
+ GIT_NOTES_REF=refs/notes/many_notes git log refs/heads/many_commits |
+ grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'verify that removing notes trigger fanout consolidation' '
+
+ # All entries in the top-level notes tree should be a full SHA1
+ git ls-tree --name-only -r refs/notes/many_notes |
+ while read path
+ do
+ # Explicitly ignore the non-note paths
+ test "$path" = "foobar/non-note.txt" && continue
+ test "$path" = "deadbeef" && continue
+ test "$path" = "de/adbeef" && continue
+
+ if test $(expr length "$path") -ne 40
+ then
+ return 1
+ fi
+ done
+
+'
+
+test_expect_success 'verify that non-notes are untouched by a fanout change' '
+
+ git cat-file -p refs/notes/many_notes:foobar/non-note.txt > actual &&
+ test_cmp expect_non-note1 actual &&
+ git cat-file -p refs/notes/many_notes:deadbeef > actual &&
+ test_cmp expect_non-note2 actual &&
+ git cat-file -p refs/notes/many_notes:de/adbeef > actual &&
+ test_cmp expect_non-note3 actual
+
+'
+
+
+rm input expect
+num_notes_refs=10
+num_commits=16
+some_commits=8
+# Create commits
+fast_import_commits "refs/heads/more_commits" $num_commits input
+# Create one note per above commit per notes ref
+i=0
+while test $i -lt $num_notes_refs
+do
+ i=$(($i + 1))
+ fast_import_notes "refs/notes/more_notes_$i" $num_commits input
+done
+# Trigger branch reloading in git-fast-import by repeating the note creation
+i=0
+while test $i -lt $num_notes_refs
+do
+ i=$(($i + 1))
+ fast_import_notes "refs/notes/more_notes_$i" $some_commits input " (2)"
+done
+# Finally create the expected output from the notes in refs/notes/more_notes_1
+i=$num_commits
+while test $i -gt 0
+do
+ note_data="note for commit #$i"
+ if test $i -le $some_commits
+ then
+ note_data="$note_data (2)"
+ fi
+ cat >>expect <<EXPECT_END
+ commit #$i
+ $note_data
+EXPECT_END
+ i=$(($i - 1))
+done
+
+test_expect_success "add notes to $num_commits commits in each of $num_notes_refs refs" '
+
+ git fast-import --active-branches=5 <input &&
+ GIT_NOTES_REF=refs/notes/more_notes_1 git log refs/heads/more_commits |
+ grep "^ " > actual &&
+ test_cmp expect actual
+
+'
+
+test_done
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
new file mode 100755
index 0000000..d43f37c
--- /dev/null
+++ b/t/t9350-fast-export.sh
@@ -0,0 +1,379 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Johannes E. Schindelin
+#
+
+test_description='git fast-export'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ echo break it > file0 &&
+ git add file0 &&
+ test_tick &&
+ echo Wohlauf > file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ echo die Luft > file &&
+ echo geht frisch > file2 &&
+ git add file file2 &&
+ test_tick &&
+ git commit -m second &&
+ echo und > file2 &&
+ test_tick &&
+ git commit -m third file2 &&
+ test_tick &&
+ git tag rein &&
+ git checkout -b wer HEAD^ &&
+ echo lange > file2
+ test_tick &&
+ git commit -m sitzt file2 &&
+ test_tick &&
+ git tag -a -m valentin muss &&
+ git merge -s ours master
+
+'
+
+test_expect_success 'fast-export | fast-import' '
+
+ MASTER=$(git rev-parse --verify master) &&
+ REIN=$(git rev-parse --verify rein) &&
+ WER=$(git rev-parse --verify wer) &&
+ MUSS=$(git rev-parse --verify muss) &&
+ mkdir new &&
+ git --git-dir=new/.git init &&
+ git fast-export --all |
+ (cd new &&
+ git fast-import &&
+ test $MASTER = $(git rev-parse --verify refs/heads/master) &&
+ test $REIN = $(git rev-parse --verify refs/tags/rein) &&
+ test $WER = $(git rev-parse --verify refs/heads/wer) &&
+ test $MUSS = $(git rev-parse --verify refs/tags/muss))
+
+'
+
+test_expect_success 'fast-export master~2..master' '
+
+ git fast-export master~2..master |
+ sed "s/master/partial/" |
+ (cd new &&
+ git fast-import &&
+ test $MASTER != $(git rev-parse --verify refs/heads/partial) &&
+ git diff --exit-code master partial &&
+ git diff --exit-code master^ partial^ &&
+ test_must_fail git rev-parse partial~2)
+
+'
+
+test_expect_success 'iso-8859-1' '
+
+ git config i18n.commitencoding ISO8859-1 &&
+ # use author and committer name in ISO-8859-1 to match it.
+ . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+ test_tick &&
+ echo rosten >file &&
+ git commit -s -m den file &&
+ git fast-export wer^..wer |
+ sed "s/wer/i18n/" |
+ (cd new &&
+ git fast-import &&
+ git cat-file commit i18n | grep "Ãéí óú")
+
+'
+test_expect_success 'import/export-marks' '
+
+ git checkout -b marks master &&
+ git fast-export --export-marks=tmp-marks HEAD &&
+ test -s tmp-marks &&
+ test $(wc -l < tmp-marks) -eq 3 &&
+ test $(
+ git fast-export --import-marks=tmp-marks\
+ --export-marks=tmp-marks HEAD |
+ grep ^commit |
+ wc -l) \
+ -eq 0 &&
+ echo change > file &&
+ git commit -m "last commit" file &&
+ test $(
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks HEAD |
+ grep ^commit\ |
+ wc -l) \
+ -eq 1 &&
+ test $(wc -l < tmp-marks) -eq 4
+
+'
+
+cat > signed-tag-import << EOF
+tag sign-your-name
+from $(git rev-parse HEAD)
+tagger C O Mitter <committer@example.com> 1112911993 -0700
+data 210
+A message for a sign
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.5 (GNU/Linux)
+
+fakedsignaturefakedsignaturefakedsignaturefakedsignaturfakedsign
+aturefakedsignaturefake=
+=/59v
+-----END PGP SIGNATURE-----
+EOF
+
+test_expect_success 'set up faked signed tag' '
+
+ cat signed-tag-import | git fast-import
+
+'
+
+test_expect_success 'signed-tags=abort' '
+
+ test_must_fail git fast-export --signed-tags=abort sign-your-name
+
+'
+
+test_expect_success 'signed-tags=verbatim' '
+
+ git fast-export --signed-tags=verbatim sign-your-name > output &&
+ grep PGP output
+
+'
+
+test_expect_success 'signed-tags=strip' '
+
+ git fast-export --signed-tags=strip sign-your-name > output &&
+ ! grep PGP output
+
+'
+
+test_expect_success 'setup submodule' '
+
+ git checkout -f master &&
+ mkdir sub &&
+ (
+ cd sub &&
+ git init &&
+ echo test file > file &&
+ git add file &&
+ git commit -m sub_initial
+ ) &&
+ git submodule add "`pwd`/sub" sub &&
+ git commit -m initial &&
+ test_tick &&
+ (
+ cd sub &&
+ echo more data >> file &&
+ git add file &&
+ git commit -m sub_second
+ ) &&
+ git add sub &&
+ git commit -m second
+
+'
+
+test_expect_success 'submodule fast-export | fast-import' '
+
+ SUBENT1=$(git ls-tree master^ sub) &&
+ SUBENT2=$(git ls-tree master sub) &&
+ rm -rf new &&
+ mkdir new &&
+ git --git-dir=new/.git init &&
+ git fast-export --signed-tags=strip --all |
+ (cd new &&
+ git fast-import &&
+ test "$SUBENT1" = "$(git ls-tree refs/heads/master^ sub)" &&
+ test "$SUBENT2" = "$(git ls-tree refs/heads/master sub)" &&
+ git checkout master &&
+ git submodule init &&
+ git submodule update &&
+ cmp sub/file ../sub/file)
+
+'
+
+GIT_AUTHOR_NAME='A U Thor'; export GIT_AUTHOR_NAME
+GIT_COMMITTER_NAME='C O Mitter'; export GIT_COMMITTER_NAME
+
+test_expect_success 'setup copies' '
+
+ git config --unset i18n.commitencoding &&
+ git checkout -b copy rein &&
+ git mv file file3 &&
+ git commit -m move1 &&
+ test_tick &&
+ cp file2 file4 &&
+ git add file4 &&
+ git mv file2 file5 &&
+ git commit -m copy1 &&
+ test_tick &&
+ cp file3 file6 &&
+ git add file6 &&
+ git commit -m copy2 &&
+ test_tick &&
+ echo more text >> file6 &&
+ echo even more text >> file6 &&
+ git add file6 &&
+ git commit -m modify &&
+ test_tick &&
+ cp file6 file7 &&
+ echo test >> file7 &&
+ git add file7 &&
+ git commit -m copy_modify
+
+'
+
+test_expect_success 'fast-export -C -C | fast-import' '
+
+ ENTRY=$(git rev-parse --verify copy) &&
+ rm -rf new &&
+ mkdir new &&
+ git --git-dir=new/.git init &&
+ git fast-export -C -C --signed-tags=strip --all > output &&
+ grep "^C \"file6\" \"file7\"\$" output &&
+ cat output |
+ (cd new &&
+ git fast-import &&
+ test $ENTRY = $(git rev-parse --verify refs/heads/copy))
+
+'
+
+test_expect_success 'fast-export | fast-import when master is tagged' '
+
+ git tag -m msg last &&
+ git fast-export -C -C --signed-tags=strip --all > output &&
+ test $(grep -c "^tag " output) = 3
+
+'
+
+cat > tag-content << EOF
+object $(git rev-parse HEAD)
+type commit
+tag rosten
+EOF
+
+test_expect_success 'cope with tagger-less tags' '
+
+ TAG=$(git hash-object -t tag -w tag-content) &&
+ git update-ref refs/tags/sonnenschein $TAG &&
+ git fast-export -C -C --signed-tags=strip --all > output &&
+ test $(grep -c "^tag " output) = 4 &&
+ ! grep "Unspecified Tagger" output &&
+ git fast-export -C -C --signed-tags=strip --all \
+ --fake-missing-tagger > output &&
+ test $(grep -c "^tag " output) = 4 &&
+ grep "Unspecified Tagger" output
+
+'
+
+test_expect_success 'setup for limiting exports by PATH' '
+ mkdir limit-by-paths &&
+ (
+ cd limit-by-paths &&
+ git init &&
+ echo hi > there &&
+ git add there &&
+ git commit -m "First file" &&
+ echo foo > bar &&
+ git add bar &&
+ git commit -m "Second file" &&
+ git tag -a -m msg mytag &&
+ echo morefoo >> bar &&
+ git add bar &&
+ git commit -m "Change to second file"
+ )
+'
+
+cat > limit-by-paths/expected << EOF
+blob
+mark :1
+data 3
+hi
+
+reset refs/tags/mytag
+commit refs/tags/mytag
+mark :2
+author A U Thor <author@example.com> 1112912713 -0700
+committer C O Mitter <committer@example.com> 1112912713 -0700
+data 11
+First file
+M 100644 :1 there
+
+EOF
+
+test_expect_success 'dropping tag of filtered out object' '
+(
+ cd limit-by-paths &&
+ git fast-export --tag-of-filtered-object=drop mytag -- there > output &&
+ test_cmp output expected
+)
+'
+
+cat >> limit-by-paths/expected << EOF
+tag mytag
+from :2
+tagger C O Mitter <committer@example.com> 1112912713 -0700
+data 4
+msg
+
+EOF
+
+test_expect_success 'rewriting tag of filtered out object' '
+(
+ cd limit-by-paths &&
+ git fast-export --tag-of-filtered-object=rewrite mytag -- there > output &&
+ test_cmp output expected
+)
+'
+
+cat > limit-by-paths/expected << EOF
+blob
+mark :1
+data 4
+foo
+
+blob
+mark :2
+data 3
+hi
+
+reset refs/heads/master
+commit refs/heads/master
+mark :3
+author A U Thor <author@example.com> 1112912713 -0700
+committer C O Mitter <committer@example.com> 1112912713 -0700
+data 12
+Second file
+M 100644 :1 bar
+M 100644 :2 there
+
+EOF
+
+test_expect_failure 'no exact-ref revisions included' '
+ (
+ cd limit-by-paths &&
+ git fast-export master~2..master~1 > output &&
+ test_cmp output expected
+ )
+'
+
+test_expect_success 'set-up a few more tags for tag export tests' '
+ git checkout -f master &&
+ HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
+ git tag tree_tag -m "tagging a tree" $HEAD_TREE &&
+ git tag -a tree_tag-obj -m "tagging a tree" $HEAD_TREE &&
+ git tag tag-obj_tag -m "tagging a tag" tree_tag-obj &&
+ git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj
+'
+
+test_expect_success 'tree_tag' '
+ mkdir result &&
+ (cd result && git init) &&
+ git fast-export tree_tag > fe-stream &&
+ (cd result && git fast-import < ../fe-stream)
+'
+
+# NEEDSWORK: not just check return status, but validate the output
+test_expect_success 'tree_tag-obj' 'git fast-export tree_tag-obj'
+test_expect_success 'tag-obj_tag' 'git fast-export tag-obj_tag'
+test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
+
+test_done
diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh
new file mode 100755
index 0000000..36c457e
--- /dev/null
+++ b/t/t9400-git-cvsserver-server.sh
@@ -0,0 +1,520 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Frank Lichtenheld
+#
+
+test_description='git-cvsserver access
+
+tests read access to a git repository with the
+cvs CLI client via git-cvsserver server'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping git cvsserver tests, perl not available'
+ test_done
+fi
+cvs >/dev/null 2>&1
+if test $? -ne 1
+then
+ skip_all='skipping git-cvsserver tests, cvs not found'
+ test_done
+fi
+"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+ skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
+ test_done
+}
+
+unset GIT_DIR GIT_CONFIG
+WORKDIR=$(pwd)
+SERVERDIR=$(pwd)/gitcvs.git
+git_config="$SERVERDIR/config"
+CVSROOT=":fork:$SERVERDIR"
+CVSWORK="$(pwd)/cvswork"
+CVS_SERVER=git-cvsserver
+export CVSROOT CVS_SERVER
+
+rm -rf "$CVSWORK" "$SERVERDIR"
+test_expect_success 'setup' '
+ echo >empty &&
+ git add empty &&
+ git commit -q -m "First Commit" &&
+ mkdir secondroot &&
+ ( cd secondroot &&
+ git init &&
+ touch secondrootfile &&
+ git add secondrootfile &&
+ git commit -m "second root") &&
+ git pull secondroot master &&
+ git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.authdb "$SERVERDIR/auth.db" &&
+ echo cvsuser:cvGVEarMLnhlA > "$SERVERDIR/auth.db"
+'
+
+# note that cvs doesn't accept absolute pathnames
+# as argument to co -d
+test_expect_success 'basic checkout' \
+ 'GIT_CONFIG="$git_config" cvs -Q co -d cvswork master &&
+ test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | head -n 1))" = "empty/1.1/"
+ test "$(echo $(grep -v ^D cvswork/CVS/Entries|cut -d/ -f2,3,5 | sed -ne \$p))" = "secondrootfile/1.1/"'
+
+#------------------------
+# PSERVER AUTHENTICATION
+#------------------------
+
+cat >request-anonymous <<EOF
+BEGIN AUTH REQUEST
+$SERVERDIR
+anonymous
+
+END AUTH REQUEST
+EOF
+
+cat >request-git <<EOF
+BEGIN AUTH REQUEST
+$SERVERDIR
+git
+
+END AUTH REQUEST
+EOF
+
+cat >login-anonymous <<EOF
+BEGIN VERIFICATION REQUEST
+$SERVERDIR
+anonymous
+
+END VERIFICATION REQUEST
+EOF
+
+cat >login-git <<EOF
+BEGIN VERIFICATION REQUEST
+$SERVERDIR
+git
+
+END VERIFICATION REQUEST
+EOF
+
+cat >login-git-ok <<EOF
+BEGIN VERIFICATION REQUEST
+$SERVERDIR
+cvsuser
+Ah<Z:yZZ30 e
+END VERIFICATION REQUEST
+EOF
+
+test_expect_success 'pserver authentication' \
+ 'cat request-anonymous | git-cvsserver pserver >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"'
+
+test_expect_success 'pserver authentication failure (non-anonymous user)' \
+ 'if cat request-git | git-cvsserver pserver >log 2>&1
+ then
+ false
+ else
+ true
+ fi &&
+ sed -ne \$p log | grep "^I HATE YOU\$"'
+
+test_expect_success 'pserver authentication success (non-anonymous user with password)' \
+ 'cat login-git-ok | git-cvsserver pserver >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"'
+
+test_expect_success 'pserver authentication (login)' \
+ 'cat login-anonymous | git-cvsserver pserver >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"'
+
+test_expect_success 'pserver authentication failure (login/non-anonymous user)' \
+ 'if cat login-git | git-cvsserver pserver >log 2>&1
+ then
+ false
+ else
+ true
+ fi &&
+ sed -ne \$p log | grep "^I HATE YOU\$"'
+
+
+# misuse pserver authentication for testing of req_Root
+
+cat >request-relative <<EOF
+BEGIN AUTH REQUEST
+gitcvs.git
+anonymous
+
+END AUTH REQUEST
+EOF
+
+cat >request-conflict <<EOF
+BEGIN AUTH REQUEST
+$SERVERDIR
+anonymous
+
+END AUTH REQUEST
+Root $WORKDIR
+EOF
+
+test_expect_success 'req_Root failure (relative pathname)' \
+ 'if cat request-relative | git-cvsserver pserver >log 2>&1
+ then
+ echo unexpected success
+ false
+ else
+ true
+ fi &&
+ tail log | grep "^error 1 Root must be an absolute pathname$"'
+
+test_expect_success 'req_Root failure (conflicting roots)' \
+ 'cat request-conflict | git-cvsserver pserver >log 2>&1 &&
+ tail log | grep "^error 1 Conflicting roots specified$"'
+
+test_expect_success 'req_Root (strict paths)' \
+ 'cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"'
+
+test_expect_success 'req_Root failure (strict-paths)' '
+ ! cat request-anonymous |
+ git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
+'
+
+test_expect_success 'req_Root (w/o strict-paths)' \
+ 'cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"'
+
+test_expect_success 'req_Root failure (w/o strict-paths)' '
+ ! cat request-anonymous |
+ git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
+'
+
+cat >request-base <<EOF
+BEGIN AUTH REQUEST
+/gitcvs.git
+anonymous
+
+END AUTH REQUEST
+Root /gitcvs.git
+EOF
+
+test_expect_success 'req_Root (base-path)' \
+ 'cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"'
+
+test_expect_success 'req_Root failure (base-path)' '
+ ! cat request-anonymous |
+ git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
+'
+
+GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
+
+test_expect_success 'req_Root (export-all)' \
+ 'cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"'
+
+test_expect_success 'req_Root failure (export-all w/o whitelist)' \
+ '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
+
+test_expect_success 'req_Root (everything together)' \
+ 'cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
+ sed -ne \$p log | grep "^I LOVE YOU\$"'
+
+GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
+
+#--------------
+# CONFIG TESTS
+#--------------
+
+test_expect_success 'gitcvs.enabled = false' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
+ if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1
+ then
+ echo unexpected cvs success
+ false
+ else
+ true
+ fi &&
+ grep "GITCVS emulation disabled" cvs.log &&
+ test ! -d cvswork2'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.ext.enabled = true' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+ test_cmp cvswork cvswork2'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.ext.enabled = false' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled false &&
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+ if GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1
+ then
+ echo unexpected cvs success
+ false
+ else
+ true
+ fi &&
+ grep "GITCVS emulation disabled" cvs.log &&
+ test ! -d cvswork2'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.dbname' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs.%a.%m.sqlite &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+ test_cmp cvswork cvswork2 &&
+ test -f "$SERVERDIR/gitcvs.ext.master.sqlite" &&
+ cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs.ext.master.sqlite"'
+
+rm -fr cvswork2
+test_expect_success 'gitcvs.ext.dbname' \
+ 'GIT_DIR="$SERVERDIR" git config --bool gitcvs.ext.enabled true &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.ext.dbname %Ggitcvs1.%a.%m.sqlite &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.dbname %Ggitcvs2.%a.%m.sqlite &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+ test_cmp cvswork cvswork2 &&
+ test -f "$SERVERDIR/gitcvs1.ext.master.sqlite" &&
+ test ! -f "$SERVERDIR/gitcvs2.ext.master.sqlite" &&
+ cmp "$SERVERDIR/gitcvs.master.sqlite" "$SERVERDIR/gitcvs1.ext.master.sqlite"'
+
+
+#------------
+# CVS UPDATE
+#------------
+
+rm -fr "$SERVERDIR"
+cd "$WORKDIR" &&
+git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log" ||
+exit 1
+
+test_expect_success 'cvs update (create new file)' \
+ 'echo testfile1 >testfile1 &&
+ git add testfile1 &&
+ git commit -q -m "Add testfile1" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.1/" &&
+ test_cmp testfile1 ../testfile1'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (update existing file)' \
+ 'echo line 2 >>testfile1 &&
+ git add testfile1 &&
+ git commit -q -m "Append to testfile1" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.2/" &&
+ test_cmp testfile1 ../testfile1'
+
+cd "$WORKDIR"
+#TODO: cvsserver doesn't support update w/o -d
+test_expect_failure "cvs update w/o -d doesn't create subdir (TODO)" '
+ mkdir test &&
+ echo >test/empty &&
+ git add test &&
+ git commit -q -m "Single Subdirectory" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test ! -d test
+'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (subdirectories)' \
+ '(for dir in A A/B A/B/C A/D E; do
+ mkdir $dir &&
+ echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")" &&
+ git add $dir;
+ done) &&
+ git commit -q -m "deep sub directory structure" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update -d &&
+ (for dir in A A/B A/B/C A/D E; do
+ filename="file_in_$(echo $dir|sed -e "s#/# #g")" &&
+ if test "$(echo $(grep -v ^D $dir/CVS/Entries|cut -d/ -f2,3,5))" = "$filename/1.1/" &&
+ test_cmp "$dir/$filename" "../$dir/$filename"; then
+ :
+ else
+ echo >failure
+ fi
+ done) &&
+ test ! -f failure'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (delete file)' \
+ 'git rm testfile1 &&
+ git commit -q -m "Remove testfile1" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test -z "$(grep testfile1 CVS/Entries)" &&
+ test ! -f testfile1'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (re-add deleted file)' \
+ 'echo readded testfile >testfile1 &&
+ git add testfile1 &&
+ git commit -q -m "Re-Add testfile1" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test "$(echo $(grep testfile1 CVS/Entries|cut -d/ -f2,3,5))" = "testfile1/1.4/" &&
+ test_cmp testfile1 ../testfile1'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (merge)' \
+ 'echo Line 0 >expected &&
+ for i in 1 2 3 4 5 6 7
+ do
+ echo Line $i >>merge
+ echo Line $i >>expected
+ done &&
+ echo Line 8 >>expected &&
+ git add merge &&
+ git commit -q -m "Merge test (pre-merge)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
+ test_cmp merge ../merge &&
+ ( echo Line 0; cat merge ) >merge.tmp &&
+ mv merge.tmp merge &&
+ cd "$WORKDIR" &&
+ echo Line 8 >>merge &&
+ git add merge &&
+ git commit -q -m "Merge test (merge)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ sleep 1 && touch merge &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test_cmp merge ../expected'
+
+cd "$WORKDIR"
+
+cat >expected.C <<EOF
+<<<<<<< merge.mine
+Line 0
+=======
+LINE 0
+>>>>>>> merge.3
+EOF
+
+for i in 1 2 3 4 5 6 7 8
+do
+ echo Line $i >>expected.C
+done
+
+test_expect_success 'cvs update (conflict merge)' \
+ '( echo LINE 0; cat merge ) >merge.tmp &&
+ mv merge.tmp merge &&
+ git add merge &&
+ git commit -q -m "Merge test (conflict)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test_cmp merge ../expected.C'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (-C)' \
+ 'cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update -C &&
+ test_cmp merge ../merge'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (merge no-op)' \
+ 'echo Line 9 >>merge &&
+ cp merge cvswork/merge &&
+ git add merge &&
+ git commit -q -m "Merge test (no-op)" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ sleep 1 && touch merge &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ test_cmp merge ../merge'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (-p)' '
+ touch really-empty &&
+ echo Line 1 > no-lf &&
+ printf "Line 2" >> no-lf &&
+ git add really-empty no-lf &&
+ git commit -q -m "Update -p test" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs update &&
+ rm -f failures &&
+ for i in merge no-lf empty really-empty; do
+ GIT_CONFIG="$git_config" cvs update -p "$i" >$i.out
+ test_cmp $i.out ../$i >>failures 2>&1
+ done &&
+ test -z "$(cat failures)"
+'
+
+cd "$WORKDIR"
+test_expect_success 'cvs update (module list supports packed refs)' '
+ GIT_DIR="$SERVERDIR" git pack-refs --all &&
+ GIT_CONFIG="$git_config" cvs -n up -d 2> out &&
+ grep "cvs update: New directory \`master'\''" < out
+'
+
+#------------
+# CVS STATUS
+#------------
+
+cd "$WORKDIR"
+test_expect_success 'cvs status' '
+ mkdir status.dir &&
+ echo Line > status.dir/status.file &&
+ echo Line > status.file &&
+ git add status.dir status.file &&
+ git commit -q -m "Status test" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs update &&
+ GIT_CONFIG="$git_config" cvs status | grep "^File: status.file" >../out &&
+ test $(wc -l <../out) = 2
+'
+
+cd "$WORKDIR"
+test_expect_success 'cvs status (nonrecursive)' '
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs status -l | grep "^File: status.file" >../out &&
+ test $(wc -l <../out) = 1
+'
+
+cd "$WORKDIR"
+test_expect_success 'cvs status (no subdirs in header)' '
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs status | grep ^File: >../out &&
+ ! grep / <../out
+'
+
+#------------
+# CVS CHECKOUT
+#------------
+
+cd "$WORKDIR"
+test_expect_success 'cvs co -c (shows module database)' '
+ GIT_CONFIG="$git_config" cvs co -c > out &&
+ grep "^master[ ]\+master$" < out &&
+ ! grep -v "^master[ ]\+master$" < out
+'
+
+#------------
+# CVS ANNOTATE
+#------------
+
+cd "$WORKDIR"
+test_expect_success 'cvs annotate' '
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs annotate merge >../out &&
+ sed -e "s/ .*//" ../out >../actual &&
+ for i in 3 1 1 1 1 1 1 1 2 4; do echo 1.$i; done >../expect &&
+ test_cmp ../expect ../actual
+'
+
+test_done
diff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh
new file mode 100755
index 0000000..925bd0f
--- /dev/null
+++ b/t/t9401-git-cvsserver-crlf.sh
@@ -0,0 +1,332 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Matthew Ogilvie
+# Parts adapted from other tests.
+#
+
+test_description='git-cvsserver -kb modes
+
+tests -kb mode for binary files when accessing a git
+repository using cvs CLI client via git-cvsserver server'
+
+. ./test-lib.sh
+
+marked_as () {
+ foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
+ if [ x"$foundEntry" = x"" ] ; then
+ echo "NOT FOUND: $1 $2 1 $3" >> "${WORKDIR}/marked.log"
+ return 1
+ fi
+ test x"$(grep "^/$2/" "$1/CVS/Entries" | cut -d/ -f5)" = x"$3"
+ stat=$?
+ echo "$1 $2 $stat '$3'" >> "${WORKDIR}/marked.log"
+ return $stat
+}
+
+not_present() {
+ foundEntry="$(grep "^/$2/" "$1/CVS/Entries")"
+ if [ -r "$1/$2" ] ; then
+ echo "Error: File still exists: $1 $2" >> "${WORKDIR}/marked.log"
+ return 1;
+ fi
+ if [ x"$foundEntry" != x"" ] ; then
+ echo "Error: should not have found: $1 $2" >> "${WORKDIR}/marked.log"
+ return 1;
+ else
+ echo "Correctly not found: $1 $2" >> "${WORKDIR}/marked.log"
+ return 0;
+ fi
+}
+
+cvs >/dev/null 2>&1
+if test $? -ne 1
+then
+ skip_all='skipping git-cvsserver tests, cvs not found'
+ test_done
+fi
+if ! test_have_prereq PERL
+then
+ skip_all='skipping git-cvsserver tests, perl not available'
+ test_done
+fi
+"$PERL_PATH" -e 'use DBI; use DBD::SQLite' >/dev/null 2>&1 || {
+ skip_all='skipping git-cvsserver tests, Perl SQLite interface unavailable'
+ test_done
+}
+
+unset GIT_DIR GIT_CONFIG
+WORKDIR=$(pwd)
+SERVERDIR=$(pwd)/gitcvs.git
+git_config="$SERVERDIR/config"
+CVSROOT=":fork:$SERVERDIR"
+CVSWORK="$(pwd)/cvswork"
+CVS_SERVER=git-cvsserver
+export CVSROOT CVS_SERVER
+
+rm -rf "$CVSWORK" "$SERVERDIR"
+test_expect_success 'setup' '
+ echo "Simple text file" >textfile.c &&
+ echo "File with embedded NUL: Q <- there" | q_to_nul > binfile.bin &&
+ mkdir subdir &&
+ echo "Another text file" > subdir/file.h &&
+ echo "Another binary: Q (this time CR)" | q_to_cr > subdir/withCr.bin &&
+ echo "Mixed up NUL, but marked text: Q <- there" | q_to_nul > mixedUp.c
+ echo "Unspecified" > subdir/unspecified.other &&
+ echo "/*.bin -crlf" > .gitattributes &&
+ echo "/*.c crlf" >> .gitattributes &&
+ echo "subdir/*.bin -crlf" >> .gitattributes &&
+ echo "subdir/*.c crlf" >> .gitattributes &&
+ echo "subdir/file.h crlf" >> .gitattributes &&
+ git add .gitattributes textfile.c binfile.bin mixedUp.c subdir/* &&
+ git commit -q -m "First Commit" &&
+ git clone -q --bare "$WORKDIR/.git" "$SERVERDIR" >/dev/null 2>&1 &&
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true &&
+ GIT_DIR="$SERVERDIR" git config gitcvs.logfile "$SERVERDIR/gitcvs.log"
+'
+
+test_expect_success 'cvs co (default crlf)' '
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+ test x"$(grep '/-k' cvswork/CVS/Entries cvswork/subdir/CVS/Entries)" = x""
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co (allbinary)' '
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary true &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+ marked_as cvswork textfile.c -kb &&
+ marked_as cvswork binfile.bin -kb &&
+ marked_as cvswork .gitattributes -kb &&
+ marked_as cvswork mixedUp.c -kb &&
+ marked_as cvswork/subdir withCr.bin -kb &&
+ marked_as cvswork/subdir file.h -kb &&
+ marked_as cvswork/subdir unspecified.other -kb
+'
+
+rm -rf cvswork cvs.log
+test_expect_success 'cvs co (use attributes/allbinary)' '
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr true &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+ marked_as cvswork textfile.c "" &&
+ marked_as cvswork binfile.bin -kb &&
+ marked_as cvswork .gitattributes -kb &&
+ marked_as cvswork mixedUp.c "" &&
+ marked_as cvswork/subdir withCr.bin -kb &&
+ marked_as cvswork/subdir file.h "" &&
+ marked_as cvswork/subdir unspecified.other -kb
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co (use attributes)' '
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.allbinary false &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+ marked_as cvswork textfile.c "" &&
+ marked_as cvswork binfile.bin -kb &&
+ marked_as cvswork .gitattributes "" &&
+ marked_as cvswork mixedUp.c "" &&
+ marked_as cvswork/subdir withCr.bin -kb &&
+ marked_as cvswork/subdir file.h "" &&
+ marked_as cvswork/subdir unspecified.other ""
+'
+
+test_expect_success 'adding files' '
+ cd cvswork/subdir &&
+ echo "more text" > src.c &&
+ GIT_CONFIG="$git_config" cvs -Q add src.c >cvs.log 2>&1 &&
+ marked_as . src.c "" &&
+ echo "psuedo-binary" > temp.bin &&
+ cd .. &&
+ GIT_CONFIG="$git_config" cvs -Q add subdir/temp.bin >cvs.log 2>&1 &&
+ marked_as subdir temp.bin "-kb" &&
+ cd subdir &&
+ GIT_CONFIG="$git_config" cvs -Q ci -m "adding files" >cvs.log 2>&1 &&
+ marked_as . temp.bin "-kb" &&
+ marked_as . src.c ""
+'
+
+cd "$WORKDIR"
+test_expect_success 'updating' '
+ git pull gitcvs.git &&
+ echo 'hi' > subdir/newfile.bin &&
+ echo 'junk' > subdir/file.h &&
+ echo 'hi' > subdir/newfile.c &&
+ echo 'hello' >> binfile.bin &&
+ git add subdir/newfile.bin subdir/file.h subdir/newfile.c binfile.bin &&
+ git commit -q -m "Add and change some files" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ cd .. &&
+ marked_as cvswork textfile.c "" &&
+ marked_as cvswork binfile.bin -kb &&
+ marked_as cvswork .gitattributes "" &&
+ marked_as cvswork mixedUp.c "" &&
+ marked_as cvswork/subdir withCr.bin -kb &&
+ marked_as cvswork/subdir file.h "" &&
+ marked_as cvswork/subdir unspecified.other "" &&
+ marked_as cvswork/subdir newfile.bin -kb &&
+ marked_as cvswork/subdir newfile.c "" &&
+ echo "File with embedded NUL: Q <- there" | q_to_nul > tmpExpect1 &&
+ echo "hello" >> tmpExpect1 &&
+ cmp cvswork/binfile.bin tmpExpect1
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co (use attributes/guess)' '
+ GIT_DIR="$SERVERDIR" git config gitcvs.allbinary guess &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+ marked_as cvswork textfile.c "" &&
+ marked_as cvswork binfile.bin -kb &&
+ marked_as cvswork .gitattributes "" &&
+ marked_as cvswork mixedUp.c "" &&
+ marked_as cvswork/subdir withCr.bin -kb &&
+ marked_as cvswork/subdir file.h "" &&
+ marked_as cvswork/subdir unspecified.other "" &&
+ marked_as cvswork/subdir newfile.bin -kb &&
+ marked_as cvswork/subdir newfile.c ""
+'
+
+test_expect_success 'setup multi-line files' '
+ ( echo "line 1" &&
+ echo "line 2" &&
+ echo "line 3" &&
+ echo "line 4 with NUL: Q <-" ) | q_to_nul > multiline.c &&
+ git add multiline.c &&
+ ( echo "line 1" &&
+ echo "line 2" &&
+ echo "line 3" &&
+ echo "line 4" ) | q_to_nul > multilineTxt.c &&
+ git add multilineTxt.c &&
+ git commit -q -m "multiline files" &&
+ git push gitcvs.git >/dev/null
+'
+
+rm -rf cvswork
+test_expect_success 'cvs co (guess)' '
+ GIT_DIR="$SERVERDIR" git config --bool gitcvs.usecrlfattr false &&
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork master >cvs.log 2>&1 &&
+ marked_as cvswork textfile.c "" &&
+ marked_as cvswork binfile.bin -kb &&
+ marked_as cvswork .gitattributes "" &&
+ marked_as cvswork mixedUp.c -kb &&
+ marked_as cvswork multiline.c -kb &&
+ marked_as cvswork multilineTxt.c "" &&
+ marked_as cvswork/subdir withCr.bin -kb &&
+ marked_as cvswork/subdir file.h "" &&
+ marked_as cvswork/subdir unspecified.other "" &&
+ marked_as cvswork/subdir newfile.bin "" &&
+ marked_as cvswork/subdir newfile.c ""
+'
+
+test_expect_success 'cvs co another copy (guess)' '
+ GIT_CONFIG="$git_config" cvs -Q co -d cvswork2 master >cvs.log 2>&1 &&
+ marked_as cvswork2 textfile.c "" &&
+ marked_as cvswork2 binfile.bin -kb &&
+ marked_as cvswork2 .gitattributes "" &&
+ marked_as cvswork2 mixedUp.c -kb &&
+ marked_as cvswork2 multiline.c -kb &&
+ marked_as cvswork2 multilineTxt.c "" &&
+ marked_as cvswork2/subdir withCr.bin -kb &&
+ marked_as cvswork2/subdir file.h "" &&
+ marked_as cvswork2/subdir unspecified.other "" &&
+ marked_as cvswork2/subdir newfile.bin "" &&
+ marked_as cvswork2/subdir newfile.c ""
+'
+
+test_expect_success 'add text (guess)' '
+ cd cvswork &&
+ echo "simpleText" > simpleText.c &&
+ GIT_CONFIG="$git_config" cvs -Q add simpleText.c &&
+ cd .. &&
+ marked_as cvswork simpleText.c ""
+'
+
+test_expect_success 'add bin (guess)' '
+ cd cvswork &&
+ echo "simpleBin: NUL: Q <- there" | q_to_nul > simpleBin.bin &&
+ GIT_CONFIG="$git_config" cvs -Q add simpleBin.bin &&
+ cd .. &&
+ marked_as cvswork simpleBin.bin -kb
+'
+
+test_expect_success 'remove files (guess)' '
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q rm -f subdir/file.h &&
+ cd subdir &&
+ GIT_CONFIG="$git_config" cvs -Q rm -f withCr.bin &&
+ cd ../.. &&
+ marked_as cvswork/subdir withCr.bin -kb &&
+ marked_as cvswork/subdir file.h ""
+'
+
+test_expect_success 'cvs ci (guess)' '
+ cd cvswork &&
+ GIT_CONFIG="$git_config" cvs -Q ci -m "add/rm files" >cvs.log 2>&1 &&
+ cd .. &&
+ marked_as cvswork textfile.c "" &&
+ marked_as cvswork binfile.bin -kb &&
+ marked_as cvswork .gitattributes "" &&
+ marked_as cvswork mixedUp.c -kb &&
+ marked_as cvswork multiline.c -kb &&
+ marked_as cvswork multilineTxt.c "" &&
+ not_present cvswork/subdir withCr.bin &&
+ not_present cvswork/subdir file.h &&
+ marked_as cvswork/subdir unspecified.other "" &&
+ marked_as cvswork/subdir newfile.bin "" &&
+ marked_as cvswork/subdir newfile.c "" &&
+ marked_as cvswork simpleBin.bin -kb &&
+ marked_as cvswork simpleText.c ""
+'
+
+test_expect_success 'update subdir of other copy (guess)' '
+ cd cvswork2/subdir &&
+ GIT_CONFIG="$git_config" cvs -Q update &&
+ cd ../.. &&
+ marked_as cvswork2 textfile.c "" &&
+ marked_as cvswork2 binfile.bin -kb &&
+ marked_as cvswork2 .gitattributes "" &&
+ marked_as cvswork2 mixedUp.c -kb &&
+ marked_as cvswork2 multiline.c -kb &&
+ marked_as cvswork2 multilineTxt.c "" &&
+ not_present cvswork2/subdir withCr.bin &&
+ not_present cvswork2/subdir file.h &&
+ marked_as cvswork2/subdir unspecified.other "" &&
+ marked_as cvswork2/subdir newfile.bin "" &&
+ marked_as cvswork2/subdir newfile.c "" &&
+ not_present cvswork2 simpleBin.bin &&
+ not_present cvswork2 simpleText.c
+'
+
+echo "starting update/merge" >> "${WORKDIR}/marked.log"
+test_expect_success 'update/merge full other copy (guess)' '
+ git pull gitcvs.git master &&
+ sed "s/3/replaced_3/" < multilineTxt.c > ml.temp &&
+ mv ml.temp multilineTxt.c &&
+ git add multilineTxt.c &&
+ git commit -q -m "modify multiline file" >> "${WORKDIR}/marked.log" &&
+ git push gitcvs.git >/dev/null &&
+ cd cvswork2 &&
+ sed "s/1/replaced_1/" < multilineTxt.c > ml.temp &&
+ mv ml.temp multilineTxt.c &&
+ GIT_CONFIG="$git_config" cvs update > cvs.log 2>&1 &&
+ cd .. &&
+ marked_as cvswork2 textfile.c "" &&
+ marked_as cvswork2 binfile.bin -kb &&
+ marked_as cvswork2 .gitattributes "" &&
+ marked_as cvswork2 mixedUp.c -kb &&
+ marked_as cvswork2 multiline.c -kb &&
+ marked_as cvswork2 multilineTxt.c "" &&
+ not_present cvswork2/subdir withCr.bin &&
+ not_present cvswork2/subdir file.h &&
+ marked_as cvswork2/subdir unspecified.other "" &&
+ marked_as cvswork2/subdir newfile.bin "" &&
+ marked_as cvswork2/subdir newfile.c "" &&
+ marked_as cvswork2 simpleBin.bin -kb &&
+ marked_as cvswork2 simpleText.c "" &&
+ echo "line replaced_1" > tmpExpect2 &&
+ echo "line 2" >> tmpExpect2 &&
+ echo "line replaced_3" >> tmpExpect2 &&
+ echo "line 4" | q_to_nul >> tmpExpect2 &&
+ cmp cvswork2/multilineTxt.c tmpExpect2
+'
+
+test_done
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
new file mode 100755
index 0000000..4f2b9b0
--- /dev/null
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -0,0 +1,679 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Jakub Narebski
+#
+
+test_description='gitweb as standalone script (basic tests).
+
+This test runs gitweb (git web interface) as CGI script from
+commandline, and checks that it would not write any errors
+or warnings to log.'
+
+
+. ./gitweb-lib.sh
+
+# ----------------------------------------------------------------------
+# no commits (empty, just initialized repository)
+
+test_expect_success \
+ 'no commits: projects_list (implicit)' \
+ 'gitweb_run'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: projects_index' \
+ 'gitweb_run "a=project_index"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git summary (implicit)' \
+ 'gitweb_run "p=.git"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git commit (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git commitdiff (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git tree (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git heads' \
+ 'gitweb_run "p=.git;a=heads"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'no commits: .git tags' \
+ 'gitweb_run "p=.git;a=tags"'
+test_debug 'cat gitweb.log'
+
+
+# ----------------------------------------------------------------------
+# initial commit
+
+test_expect_success \
+ 'Make initial commit' \
+ 'echo "Not an empty file." > file &&
+ git add file &&
+ git commit -a -m "Initial commit." &&
+ git branch b'
+
+test_expect_success \
+ 'projects_list (implicit)' \
+ 'gitweb_run'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'projects_index' \
+ 'gitweb_run "a=project_index"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git summary (implicit)' \
+ 'gitweb_run "p=.git"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commit (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commitdiff (implicit HEAD, root commit)' \
+ 'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commitdiff_plain (implicit HEAD, root commit)' \
+ 'gitweb_run "p=.git;a=commitdiff_plain"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commit (HEAD)' \
+ 'gitweb_run "p=.git;a=commit;h=HEAD"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git tree (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git blob (file)' \
+ 'gitweb_run "p=.git;a=blob;f=file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git blob_plain (file)' \
+ 'gitweb_run "p=.git;a=blob_plain;f=file"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# nonexistent objects
+
+test_expect_success \
+ '.git commit (non-existent)' \
+ 'gitweb_run "p=.git;a=commit;h=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commitdiff (non-existent)' \
+ 'gitweb_run "p=.git;a=commitdiff;h=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git commitdiff (non-existent vs HEAD)' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=non-existent;h=HEAD"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git tree (0000000000000000000000000000000000000000)' \
+ 'gitweb_run "p=.git;a=tree;h=0000000000000000000000000000000000000000"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git tag (0000000000000000000000000000000000000000)' \
+ 'gitweb_run "p=.git;a=tag;h=0000000000000000000000000000000000000000"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git blob (non-existent)' \
+ 'gitweb_run "p=.git;a=blob;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ '.git blob_plain (non-existent)' \
+ 'gitweb_run "p=.git;a=blob_plain;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+
+# ----------------------------------------------------------------------
+# commitdiff testing (implicit, one implicit tree-ish)
+
+test_expect_success \
+ 'commitdiff(0): root' \
+ 'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): file added' \
+ 'echo "New file" > new_file &&
+ git add new_file &&
+ git commit -a -m "File added." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): mode change' \
+ 'test_chmod +x new_file &&
+ git commit -a -m "Mode changed." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): file renamed' \
+ 'git mv new_file renamed_file &&
+ git commit -a -m "File renamed." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success SYMLINKS \
+ 'commitdiff(0): file to symlink' \
+ 'rm renamed_file &&
+ ln -s file renamed_file &&
+ git commit -a -m "File to symlink." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): file deleted' \
+ 'git rm renamed_file &&
+ rm -f renamed_file &&
+ git commit -a -m "File removed." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): file copied / new file' \
+ 'cp file file2 &&
+ git add file2 &&
+ git commit -a -m "File copied." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): mode change and modified' \
+ 'echo "New line" >> file2 &&
+ test_chmod +x file2 &&
+ git commit -a -m "Mode change and modification." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): renamed and modified' \
+ 'cat >file2<<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.
+EOF
+ git commit -a -m "File added." &&
+ git mv file2 file3 &&
+ echo "Propter nomen suum." >> file3 &&
+ git commit -a -m "File rename and modification." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): renamed, mode change and modified' \
+ 'git mv file3 file2 &&
+ echo "Propter nomen suum." >> file2 &&
+ test_chmod +x file2 &&
+ git commit -a -m "File rename, mode change and modification." &&
+ gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# commitdiff testing (taken from t4114-apply-typechange.sh)
+
+test_expect_success SYMLINKS 'setup typechange commits' '
+ echo "hello world" > foo &&
+ echo "hi planet" > bar &&
+ git update-index --add foo bar &&
+ git commit -m initial &&
+ git branch initial &&
+ rm -f foo &&
+ ln -s bar foo &&
+ git update-index foo &&
+ git commit -m "foo symlinked to bar" &&
+ git branch foo-symlinked-to-bar &&
+ rm -f foo &&
+ echo "how far is the sun?" > foo &&
+ git update-index foo &&
+ git commit -m "foo back to file" &&
+ git branch foo-back-to-file &&
+ rm -f foo &&
+ git update-index --remove foo &&
+ mkdir foo &&
+ echo "if only I knew" > foo/baz &&
+ git update-index --add foo/baz &&
+ git commit -m "foo becomes a directory" &&
+ git branch "foo-becomes-a-directory" &&
+ echo "hello world" > foo/baz &&
+ git update-index foo/baz &&
+ git commit -m "foo/baz is the original foo" &&
+ git branch foo-baz-renamed-from-foo
+ '
+
+test_expect_success \
+ 'commitdiff(2): file renamed from foo to foo/baz' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-baz-renamed-from-foo"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): file renamed from foo/baz to foo' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-baz-renamed-from-foo;h=initial"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): directory becomes file' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=initial"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): file becomes directory' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-becomes-a-directory"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): file becomes symlink' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=initial;h=foo-symlinked-to-bar"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): symlink becomes file' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-back-to-file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): symlink becomes directory' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-symlinked-to-bar;h=foo-becomes-a-directory"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(2): directory becomes symlink' \
+ 'gitweb_run "p=.git;a=commitdiff;hp=foo-becomes-a-directory;h=foo-symlinked-to-bar"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# commit, commitdiff: merge, large
+test_expect_success \
+ 'Create a merge' \
+ 'git checkout b &&
+ echo "Branch" >> b &&
+ git add b &&
+ git commit -a -m "On branch" &&
+ git checkout master &&
+ git pull . b'
+
+test_expect_success \
+ 'commit(0): merge commit' \
+ 'gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(0): merge commit' \
+ 'gitweb_run "p=.git;a=commitdiff"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'Prepare large commit' \
+ 'git checkout b &&
+ echo "To be changed" > 01-change &&
+ echo "To be renamed" > 02-pure-rename-from &&
+ echo "To be deleted" > 03-delete &&
+ echo "To be renamed and changed" > 04-rename-from &&
+ echo "To have mode changed" > 05-mode-change &&
+ echo "File to symlink" > 06-file-or-symlink &&
+ echo "To be changed and have mode changed" > 07-change-mode-change &&
+ git add 0* &&
+ git commit -a -m "Prepare large commit" &&
+ echo "Changed" > 01-change &&
+ git mv 02-pure-rename-from 02-pure-rename-to &&
+ git rm 03-delete && rm -f 03-delete &&
+ echo "A new file" > 03-new &&
+ git add 03-new &&
+ git mv 04-rename-from 04-rename-to &&
+ echo "Changed" >> 04-rename-to &&
+ test_chmod +x 05-mode-change &&
+ rm -f 06-file-or-symlink &&
+ if test_have_prereq SYMLINKS; then
+ ln -s 01-change 06-file-or-symlink
+ else
+ printf %s 01-change > 06-file-or-symlink
+ fi &&
+ echo "Changed and have mode changed" > 07-change-mode-change &&
+ test_chmod +x 07-change-mode-change &&
+ git commit -a -m "Large commit" &&
+ git checkout master'
+
+test_expect_success \
+ 'commit(1): large commit' \
+ 'gitweb_run "p=.git;a=commit;h=b"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'commitdiff(1): large commit' \
+ 'gitweb_run "p=.git;a=commitdiff;h=b"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# tags testing
+
+test_expect_success \
+ 'tags: list of different types of tags' \
+ 'git checkout master &&
+ git tag -a -m "Tag commit object" tag-commit HEAD &&
+ git tag -a -m "" tag-commit-nomessage HEAD &&
+ git tag -a -m "Tag tag object" tag-tag tag-commit &&
+ git tag -a -m "Tag tree object" tag-tree HEAD^{tree} &&
+ git tag -a -m "Tag blob object" tag-blob HEAD:file &&
+ git tag lightweight/tag-commit HEAD &&
+ git tag lightweight/tag-tag tag-commit &&
+ git tag lightweight/tag-tree HEAD^{tree} &&
+ git tag lightweight/tag-blob HEAD:file &&
+ gitweb_run "p=.git;a=tags"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'tag: Tag to commit object' \
+ 'gitweb_run "p=.git;a=tag;h=tag-commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'tag: on lightweight tag (invalid)' \
+ 'gitweb_run "p=.git;a=tag;h=lightweight/tag-commit"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# logs
+
+test_expect_success \
+ 'logs: log (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=log"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'logs: shortlog (implicit HEAD)' \
+ 'gitweb_run "p=.git;a=shortlog"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'logs: history (implicit HEAD, file)' \
+ 'gitweb_run "p=.git;a=history;f=file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'logs: history (implicit HEAD, non-existent file)' \
+ 'gitweb_run "p=.git;a=history;f=non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'logs: history (implicit HEAD, deleted file)' \
+ 'git checkout master &&
+ echo "to be deleted" > deleted_file &&
+ git add deleted_file &&
+ git commit -m "Add file to be deleted" &&
+ git rm deleted_file &&
+ git commit -m "Delete file" &&
+ gitweb_run "p=.git;a=history;f=deleted_file"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# path_info links
+test_expect_success \
+ 'path_info: project' \
+ 'gitweb_run "" "/.git"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'path_info: project/branch' \
+ 'gitweb_run "" "/.git/b"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'path_info: project/branch:file' \
+ 'gitweb_run "" "/.git/master:file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'path_info: project/branch:dir/' \
+ 'gitweb_run "" "/.git/master:foo/"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'path_info: project/branch:file (non-existent)' \
+ 'gitweb_run "" "/.git/master:non-existent"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'path_info: project/branch:dir/ (non-existent)' \
+ 'gitweb_run "" "/.git/master:non-existent/"'
+test_debug 'cat gitweb.log'
+
+
+test_expect_success \
+ 'path_info: project/branch:/file' \
+ 'gitweb_run "" "/.git/master:/file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'path_info: project/:/file (implicit HEAD)' \
+ 'gitweb_run "" "/.git/:/file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'path_info: project/:/ (implicit HEAD, top tree)' \
+ 'gitweb_run "" "/.git/:/"'
+test_debug 'cat gitweb.log'
+
+
+# ----------------------------------------------------------------------
+# feed generation
+
+test_expect_success \
+ 'feeds: OPML' \
+ 'gitweb_run "a=opml"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'feed: RSS' \
+ 'gitweb_run "p=.git;a=rss"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'feed: Atom' \
+ 'gitweb_run "p=.git;a=atom"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# encoding/decoding
+
+test_expect_success \
+ 'encode(commit): utf8' \
+ '. "$TEST_DIRECTORY"/t3901-utf8.txt &&
+ echo "UTF-8" >> file &&
+ git add file &&
+ git commit -F "$TEST_DIRECTORY"/t3900/1-UTF-8.txt &&
+ gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'encode(commit): iso-8859-1' \
+ '. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+ echo "ISO-8859-1" >> file &&
+ git add file &&
+ git config i18n.commitencoding ISO-8859-1 &&
+ git commit -F "$TEST_DIRECTORY"/t3900/ISO8859-1.txt &&
+ git config --unset i18n.commitencoding &&
+ gitweb_run "p=.git;a=commit"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'encode(log): utf-8 and iso-8859-1' \
+ 'gitweb_run "p=.git;a=log"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# extra options
+
+test_expect_success \
+ 'opt: log --no-merges' \
+ 'gitweb_run "p=.git;a=log;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'opt: atom --no-merges' \
+ 'gitweb_run "p=.git;a=log;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'opt: "file" history --no-merges' \
+ 'gitweb_run "p=.git;a=history;f=file;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'opt: log --no-such-option (invalid option)' \
+ 'gitweb_run "p=.git;a=log;opt=--no-such-option"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'opt: tree --no-merges (invalid option for action)' \
+ 'gitweb_run "p=.git;a=tree;opt=--no-merges"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# testing config_to_multi / cloneurl
+
+test_expect_success \
+ 'URL: no project URLs, no base URL' \
+ 'gitweb_run "p=.git;a=summary"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'URL: project URLs via gitweb.url' \
+ 'git config --add gitweb.url git://example.com/git/trash.git &&
+ git config --add gitweb.url http://example.com/git/trash.git &&
+ gitweb_run "p=.git;a=summary"'
+test_debug 'cat gitweb.log'
+
+cat >.git/cloneurl <<\EOF
+git://example.com/git/trash.git
+http://example.com/git/trash.git
+EOF
+
+test_expect_success \
+ 'URL: project URLs via cloneurl file' \
+ 'gitweb_run "p=.git;a=summary"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# gitweb config and repo config
+
+cat >>gitweb_config.perl <<\EOF
+
+# turn on override for each overridable feature
+foreach my $key (keys %feature) {
+ if ($feature{$key}{'sub'}) {
+ $feature{$key}{'override'} = 1;
+ }
+}
+EOF
+
+test_expect_success \
+ 'config override: projects list (implicit)' \
+ 'gitweb_run'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'config override: tree view, features not overridden in repo config' \
+ 'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'config override: tree view, features disabled in repo config' \
+ 'git config gitweb.blame no &&
+ git config gitweb.snapshot none &&
+ git config gitweb.avatar gravatar &&
+ gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+test_expect_success \
+ 'config override: tree view, features enabled in repo config (1)' \
+ 'git config gitweb.blame yes &&
+ git config gitweb.snapshot "zip,tgz, tbz2" &&
+ gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+cat >.git/config <<\EOF
+# testing noval and alternate separator
+[gitweb]
+ blame
+ snapshot = zip tgz
+EOF
+test_expect_success \
+ 'config override: tree view, features enabled in repo config (2)' \
+ 'gitweb_run "p=.git;a=tree"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# non-ASCII in README.html
+
+test_expect_success \
+ 'README.html with non-ASCII characters (utf-8)' \
+ 'echo "<b>UTF-8 example:</b><br />" > .git/README.html &&
+ cat "$TEST_DIRECTORY"/t3900/1-UTF-8.txt >> .git/README.html &&
+ gitweb_run "p=.git;a=summary"'
+test_debug 'cat gitweb.log'
+
+# ----------------------------------------------------------------------
+# syntax highlighting
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'highlight'}{'override'} = 1;
+EOF
+
+highlight --version >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+ say "Skipping syntax highlighting test, because 'highlight' was not found"
+else
+ test_set_prereq HIGHLIGHT
+fi
+
+test_expect_success HIGHLIGHT \
+ 'syntax highlighting (no highlight)' \
+ 'git config gitweb.highlight yes &&
+ gitweb_run "p=.git;a=blob;f=file"'
+test_debug 'cat gitweb.log'
+
+test_expect_success HIGHLIGHT \
+ 'syntax highlighting (highlighted)' \
+ 'git config gitweb.highlight yes &&
+ echo "#!/usr/bin/sh" > test.sh &&
+ git add test.sh &&
+ git commit -m "Add test.sh" &&
+ gitweb_run "p=.git;a=blob;f=test.sh"'
+test_debug 'cat gitweb.log'
+
+test_done
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
new file mode 100755
index 0000000..2487da1
--- /dev/null
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -0,0 +1,138 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Mark Rada
+#
+
+test_description='gitweb as standalone script (http status tests).
+
+This test runs gitweb (git web interface) as a CGI script from the
+commandline, and checks that it returns the expected HTTP status
+code and message.'
+
+
+. ./gitweb-lib.sh
+
+# ----------------------------------------------------------------------
+# snapshot settings
+
+test_expect_success 'setup' "
+ test_commit 'SnapshotTests' 'i can has snapshot?'
+"
+
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'override'} = 0;
+EOF
+
+test_expect_success \
+ 'snapshots: tgz only default format enabled' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+ grep "403 - Unsupported snapshot format" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+ grep "403 - Snapshot format not allowed" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+ grep "403 - Unsupported snapshot format" gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'default'} = ['tgz','tbz2','txz','zip'];
+EOF
+
+test_expect_success \
+ 'snapshots: all enabled in default, use default disabled value' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+ grep "Status: 200 OK" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+ grep "403 - Snapshot format not allowed" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+ grep "Status: 200 OK" gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'zip'}{'disabled'} = 1;
+EOF
+
+test_expect_success \
+ 'snapshots: zip explicitly disabled' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+ grep "403 - Snapshot format not allowed" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'tgz'}{'disabled'} = 0;
+EOF
+
+test_expect_success \
+ 'snapshots: tgz explicitly enabled' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output'
+test_debug 'cat gitweb.headers'
+
+
+# ----------------------------------------------------------------------
+# snapshot hash ids
+
+test_expect_success 'snapshots: good tree-ish id' '
+ gitweb_run "p=.git;a=snapshot;h=master;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'snapshots: bad tree-ish id' '
+ gitweb_run "p=.git;a=snapshot;h=frizzumFrazzum;sf=tgz" &&
+ grep "404 - Object does not exist" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+test_expect_success 'snapshots: bad tree-ish id (tagged object)' '
+ echo object > tag-object &&
+ git add tag-object &&
+ git commit -m "Object to be tagged" &&
+ git tag tagged-object `git hash-object tag-object` &&
+ gitweb_run "p=.git;a=snapshot;h=tagged-object;sf=tgz" &&
+ grep "400 - Object is not a tree-ish" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+test_expect_success 'snapshots: good object id' '
+ ID=`git rev-parse --verify HEAD` &&
+ gitweb_run "p=.git;a=snapshot;h=$ID;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output
+'
+test_debug 'cat gitweb.headers'
+
+test_expect_success 'snapshots: bad object id' '
+ gitweb_run "p=.git;a=snapshot;h=abcdef01234;sf=tgz" &&
+ grep "404 - Object does not exist" gitweb.output
+'
+test_debug 'cat gitweb.output'
+
+
+# ----------------------------------------------------------------------
+# load checking
+
+# always hit the load limit
+cat >>gitweb_config.perl <<\EOF
+our $maxload = -1;
+EOF
+
+test_expect_success 'load checking: load too high (default action)' '
+ gitweb_run "p=.git" &&
+ grep "Status: 503 Service Unavailable" gitweb.headers &&
+ grep "503 - The load average on the server is too high" gitweb.body
+'
+test_debug 'cat gitweb.log' # just in case
+test_debug 'cat gitweb.headers'
+
+# turn off load checking
+cat >>gitweb_config.perl <<\EOF
+our $maxload = undef;
+EOF
+
+
+test_done
diff --git a/t/t9502-gitweb-standalone-parse-output.sh b/t/t9502-gitweb-standalone-parse-output.sh
new file mode 100755
index 0000000..dd83890
--- /dev/null
+++ b/t/t9502-gitweb-standalone-parse-output.sh
@@ -0,0 +1,115 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Mark Rada
+#
+
+test_description='gitweb as standalone script (parsing script output).
+
+This test runs gitweb (git web interface) as a CGI script from the
+commandline, and checks that it produces the correct output, either
+in the HTTP header or the actual script output.'
+
+
+. ./gitweb-lib.sh
+
+# ----------------------------------------------------------------------
+# snapshot file name and prefix
+
+cat >>gitweb_config.perl <<\EOF
+
+$known_snapshot_formats{'tar'} = {
+ 'display' => 'tar',
+ 'type' => 'application/x-tar',
+ 'suffix' => '.tar',
+ 'format' => 'tar',
+};
+
+$feature{'snapshot'}{'default'} = ['tar'];
+EOF
+
+# Call check_snapshot with the arguments "<basename> [<prefix>]"
+#
+# This will check that gitweb HTTP header contains proposed filename
+# as <basename> with '.tar' suffix added, and that generated tarfile
+# (gitweb message body) has <prefix> as prefix for al files in tarfile
+#
+# <prefix> default to <basename>
+check_snapshot () {
+ basename=$1
+ prefix=${2:-"$1"}
+ echo "basename=$basename"
+ grep "filename=.*$basename.tar" gitweb.headers >/dev/null 2>&1 &&
+ "$TAR" tf gitweb.body >file_list &&
+ ! grep -v "^$prefix/" file_list
+}
+
+test_expect_success setup '
+ test_commit first foo &&
+ git branch xx/test &&
+ FULL_ID=$(git rev-parse --verify HEAD) &&
+ SHORT_ID=$(git rev-parse --verify --short=7 HEAD)
+'
+test_debug '
+ echo "FULL_ID = $FULL_ID"
+ echo "SHORT_ID = $SHORT_ID"
+'
+
+test_expect_success 'snapshot: full sha1' '
+ gitweb_run "p=.git;a=snapshot;h=$FULL_ID;sf=tar" &&
+ check_snapshot ".git-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: shortened sha1' '
+ gitweb_run "p=.git;a=snapshot;h=$SHORT_ID;sf=tar" &&
+ check_snapshot ".git-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: almost full sha1' '
+ ID=$(git rev-parse --short=30 HEAD) &&
+ gitweb_run "p=.git;a=snapshot;h=$ID;sf=tar" &&
+ check_snapshot ".git-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: HEAD' '
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tar" &&
+ check_snapshot ".git-HEAD-$SHORT_ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: short branch name (master)' '
+ gitweb_run "p=.git;a=snapshot;h=master;sf=tar" &&
+ ID=$(git rev-parse --verify --short=7 master) &&
+ check_snapshot ".git-master-$ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: short tag name (first)' '
+ gitweb_run "p=.git;a=snapshot;h=first;sf=tar" &&
+ ID=$(git rev-parse --verify --short=7 first) &&
+ check_snapshot ".git-first-$ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: full branch name (refs/heads/master)' '
+ gitweb_run "p=.git;a=snapshot;h=refs/heads/master;sf=tar" &&
+ ID=$(git rev-parse --verify --short=7 master) &&
+ check_snapshot ".git-master-$ID"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: full tag name (refs/tags/first)' '
+ gitweb_run "p=.git;a=snapshot;h=refs/tags/first;sf=tar" &&
+ check_snapshot ".git-first"
+'
+test_debug 'cat gitweb.headers && cat file_list'
+
+test_expect_success 'snapshot: hierarchical branch name (xx/test)' '
+ gitweb_run "p=.git;a=snapshot;h=xx/test;sf=tar" &&
+ ! grep "filename=.*/" gitweb.headers
+'
+test_debug 'cat gitweb.headers'
+
+test_done
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
new file mode 100755
index 0000000..2eff9cd
--- /dev/null
+++ b/t/t9600-cvsimport.sh
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+test_description='git cvsimport basic tests'
+. ./lib-cvs.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping git cvsimport tests, perl not available'
+ test_done
+fi
+
+CVSROOT=$(pwd)/cvsroot
+export CVSROOT
+
+test_expect_success 'setup cvsroot' '$CVS init'
+
+test_expect_success 'setup a cvs module' '
+
+ mkdir "$CVSROOT/module" &&
+ $CVS co -d module-cvs module &&
+ cd module-cvs &&
+ cat <<EOF >o_fortuna &&
+O Fortuna
+velut luna
+statu variabilis,
+
+semper crescis
+aut decrescis;
+vita detestabilis
+
+nunc obdurat
+et tunc curat
+ludo mentis aciem,
+
+egestatem,
+potestatem
+dissolvit ut glaciem.
+EOF
+ $CVS add o_fortuna &&
+ cat <<EOF >message &&
+add "O Fortuna" lyrics
+
+These public domain lyrics make an excellent sample text.
+EOF
+ $CVS commit -F message &&
+ cd ..
+'
+
+test_expect_success 'import a trivial module' '
+
+ git cvsimport -a -R -z 0 -C module-git module &&
+ test_cmp module-cvs/o_fortuna module-git/o_fortuna
+
+'
+
+test_expect_success 'pack refs' 'cd module-git && git gc && cd ..'
+
+test_expect_success 'initial import has correct .git/cvs-revisions' '
+
+ (cd module-git &&
+ git log --format="o_fortuna 1.1 %H" -1) > expected &&
+ test_cmp expected module-git/.git/cvs-revisions
+'
+
+test_expect_success 'update cvs module' '
+
+ cd module-cvs &&
+ cat <<EOF >o_fortuna &&
+O Fortune,
+like the moon
+you are changeable,
+
+ever waxing
+and waning;
+hateful life
+
+first oppresses
+and then soothes
+as fancy takes it;
+
+poverty
+and power
+it melts them like ice.
+EOF
+ cat <<EOF >message &&
+translate to English
+
+My Latin is terrible.
+EOF
+ $CVS commit -F message &&
+ cd ..
+'
+
+test_expect_success 'update git module' '
+
+ cd module-git &&
+ git cvsimport -a -R -z 0 module &&
+ git merge origin &&
+ cd .. &&
+ test_cmp module-cvs/o_fortuna module-git/o_fortuna
+
+'
+
+test_expect_success 'update has correct .git/cvs-revisions' '
+
+ (cd module-git &&
+ git log --format="o_fortuna 1.1 %H" -1 HEAD^ &&
+ git log --format="o_fortuna 1.2 %H" -1 HEAD) > expected &&
+ test_cmp expected module-git/.git/cvs-revisions
+'
+
+test_expect_success 'update cvs module' '
+
+ cd module-cvs &&
+ echo 1 >tick &&
+ $CVS add tick &&
+ $CVS commit -m 1
+ cd ..
+
+'
+
+test_expect_success 'cvsimport.module config works' '
+
+ cd module-git &&
+ git config cvsimport.module module &&
+ git cvsimport -a -R -z0 &&
+ git merge origin &&
+ cd .. &&
+ test_cmp module-cvs/tick module-git/tick
+
+'
+
+test_expect_success 'second update has correct .git/cvs-revisions' '
+
+ (cd module-git &&
+ git log --format="o_fortuna 1.1 %H" -1 HEAD^^ &&
+ git log --format="o_fortuna 1.2 %H" -1 HEAD^
+ git log --format="tick 1.1 %H" -1 HEAD) > expected &&
+ test_cmp expected module-git/.git/cvs-revisions
+'
+
+test_expect_success 'import from a CVS working tree' '
+
+ $CVS co -d import-from-wt module &&
+ cd import-from-wt &&
+ git cvsimport -a -z0 &&
+ echo 1 >expect &&
+ git log -1 --pretty=format:%s%n >actual &&
+ test_cmp actual expect &&
+ cd ..
+
+'
+
+test_expect_success 'no .git/cvs-revisions created by default' '
+
+ ! test -e import-from-wt/.git/cvs-revisions
+
+'
+
+test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master'
+
+test_done
diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh
new file mode 100755
index 0000000..3afaf56
--- /dev/null
+++ b/t/t9601-cvsimport-vendor-branch.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+# Description of the files in the repository:
+#
+# imported-once.txt:
+#
+# Imported once. 1.1 and 1.1.1.1 should be identical.
+#
+# imported-twice.txt:
+#
+# Imported twice. HEAD should reflect the contents of the
+# second import (i.e., have the same contents as 1.1.1.2).
+#
+# imported-modified.txt:
+#
+# Imported, then modified on HEAD. HEAD should reflect the
+# modification.
+#
+# imported-modified-imported.txt:
+#
+# Imported, then modified on HEAD, then imported again.
+#
+# added-imported.txt,v:
+#
+# Added with 'cvs add' to create 1.1, then imported with
+# completely different contents to create 1.1.1.1, therefore the
+# vendor branch was never the default branch.
+#
+# imported-anonymously.txt:
+#
+# Like imported-twice.txt, but with a vendor branch whose branch
+# tag has been removed.
+
+test_description='git cvsimport handling of vendor branches'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9601/cvsroot
+export CVSROOT
+
+test_expect_success 'import a module with a vendor branch' '
+
+ git cvsimport -C module-git module
+
+'
+
+test_expect_success 'check HEAD out of cvs repository' 'test_cvs_co master'
+
+test_expect_success 'check master out of git repository' 'test_git_co master'
+
+test_expect_success 'check a file that was imported once' '
+
+ test_cmp_branch_file master imported-once.txt
+
+'
+
+test_expect_failure 'check a file that was imported twice' '
+
+ test_cmp_branch_file master imported-twice.txt
+
+'
+
+test_expect_success 'check a file that was imported then modified on HEAD' '
+
+ test_cmp_branch_file master imported-modified.txt
+
+'
+
+test_expect_success 'check a file that was imported, modified, then imported again' '
+
+ test_cmp_branch_file master imported-modified-imported.txt
+
+'
+
+test_expect_success 'check a file that was added to HEAD then imported' '
+
+ test_cmp_branch_file master added-imported.txt
+
+'
+
+test_expect_success 'a vendor branch whose tag has been removed' '
+
+ test_cmp_branch_file master imported-anonymously.txt
+
+'
+
+test_done
diff --git a/t/t9601/cvsroot/.gitattributes b/t/t9601/cvsroot/.gitattributes
new file mode 100644
index 0000000..562b12e
--- /dev/null
+++ b/t/t9601/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9601/cvsroot/CVSROOT/.gitignore b/t/t9601/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 0000000..3bb9b34
--- /dev/null
+++ b/t/t9601/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9601/cvsroot/module/added-imported.txt,v b/t/t9601/cvsroot/module/added-imported.txt,v
new file mode 100644
index 0000000..5f83072
--- /dev/null
+++ b/t/t9601/cvsroot/module/added-imported.txt,v
@@ -0,0 +1,44 @@
+head 1.1;
+access;
+symbols
+ vtag-4:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.15; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.16; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Add a file to the working copy.
+@
+text
+@Adding this file, before importing it with different contents.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-4).
+@
+text
+@d1 1
+a1 1
+This is vtag-4 (on vbranchA) of added-then-imported.txt.
+@
+
diff --git a/t/t9601/cvsroot/module/imported-anonymously.txt,v b/t/t9601/cvsroot/module/imported-anonymously.txt,v
new file mode 100644
index 0000000..55e1b0c
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-anonymously.txt,v
@@ -0,0 +1,42 @@
+head 1.1;
+branch 1.1.1;
+access;
+symbols
+ vtag-1:1.1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-anonymously.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-modified-imported.txt,v b/t/t9601/cvsroot/module/imported-modified-imported.txt,v
new file mode 100644
index 0000000..e5830ae
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-modified-imported.txt,v
@@ -0,0 +1,76 @@
+head 1.2;
+access;
+symbols
+ vtag-2:1.1.1.2
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2004.02.09.15.43.14; author kfogel; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next 1.1.1.2;
+
+1.1.1.2
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@First regular commit, to imported-modified-imported.txt, on HEAD.
+@
+text
+@This is a modification of imported-modified-imported.txt on HEAD.
+It should supersede the version from the vendor branch.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d1 2
+a2 1
+This is vtag-1 (on vbranchA) of imported-modified-imported.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
+1.1.1.2
+log
+@Import (vbranchA, vtag-2).
+@
+text
+@d1 1
+a1 1
+This is vtag-2 (on vbranchA) of imported-modified-imported.txt.
+@
+
+
diff --git a/t/t9601/cvsroot/module/imported-modified.txt,v b/t/t9601/cvsroot/module/imported-modified.txt,v
new file mode 100644
index 0000000..bbcfe44
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-modified.txt,v
@@ -0,0 +1,59 @@
+head 1.2;
+access;
+symbols
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2004.02.09.15.43.14; author kfogel; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Commit on HEAD.
+@
+text
+@This is a modification of imported-modified.txt on HEAD.
+It should supersede the version from the vendor branch.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d1 2
+a2 1
+This is vtag-1 (on vbranchA) of imported-modified.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-once.txt,v b/t/t9601/cvsroot/module/imported-once.txt,v
new file mode 100644
index 0000000..c5dd82b
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-once.txt,v
@@ -0,0 +1,43 @@
+head 1.1;
+branch 1.1.1;
+access;
+symbols
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-once.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-twice.txt,v b/t/t9601/cvsroot/module/imported-twice.txt,v
new file mode 100644
index 0000000..d1f3f1b
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-twice.txt,v
@@ -0,0 +1,60 @@
+head 1.1;
+branch 1.1.1;
+access;
+symbols
+ vtag-2:1.1.1.2
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next 1.1.1.2;
+
+1.1.1.2
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-twice.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
+1.1.1.2
+log
+@Import (vbranchA, vtag-2).
+@
+text
+@d1 1
+a1 1
+This is vtag-2 (on vbranchA) of imported-twice.txt.
+@
+
+
diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh
new file mode 100755
index 0000000..67878b2
--- /dev/null
+++ b/t/t9602-cvsimport-branches-tags.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# A description of the repository used for this test can be found in
+# t9602/README.
+
+test_description='git cvsimport handling of branches and tags'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9602/cvsroot
+export CVSROOT
+
+test_expect_success 'import module' '
+
+ git cvsimport -C module-git module
+
+'
+
+test_expect_success 'test branch master' '
+
+ test_cmp_branch_tree master
+
+'
+
+test_expect_success 'test branch vendorbranch' '
+
+ test_cmp_branch_tree vendorbranch
+
+'
+
+test_expect_failure 'test branch B_FROM_INITIALS' '
+
+ test_cmp_branch_tree B_FROM_INITIALS
+
+'
+
+test_expect_failure 'test branch B_FROM_INITIALS_BUT_ONE' '
+
+ test_cmp_branch_tree B_FROM_INITIALS_BUT_ONE
+
+'
+
+test_expect_failure 'test branch B_MIXED' '
+
+ test_cmp_branch_tree B_MIXED
+
+'
+
+test_expect_success 'test branch B_SPLIT' '
+
+ test_cmp_branch_tree B_SPLIT
+
+'
+
+test_expect_failure 'test tag vendortag' '
+
+ test_cmp_branch_tree vendortag
+
+'
+
+test_expect_success 'test tag T_ALL_INITIAL_FILES' '
+
+ test_cmp_branch_tree T_ALL_INITIAL_FILES
+
+'
+
+test_expect_failure 'test tag T_ALL_INITIAL_FILES_BUT_ONE' '
+
+ test_cmp_branch_tree T_ALL_INITIAL_FILES_BUT_ONE
+
+'
+
+test_expect_failure 'test tag T_MIXED' '
+
+ test_cmp_branch_tree T_MIXED
+
+'
+
+
+test_done
diff --git a/t/t9602/README b/t/t9602/README
new file mode 100644
index 0000000..c231e0f
--- /dev/null
+++ b/t/t9602/README
@@ -0,0 +1,62 @@
+This repository is for testing the ability to group revisions
+correctly along tags and branches. Here is its history:
+
+ 1. The initial import (revision 1.1 of everybody) created a
+ directory structure with a file named `default' in each dir:
+
+ ./
+ default
+ sub1/default
+ subsubA/default
+ subsubB/default
+ sub2/default
+ subsubA/default
+ sub3/default
+
+ 2. Then tagged everyone with T_ALL_INITIAL_FILES.
+
+ 3. Then tagged everyone except sub1/subsubB/default with
+ T_ALL_INITIAL_FILES_BUT_ONE.
+
+ 4. Then created branch B_FROM_INITIALS on everyone.
+
+ 5. Then created branch B_FROM_INITIALS_BUT_ONE on everyone except
+ /sub1/subsubB/default.
+
+ 6. Then committed modifications to two files: sub3/default, and
+ sub1/subsubA/default.
+
+ 7. Then committed a modification to all 7 files.
+
+ 8. Then backdated sub3/default to revision 1.2, and
+ sub2/subsubA/default to revision 1.1, and tagged with T_MIXED.
+
+ 9. Same as 8, but tagged with -b to create branch B_MIXED.
+
+ 10. Switched the working copy to B_MIXED, and added
+ sub2/branch_B_MIXED_only. (That's why the RCS file is in
+ sub2/Attic/ -- it never existed on trunk.)
+
+ 11. In one commit, modified default, sub1/default, and
+ sub2/subsubA/default, on branch B_MIXED.
+
+ 12. Did "cvs up -A" on sub2/default, then in one commit, made a
+ change to sub2/default and sub2/branch_B_MIXED_only. So this
+ commit should be spread between the branch and the trunk.
+
+ 13. Do "cvs up -A" to get everyone back to trunk, then make a new
+ branch B_SPLIT on everyone except sub1/subsubB/default,v.
+
+ 14. Switch to branch B_SPLIT (see sub1/subsubB/default disappear)
+ and commit a change that affects everyone except sub3/default.
+
+ 15. An hour or so later, "cvs up -A" to get sub1/subsubB/default
+ back, then commit a change on that file, on trunk. (It's
+ important that this change happened after the previous commits
+ on B_SPLIT.)
+
+ 16. Branch sub1/subsubB/default to B_SPLIT, then "cvs up -r B_SPLIT"
+ to switch the whole working copy to the branch.
+
+ 17. Commit a change on B_SPLIT, to sub1/subsubB/default and
+ sub3/default.
diff --git a/t/t9602/cvsroot/.gitattributes b/t/t9602/cvsroot/.gitattributes
new file mode 100644
index 0000000..562b12e
--- /dev/null
+++ b/t/t9602/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9602/cvsroot/CVSROOT/.gitignore b/t/t9602/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 0000000..3bb9b34
--- /dev/null
+++ b/t/t9602/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9602/cvsroot/module/default,v b/t/t9602/cvsroot/module/default,v
new file mode 100644
index 0000000..3b68382
--- /dev/null
+++ b/t/t9602/cvsroot/module/default,v
@@ -0,0 +1,102 @@
+head 1.2;
+access;
+symbols
+ B_SPLIT:1.2.0.4
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.2.2.1
+ 1.2.4.1;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2003.05.23.00.31.36; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.4.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is the file `default' in the top level of the project.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a5 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/default,v b/t/t9602/cvsroot/module/sub1/default,v
new file mode 100644
index 0000000..b7fdccd
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub1/default,v
@@ -0,0 +1,102 @@
+head 1.2;
+access;
+symbols
+ B_SPLIT:1.2.0.4
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.2.2.1
+ 1.2.4.1;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2003.05.23.00.31.36; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.4.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub1/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a5 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/subsubA/default,v b/t/t9602/cvsroot/module/sub1/subsubA/default,v
new file mode 100644
index 0000000..472b7b2
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub1/subsubA/default,v
@@ -0,0 +1,101 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.4
+ B_MIXED:1.3.0.2
+ T_MIXED:1.3
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.3.4.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.15.26; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.4.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub1/subsubA/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added by the first commit (affecting two files).
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.3.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a7 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2
+log
+@First commit to proj, affecting two files.
+@
+text
+@d6 2
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/subsubB/default,v b/t/t9602/cvsroot/module/sub1/subsubB/default,v
new file mode 100644
index 0000000..fe6efa4
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub1/subsubB/default,v
@@ -0,0 +1,107 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.2
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.06.03.04.29.14; author jrandom; state Exp;
+branches
+ 1.3.2.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.2.1
+date 2003.06.03.04.33.13; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@A trunk change to sub1/subsubB/default. This was committed about an
+hour after an earlier change that affected most files on branch
+B_SPLIT. This file is not on that branch yet, but after this commit,
+we'll branch to B_SPLIT, albeit rooted in a revision that didn't exist
+at the time the rest of B_SPLIT was created.
+@
+text
+@This is sub1/subsubB/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+
+This bit was committed on trunk about an hour after an earlier change
+to everyone else on branch B_SPLIT. Afterwards, we'll finally branch
+this file to B_SPLIT, but rooted in a revision that didn't exist at
+the time the rest of B_SPLIT was created.
+@
+
+
+1.3.2.1
+log
+@This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+text
+@a10 4
+
+This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@d6 5
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v
new file mode 100644
index 0000000..34c9789
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v
@@ -0,0 +1,59 @@
+head 1.1;
+access;
+symbols
+ B_MIXED:1.1.0.2;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2003.05.23.00.25.26; author jrandom; state dead;
+branches
+ 1.1.2.1;
+next ;
+
+1.1.2.1
+date 2003.05.23.00.25.26; author jrandom; state Exp;
+branches;
+next 1.1.2.2;
+
+1.1.2.2
+date 2003.05.23.00.48.51; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@file branch_B_MIXED_only was initially added on branch B_MIXED.
+@
+text
+@@
+
+
+1.1.2.1
+log
+@Add a file on branch B_MIXED.
+@
+text
+@a0 1
+This file was added on branch B_MIXED. It never existed on trunk.
+@
+
+
+1.1.2.2
+log
+@A single commit affecting one file on branch B_MIXED and one on trunk.
+@
+text
+@a1 3
+
+The same commit added these two lines here on branch B_MIXED, and two
+similar lines to ./default on trunk.
+@
+
+
diff --git a/t/t9602/cvsroot/module/sub2/default,v b/t/t9602/cvsroot/module/sub2/default,v
new file mode 100644
index 0000000..018f7f8
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub2/default,v
@@ -0,0 +1,102 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.2
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.05.23.00.48.51; author jrandom; state Exp;
+branches
+ 1.3.2.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.2.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@A single commit affecting one file on branch B_MIXED and one on trunk.
+@
+text
+@This is sub2/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+
+The same commit added these two lines here on trunk, and two similar
+lines to ./branch_B_MIXED_only on branch B_MIXED.
+@
+
+
+1.3.2.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a8 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@d6 3
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub2/subsubA/default,v b/t/t9602/cvsroot/module/sub2/subsubA/default,v
new file mode 100644
index 0000000..d13242c
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub2/subsubA/default,v
@@ -0,0 +1,102 @@
+head 1.2;
+access;
+symbols
+ B_SPLIT:1.2.0.2
+ B_MIXED:1.1.0.2
+ T_MIXED:1.1
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.2.2.1;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1
+ 1.1.2.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.1.2.1
+date 2003.05.23.00.31.36; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub2/subsub2/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.2.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a3 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub3/default,v b/t/t9602/cvsroot/module/sub3/default,v
new file mode 100644
index 0000000..88e4567
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub3/default,v
@@ -0,0 +1,102 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.2
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.3.2.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.15.26; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.2.1
+date 2003.06.03.04.33.13; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub3/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added by the first commit (affecting two files).
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.3.2.1
+log
+@This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+text
+@a7 4
+
+This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+
+
+1.2
+log
+@First commit to proj, affecting two files.
+@
+text
+@d6 2
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
new file mode 100755
index 0000000..958bdce
--- /dev/null
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Structure of the test cvs repository
+#
+# Message File:Content Commit Time
+# Rev 1 a: 1.1 2009-02-21 19:11:43 +0100
+# Rev 2 a: 1.2 b: 1.1 2009-02-21 19:11:14 +0100
+# Rev 3 b: 1.2 2009-02-21 19:11:43 +0100
+#
+# As you can see the commit of Rev 3 has the same time as
+# Rev 1 this leads to a broken import because of a cvsps
+# bug.
+
+test_description='git cvsimport testing for correct patchset estimation'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9603/cvsroot
+export CVSROOT
+
+test_expect_failure 'import with criss cross times on revisions' '
+
+ git cvsimport -p"-x" -C module-git module &&
+ cd module-git &&
+ git log --pretty=format:%s > ../actual-master &&
+ git log A~2..A --pretty="format:%s %ad" -- > ../actual-A &&
+ echo "" >> ../actual-master &&
+ echo "" >> ../actual-A &&
+ cd .. &&
+ echo "Rev 4
+Rev 3
+Rev 2
+Rev 1" > expect-master &&
+ test_cmp actual-master expect-master &&
+
+ echo "Rev 5 Branch A Wed Mar 11 19:09:10 2009 +0000
+Rev 4 Branch A Wed Mar 11 19:03:52 2009 +0000" > expect-A &&
+ test_cmp actual-A expect-A
+'
+
+test_done
diff --git a/t/t9603/cvsroot/.gitattributes b/t/t9603/cvsroot/.gitattributes
new file mode 100644
index 0000000..562b12e
--- /dev/null
+++ b/t/t9603/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9603/cvsroot/CVSROOT/.gitignore b/t/t9603/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 0000000..3bb9b34
--- /dev/null
+++ b/t/t9603/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9603/cvsroot/module/a,v b/t/t9603/cvsroot/module/a,v
new file mode 100644
index 0000000..ba8fd5a
--- /dev/null
+++ b/t/t9603/cvsroot/module/a,v
@@ -0,0 +1,74 @@
+head 1.2;
+access;
+symbols
+ A:1.2.0.2;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2009.02.21.18.11.14; author tester; state Exp;
+branches
+ 1.2.2.1;
+next 1.1;
+
+1.1
+date 2009.02.21.18.11.43; author tester; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2009.03.11.19.03.52; author tester; state Exp;
+branches;
+next 1.2.2.2;
+
+1.2.2.2
+date 2009.03.11.19.09.10; author tester; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Rev 2
+@
+text
+@1.2
+@
+
+
+1.2.2.1
+log
+@Rev 4 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.1
+@
+
+
+1.2.2.2
+log
+@Rev 5 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.2
+@
+
+
+1.1
+log
+@Rev 1
+@
+text
+@d1 1
+a1 1
+1.1
+@
diff --git a/t/t9603/cvsroot/module/b,v b/t/t9603/cvsroot/module/b,v
new file mode 100644
index 0000000..d268855
--- /dev/null
+++ b/t/t9603/cvsroot/module/b,v
@@ -0,0 +1,90 @@
+head 1.3;
+access;
+symbols
+ A:1.2.0.2;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2009.03.11.19.05.08; author tester; state Exp;
+branches;
+next 1.2;
+
+1.2
+date 2009.02.21.18.11.43; author tester; state Exp;
+branches
+ 1.2.2.1;
+next 1.1;
+
+1.1
+date 2009.02.21.18.11.14; author tester; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2009.03.11.19.03.52; author tester; state Exp;
+branches;
+next 1.2.2.2;
+
+1.2.2.2
+date 2009.03.11.19.09.10; author tester; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Rev 4
+@
+text
+@1.3
+@
+
+
+1.2
+log
+@Rev 3
+@
+text
+@d1 1
+a1 1
+1.2
+@
+
+
+1.2.2.1
+log
+@Rev 4 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.1
+@
+
+
+1.2.2.2
+log
+@Rev 5 Branch A
+@
+text
+@d1 1
+a1 1
+1.2
+@
+
+
+1.1
+log
+@Rev 2
+@
+text
+@d1 1
+a1 1
+1.1
+@
diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh
new file mode 100755
index 0000000..3787186
--- /dev/null
+++ b/t/t9700-perl-git.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Lea Wiemann
+#
+
+test_description='perl interface (Git.pm)'
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ skip_all='skipping perl interface tests, perl not available'
+ test_done
+fi
+
+"$PERL_PATH" -MTest::More -e 0 2>/dev/null || {
+ skip_all="Perl Test::More unavailable, skipping test"
+ test_done
+}
+
+# set up test repository
+
+test_expect_success \
+ 'set up test repository' \
+ 'echo "test file 1" > file1 &&
+ echo "test file 2" > file2 &&
+ mkdir directory1 &&
+ echo "in directory1" >> directory1/file &&
+ mkdir directory2 &&
+ echo "in directory2" >> directory2/file &&
+ git add . &&
+ git commit -m "first commit" &&
+
+ echo "new file in subdir 2" > directory2/file2 &&
+ git add . &&
+ git commit -m "commit in directory2" &&
+
+ echo "changed file 1" > file1 &&
+ git commit -a -m "second commit" &&
+
+ git config --add color.test.slot1 green &&
+ git config --add test.string value &&
+ git config --add test.dupstring value1 &&
+ git config --add test.dupstring value2 &&
+ git config --add test.booltrue true &&
+ git config --add test.boolfalse no &&
+ git config --add test.boolother other &&
+ git config --add test.int 2k
+ '
+
+# The external test will outputs its own plan
+test_external_has_tap=1
+
+test_external_without_stderr \
+ 'Perl API' \
+ "$PERL_PATH" "$TEST_DIRECTORY"/t9700/test.pl
+
+test_done
diff --git a/t/t9700/test.pl b/t/t9700/test.pl
new file mode 100755
index 0000000..671f38d
--- /dev/null
+++ b/t/t9700/test.pl
@@ -0,0 +1,119 @@
+#!/usr/bin/perl
+use lib (split(/:/, $ENV{GITPERLLIB}));
+
+use 5.006002;
+use warnings;
+use strict;
+
+use Test::More qw(no_plan);
+
+BEGIN {
+ # t9700-perl-git.sh kicks off our testing, so we have to go from
+ # there.
+ Test::More->builder->current_test(1);
+ Test::More->builder->no_ending(1);
+}
+
+use Cwd;
+use File::Basename;
+
+BEGIN { use_ok('Git') }
+
+# set up
+our $abs_repo_dir = cwd();
+ok(our $r = Git->repository(Directory => "."), "open repository");
+
+# config
+is($r->config("test.string"), "value", "config scalar: string");
+is_deeply([$r->config("test.dupstring")], ["value1", "value2"],
+ "config array: string");
+is($r->config("test.nonexistent"), undef, "config scalar: nonexistent");
+is_deeply([$r->config("test.nonexistent")], [], "config array: nonexistent");
+is($r->config_int("test.int"), 2048, "config_int: integer");
+is($r->config_int("test.nonexistent"), undef, "config_int: nonexistent");
+ok($r->config_bool("test.booltrue"), "config_bool: true");
+ok(!$r->config_bool("test.boolfalse"), "config_bool: false");
+our $ansi_green = "\x1b[32m";
+is($r->get_color("color.test.slot1", "red"), $ansi_green, "get_color");
+# Cannot test $r->get_colorbool("color.foo")) because we do not
+# control whether our STDOUT is a terminal.
+
+# Failure cases for config:
+# Save and restore STDERR; we will probably extract this into a
+# "dies_ok" method and possibly move the STDERR handling to Git.pm.
+open our $tmpstderr, ">&STDERR" or die "cannot save STDERR"; close STDERR;
+eval { $r->config("test.dupstring") };
+ok($@, "config: duplicate entry in scalar context fails");
+eval { $r->config_bool("test.boolother") };
+ok($@, "config_bool: non-boolean values fail");
+open STDERR, ">&", $tmpstderr or die "cannot restore STDERR";
+
+# ident
+like($r->ident("aUthor"), qr/^A U Thor <author\@example.com> [0-9]+ \+0000$/,
+ "ident scalar: author (type)");
+like($r->ident("cOmmitter"), qr/^C O Mitter <committer\@example.com> [0-9]+ \+0000$/,
+ "ident scalar: committer (type)");
+is($r->ident("invalid"), "invalid", "ident scalar: invalid ident string (no parsing)");
+my ($name, $email, $time_tz) = $r->ident('author');
+is_deeply([$name, $email], ["A U Thor", "author\@example.com"],
+ "ident array: author");
+like($time_tz, qr/[0-9]+ \+0000/, "ident array: author");
+is_deeply([$r->ident("Name <email> 123 +0000")], ["Name", "email", "123 +0000"],
+ "ident array: ident string");
+is_deeply([$r->ident("invalid")], [], "ident array: invalid ident string");
+
+# ident_person
+is($r->ident_person("aUthor"), "A U Thor <author\@example.com>",
+ "ident_person: author (type)");
+is($r->ident_person("Name <email> 123 +0000"), "Name <email>",
+ "ident_person: ident string");
+is($r->ident_person("Name", "email", "123 +0000"), "Name <email>",
+ "ident_person: array");
+
+# objects and hashes
+ok(our $file1hash = $r->command_oneline('rev-parse', "HEAD:file1"), "(get file hash)");
+my $tmpfile = "file.tmp";
+open TEMPFILE, "+>$tmpfile" or die "Can't open $tmpfile: $!";
+is($r->cat_blob($file1hash, \*TEMPFILE), 15, "cat_blob: size");
+our $blobcontents;
+{ local $/; seek TEMPFILE, 0, 0; $blobcontents = <TEMPFILE>; }
+is($blobcontents, "changed file 1\n", "cat_blob: data");
+close TEMPFILE or die "Failed writing to $tmpfile: $!";
+is(Git::hash_object("blob", $tmpfile), $file1hash, "hash_object: roundtrip");
+open TEMPFILE, ">$tmpfile" or die "Can't open $tmpfile: $!";
+print TEMPFILE my $test_text = "test blob, to be inserted\n";
+close TEMPFILE or die "Failed writing to $tmpfile: $!";
+like(our $newhash = $r->hash_and_insert_object($tmpfile), qr/[0-9a-fA-F]{40}/,
+ "hash_and_insert_object: returns hash");
+open TEMPFILE, "+>$tmpfile" or die "Can't open $tmpfile: $!";
+is($r->cat_blob($newhash, \*TEMPFILE), length $test_text, "cat_blob: roundtrip size");
+{ local $/; seek TEMPFILE, 0, 0; $blobcontents = <TEMPFILE>; }
+is($blobcontents, $test_text, "cat_blob: roundtrip data");
+close TEMPFILE;
+unlink $tmpfile;
+
+# paths
+is($r->repo_path, $abs_repo_dir . "/.git", "repo_path");
+is($r->wc_path, $abs_repo_dir . "/", "wc_path");
+is($r->wc_subdir, "", "wc_subdir initial");
+$r->wc_chdir("directory1");
+is($r->wc_subdir, "directory1", "wc_subdir after wc_chdir");
+is($r->config("test.string"), "value", "config after wc_chdir");
+
+# Object generation in sub directory
+chdir("directory2");
+my $r2 = Git->repository();
+is($r2->repo_path, $abs_repo_dir . "/.git", "repo_path (2)");
+is($r2->wc_path, $abs_repo_dir . "/", "wc_path (2)");
+is($r2->wc_subdir, "directory2/", "wc_subdir initial (2)");
+
+# commands in sub directory
+my $last_commit = $r2->command_oneline(qw(rev-parse --verify HEAD));
+like($last_commit, qr/^[0-9a-fA-F]{40}$/, 'rev-parse returned hash');
+my $dir_commit = $r2->command_oneline('log', '-n1', '--pretty=format:%H', '.');
+isnt($last_commit, $dir_commit, 'log . does not show last commit');
+
+printf "1..%d\n", Test::More->builder->current_test;
+
+my $is_passing = eval { Test::More->is_passing };
+exit($is_passing ? 0 : 1) unless $@ =~ /Can't locate object method/;
diff --git a/t/test-lib.sh b/t/test-lib.sh
new file mode 100644
index 0000000..e5523dd
--- /dev/null
+++ b/t/test-lib.sh
@@ -0,0 +1,889 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see http://www.gnu.org/licenses/ .
+
+# if --tee was passed, write the output not only to the terminal, but
+# additionally to the file test-results/$BASENAME.out, too.
+case "$GIT_TEST_TEE_STARTED, $* " in
+done,*)
+ # do not redirect again
+ ;;
+*' --tee '*|*' --va'*)
+ mkdir -p test-results
+ BASE=test-results/$(basename "$0" .sh)
+ (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
+ echo $? > $BASE.exit) | tee $BASE.out
+ test "$(cat $BASE.exit)" = 0
+ exit
+ ;;
+esac
+
+# Keep the original TERM for say_color
+ORIGINAL_TERM=$TERM
+
+# For repeatability, reset the environment to known value.
+LANG=C
+LC_ALL=C
+PAGER=cat
+TZ=UTC
+TERM=dumb
+export LANG LC_ALL PAGER TERM TZ
+EDITOR=:
+unset VISUAL
+unset GIT_EDITOR
+unset AUTHOR_DATE
+unset AUTHOR_EMAIL
+unset AUTHOR_NAME
+unset COMMIT_AUTHOR_EMAIL
+unset COMMIT_AUTHOR_NAME
+unset EMAIL
+unset GIT_ALTERNATE_OBJECT_DIRECTORIES
+unset GIT_AUTHOR_DATE
+GIT_AUTHOR_EMAIL=author@example.com
+GIT_AUTHOR_NAME='A U Thor'
+unset GIT_COMMITTER_DATE
+GIT_COMMITTER_EMAIL=committer@example.com
+GIT_COMMITTER_NAME='C O Mitter'
+unset GIT_DIFF_OPTS
+unset GIT_DIR
+unset GIT_WORK_TREE
+unset GIT_EXTERNAL_DIFF
+unset GIT_INDEX_FILE
+unset GIT_OBJECT_DIRECTORY
+unset GIT_CEILING_DIRECTORIES
+unset SHA1_FILE_DIRECTORIES
+unset SHA1_FILE_DIRECTORY
+unset GIT_NOTES_REF
+unset GIT_NOTES_DISPLAY_REF
+unset GIT_NOTES_REWRITE_REF
+unset GIT_NOTES_REWRITE_MODE
+GIT_MERGE_VERBOSITY=5
+export GIT_MERGE_VERBOSITY
+export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
+export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
+export EDITOR
+
+# Protect ourselves from common misconfiguration to export
+# CDPATH into the environment
+unset CDPATH
+
+unset GREP_OPTIONS
+
+case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
+ 1|2|true)
+ echo "* warning: Some tests will not work if GIT_TRACE" \
+ "is set as to trace on STDERR ! *"
+ echo "* warning: Please set GIT_TRACE to something" \
+ "other than 1, 2 or true ! *"
+ ;;
+esac
+
+# Convenience
+#
+# A regexp to match 5 and 40 hexdigits
+_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+
+# Each test should start with something like this, after copyright notices:
+#
+# test_description='Description of this test...
+# This test checks if command xyzzy does the right thing...
+# '
+# . ./test-lib.sh
+[ "x$ORIGINAL_TERM" != "xdumb" ] && (
+ TERM=$ORIGINAL_TERM &&
+ export TERM &&
+ [ -t 1 ] &&
+ tput bold >/dev/null 2>&1 &&
+ tput setaf 1 >/dev/null 2>&1 &&
+ tput sgr0 >/dev/null 2>&1
+ ) &&
+ color=t
+
+while test "$#" -ne 0
+do
+ case "$1" in
+ -d|--d|--de|--deb|--debu|--debug)
+ debug=t; shift ;;
+ -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
+ immediate=t; shift ;;
+ -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
+ GIT_TEST_LONG=t; export GIT_TEST_LONG; shift ;;
+ -h|--h|--he|--hel|--help)
+ help=t; shift ;;
+ -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+ verbose=t; shift ;;
+ -q|--q|--qu|--qui|--quie|--quiet)
+ quiet=t; shift ;;
+ --with-dashes)
+ with_dashes=t; shift ;;
+ --no-color)
+ color=; shift ;;
+ --no-python)
+ # noop now...
+ shift ;;
+ --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
+ valgrind=t; verbose=t; shift ;;
+ --tee)
+ shift ;; # was handled already
+ --root=*)
+ root=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ shift ;;
+ *)
+ echo "error: unknown test option '$1'" >&2; exit 1 ;;
+ esac
+done
+
+if test -n "$color"; then
+ say_color () {
+ (
+ TERM=$ORIGINAL_TERM
+ export TERM
+ case "$1" in
+ error) tput bold; tput setaf 1;; # bold red
+ skip) tput bold; tput setaf 2;; # bold green
+ pass) tput setaf 2;; # green
+ info) tput setaf 3;; # brown
+ *) test -n "$quiet" && return;;
+ esac
+ shift
+ printf "%s" "$*"
+ tput sgr0
+ echo
+ )
+ }
+else
+ say_color() {
+ test -z "$1" && test -n "$quiet" && return
+ shift
+ echo "$*"
+ }
+fi
+
+error () {
+ say_color error "error: $*"
+ GIT_EXIT_OK=t
+ exit 1
+}
+
+say () {
+ say_color info "$*"
+}
+
+test "${test_description}" != "" ||
+error "Test script did not set test_description."
+
+if test "$help" = "t"
+then
+ echo "$test_description"
+ exit 0
+fi
+
+exec 5>&1
+if test "$verbose" = "t"
+then
+ exec 4>&2 3>&1
+else
+ exec 4>/dev/null 3>/dev/null
+fi
+
+test_failure=0
+test_count=0
+test_fixed=0
+test_broken=0
+test_success=0
+
+test_external_has_tap=0
+
+die () {
+ code=$?
+ if test -n "$GIT_EXIT_OK"
+ then
+ exit $code
+ else
+ echo >&5 "FATAL: Unexpected exit with code $code"
+ exit 1
+ fi
+}
+
+GIT_EXIT_OK=
+trap 'die' EXIT
+
+# The semantics of the editor variables are that of invoking
+# sh -c "$EDITOR \"$@\"" files ...
+#
+# If our trash directory contains shell metacharacters, they will be
+# interpreted if we just set $EDITOR directly, so do a little dance with
+# environment variables to work around this.
+#
+# In particular, quoting isn't enough, as the path may contain the same quote
+# that we're using.
+test_set_editor () {
+ FAKE_EDITOR="$1"
+ export FAKE_EDITOR
+ EDITOR='"$FAKE_EDITOR"'
+ export EDITOR
+}
+
+test_decode_color () {
+ sed -e 's/.\[1m/<WHITE>/g' \
+ -e 's/.\[31m/<RED>/g' \
+ -e 's/.\[32m/<GREEN>/g' \
+ -e 's/.\[33m/<YELLOW>/g' \
+ -e 's/.\[34m/<BLUE>/g' \
+ -e 's/.\[35m/<MAGENTA>/g' \
+ -e 's/.\[36m/<CYAN>/g' \
+ -e 's/.\[m/<RESET>/g'
+}
+
+q_to_nul () {
+ perl -pe 'y/Q/\000/'
+}
+
+q_to_cr () {
+ tr Q '\015'
+}
+
+append_cr () {
+ sed -e 's/$/Q/' | tr Q '\015'
+}
+
+remove_cr () {
+ tr '\015' Q | sed -e 's/Q$//'
+}
+
+test_tick () {
+ if test -z "${test_tick+set}"
+ then
+ test_tick=1112911993
+ else
+ test_tick=$(($test_tick + 60))
+ fi
+ GIT_COMMITTER_DATE="$test_tick -0700"
+ GIT_AUTHOR_DATE="$test_tick -0700"
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+# Call test_commit with the arguments "<message> [<file> [<contents>]]"
+#
+# This will commit a file with the given contents and the given commit
+# message. It will also add a tag with <message> as name.
+#
+# Both <file> and <contents> default to <message>.
+
+test_commit () {
+ file=${2:-"$1.t"}
+ echo "${3-$1}" > "$file" &&
+ git add "$file" &&
+ test_tick &&
+ git commit -m "$1" &&
+ git tag "$1"
+}
+
+# Call test_merge with the arguments "<message> <commit>", where <commit>
+# can be a tag pointing to the commit-to-merge.
+
+test_merge () {
+ test_tick &&
+ git merge -m "$1" "$2" &&
+ git tag "$1"
+}
+
+# This function helps systems where core.filemode=false is set.
+# Use it instead of plain 'chmod +x' to set or unset the executable bit
+# of a file in the working directory and add it to the index.
+
+test_chmod () {
+ chmod "$@" &&
+ git update-index --add "--chmod=$@"
+}
+
+# Use test_set_prereq to tell that a particular prerequisite is available.
+# The prerequisite can later be checked for in two ways:
+#
+# - Explicitly using test_have_prereq.
+#
+# - Implicitly by specifying the prerequisite tag in the calls to
+# test_expect_{success,failure,code}.
+#
+# The single parameter is the prerequisite tag (a simple word, in all
+# capital letters by convention).
+
+test_set_prereq () {
+ satisfied="$satisfied$1 "
+}
+satisfied=" "
+
+test_have_prereq () {
+ case $satisfied in
+ *" $1 "*)
+ : yes, have it ;;
+ *)
+ ! : nope ;;
+ esac
+}
+
+# You are not expected to call test_ok_ and test_failure_ directly, use
+# the text_expect_* functions instead.
+
+test_ok_ () {
+ test_success=$(($test_success + 1))
+ say_color "" "ok $test_count - $@"
+}
+
+test_failure_ () {
+ test_failure=$(($test_failure + 1))
+ say_color error "not ok - $test_count $1"
+ shift
+ echo "$@" | sed -e 's/^/# /'
+ test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
+}
+
+test_known_broken_ok_ () {
+ test_fixed=$(($test_fixed+1))
+ say_color "" "ok $test_count - $@ # TODO known breakage"
+}
+
+test_known_broken_failure_ () {
+ test_broken=$(($test_broken+1))
+ say_color skip "not ok $test_count - $@ # TODO known breakage"
+}
+
+test_debug () {
+ test "$debug" = "" || eval "$1"
+}
+
+test_run_ () {
+ test_cleanup=:
+ eval >&3 2>&4 "$1"
+ eval_ret=$?
+ eval >&3 2>&4 "$test_cleanup"
+ if test "$verbose" = "t" && test -n "$HARNESS_ACTIVE"; then
+ echo ""
+ fi
+ return 0
+}
+
+test_skip () {
+ test_count=$(($test_count+1))
+ to_skip=
+ for skp in $GIT_SKIP_TESTS
+ do
+ case $this_test.$test_count in
+ $skp)
+ to_skip=t
+ break
+ esac
+ done
+ if test -z "$to_skip" && test -n "$prereq" &&
+ ! test_have_prereq "$prereq"
+ then
+ to_skip=t
+ fi
+ case "$to_skip" in
+ t)
+ say_color skip >&3 "skipping test: $@"
+ say_color skip "ok $test_count # skip $1"
+ : true
+ ;;
+ *)
+ false
+ ;;
+ esac
+}
+
+test_expect_failure () {
+ test "$#" = 3 && { prereq=$1; shift; } || prereq=
+ test "$#" = 2 ||
+ error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
+ if ! test_skip "$@"
+ then
+ say >&3 "checking known breakage: $2"
+ test_run_ "$2"
+ if [ "$?" = 0 -a "$eval_ret" = 0 ]
+ then
+ test_known_broken_ok_ "$1"
+ else
+ test_known_broken_failure_ "$1"
+ fi
+ fi
+ echo >&3 ""
+}
+
+test_expect_success () {
+ test "$#" = 3 && { prereq=$1; shift; } || prereq=
+ test "$#" = 2 ||
+ error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+ if ! test_skip "$@"
+ then
+ say >&3 "expecting success: $2"
+ test_run_ "$2"
+ if [ "$?" = 0 -a "$eval_ret" = 0 ]
+ then
+ test_ok_ "$1"
+ else
+ test_failure_ "$@"
+ fi
+ fi
+ echo >&3 ""
+}
+
+test_expect_code () {
+ test "$#" = 4 && { prereq=$1; shift; } || prereq=
+ test "$#" = 3 ||
+ error "bug in the test script: not 3 or 4 parameters to test-expect-code"
+ if ! test_skip "$@"
+ then
+ say >&3 "expecting exit code $1: $3"
+ test_run_ "$3"
+ if [ "$?" = 0 -a "$eval_ret" = "$1" ]
+ then
+ test_ok_ "$2"
+ else
+ test_failure_ "$@"
+ fi
+ fi
+ echo >&3 ""
+}
+
+# test_external runs external test scripts that provide continuous
+# test output about their progress, and succeeds/fails on
+# zero/non-zero exit code. It outputs the test output on stdout even
+# in non-verbose mode, and announces the external script with "# run
+# <n>: ..." before running it. When providing relative paths, keep in
+# mind that all scripts run in "trash directory".
+# Usage: test_external description command arguments...
+# Example: test_external 'Perl API' perl ../path/to/test.pl
+test_external () {
+ test "$#" = 4 && { prereq=$1; shift; } || prereq=
+ test "$#" = 3 ||
+ error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
+ descr="$1"
+ shift
+ if ! test_skip "$descr" "$@"
+ then
+ # Announce the script to reduce confusion about the
+ # test output that follows.
+ say_color "" "# run $test_count: $descr ($*)"
+ # Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG
+ # to be able to use them in script
+ export TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG
+ # Run command; redirect its stderr to &4 as in
+ # test_run_, but keep its stdout on our stdout even in
+ # non-verbose mode.
+ "$@" 2>&4
+ if [ "$?" = 0 ]
+ then
+ if test $test_external_has_tap -eq 0; then
+ test_ok_ "$descr"
+ else
+ say_color "" "# test_external test $descr was ok"
+ test_success=$(($test_success + 1))
+ fi
+ else
+ if test $test_external_has_tap -eq 0; then
+ test_failure_ "$descr" "$@"
+ else
+ say_color error "# test_external test $descr failed: $@"
+ test_failure=$(($test_failure + 1))
+ fi
+ fi
+ fi
+}
+
+# Like test_external, but in addition tests that the command generated
+# no output on stderr.
+test_external_without_stderr () {
+ # The temporary file has no (and must have no) security
+ # implications.
+ tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi
+ stderr="$tmp/git-external-stderr.$$.tmp"
+ test_external "$@" 4> "$stderr"
+ [ -f "$stderr" ] || error "Internal error: $stderr disappeared."
+ descr="no stderr: $1"
+ shift
+ say >&3 "# expecting no stderr from previous command"
+ if [ ! -s "$stderr" ]; then
+ rm "$stderr"
+
+ if test $test_external_has_tap -eq 0; then
+ test_ok_ "$descr"
+ else
+ say_color "" "# test_external_without_stderr test $descr was ok"
+ test_success=$(($test_success + 1))
+ fi
+ else
+ if [ "$verbose" = t ]; then
+ output=`echo; echo "# Stderr is:"; cat "$stderr"`
+ else
+ output=
+ fi
+ # rm first in case test_failure exits.
+ rm "$stderr"
+ if test $test_external_has_tap -eq 0; then
+ test_failure_ "$descr" "$@" "$output"
+ else
+ say_color error "# test_external_without_stderr test $descr failed: $@: $output"
+ test_failure=$(($test_failure + 1))
+ fi
+ fi
+}
+
+# This is not among top-level (test_expect_success | test_expect_failure)
+# but is a prefix that can be used in the test script, like:
+#
+# test_expect_success 'complain and die' '
+# do something &&
+# do something else &&
+# test_must_fail git checkout ../outerspace
+# '
+#
+# Writing this as "! git checkout ../outerspace" is wrong, because
+# the failure could be due to a segv. We want a controlled failure.
+
+test_must_fail () {
+ "$@"
+ test $? -gt 0 -a $? -le 129 -o $? -gt 192
+}
+
+# Similar to test_must_fail, but tolerates success, too. This is
+# meant to be used in contexts like:
+#
+# test_expect_success 'some command works without configuration' '
+# test_might_fail git config --unset all.configuration &&
+# do something
+# '
+#
+# Writing "git config --unset all.configuration || :" would be wrong,
+# because we want to notice if it fails due to segv.
+
+test_might_fail () {
+ "$@"
+ test $? -ge 0 -a $? -le 129 -o $? -gt 192
+}
+
+# test_cmp is a helper function to compare actual and expected output.
+# You can use it like:
+#
+# test_expect_success 'foo works' '
+# echo expected >expected &&
+# foo >actual &&
+# test_cmp expected actual
+# '
+#
+# This could be written as either "cmp" or "diff -u", but:
+# - cmp's output is not nearly as easy to read as diff -u
+# - not all diff versions understand "-u"
+
+test_cmp() {
+ $GIT_TEST_CMP "$@"
+}
+
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test to restore sanity:
+#
+# test_expect_success 'test core.capslock' '
+# git config core.capslock true &&
+# test_when_finished "git config --unset core.capslock" &&
+# hello world
+# '
+#
+# That would be roughly equivalent to
+#
+# test_expect_success 'test core.capslock' '
+# git config core.capslock true &&
+# hello world
+# git config --unset core.capslock
+# '
+#
+# except that the greeting and config --unset must both succeed for
+# the test to pass.
+
+test_when_finished () {
+ test_cleanup="{ $*
+ } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+}
+
+# Most tests can use the created repository, but some may need to create more.
+# Usage: test_create_repo <directory>
+test_create_repo () {
+ test "$#" = 1 ||
+ error "bug in the test script: not 1 parameter to test-create-repo"
+ owd=`pwd`
+ repo="$1"
+ mkdir -p "$repo"
+ cd "$repo" || error "Cannot setup test environment"
+ "$GIT_EXEC_PATH/git-init" "--template=$TEST_DIRECTORY/../templates/blt/" >&3 2>&4 ||
+ error "cannot run git init -- have you built things yet?"
+ mv .git/hooks .git/hooks-disabled
+ cd "$owd"
+}
+
+test_done () {
+ GIT_EXIT_OK=t
+ test_results_dir="$TEST_DIRECTORY/test-results"
+ mkdir -p "$test_results_dir"
+ test_results_path="$test_results_dir/${0%.sh}-$$.counts"
+
+ echo "total $test_count" >> $test_results_path
+ echo "success $test_success" >> $test_results_path
+ echo "fixed $test_fixed" >> $test_results_path
+ echo "broken $test_broken" >> $test_results_path
+ echo "failed $test_failure" >> $test_results_path
+ echo "" >> $test_results_path
+
+ if test "$test_fixed" != 0
+ then
+ say_color pass "# fixed $test_fixed known breakage(s)"
+ fi
+ if test "$test_broken" != 0
+ then
+ say_color error "# still have $test_broken known breakage(s)"
+ msg="remaining $(($test_count-$test_broken)) test(s)"
+ else
+ msg="$test_count test(s)"
+ fi
+ case "$test_failure" in
+ 0)
+ # Maybe print SKIP message
+ [ -z "$skip_all" ] || skip_all=" # SKIP $skip_all"
+
+ if test $test_external_has_tap -eq 0; then
+ say_color pass "# passed all $msg"
+ say "1..$test_count$skip_all"
+ fi
+
+ test -d "$remove_trash" &&
+ cd "$(dirname "$remove_trash")" &&
+ rm -rf "$(basename "$remove_trash")"
+
+ exit 0 ;;
+
+ *)
+ if test $test_external_has_tap -eq 0; then
+ say_color error "# failed $test_failure among $msg"
+ say "1..$test_count"
+ fi
+
+ exit 1 ;;
+
+ esac
+}
+
+# Test the binaries we have just built. The tests are kept in
+# t/ subdirectory and are run in 'trash directory' subdirectory.
+TEST_DIRECTORY=$(pwd)
+if test -n "$valgrind"
+then
+ make_symlink () {
+ test -h "$2" &&
+ test "$1" = "$(readlink "$2")" || {
+ # be super paranoid
+ if mkdir "$2".lock
+ then
+ rm -f "$2" &&
+ ln -s "$1" "$2" &&
+ rm -r "$2".lock
+ else
+ while test -d "$2".lock
+ do
+ say "Waiting for lock on $2."
+ sleep 1
+ done
+ fi
+ }
+ }
+
+ make_valgrind_symlink () {
+ # handle only executables
+ test -x "$1" || return
+
+ base=$(basename "$1")
+ symlink_target=$TEST_DIRECTORY/../$base
+ # do not override scripts
+ if test -x "$symlink_target" &&
+ test ! -d "$symlink_target" &&
+ test "#!" != "$(head -c 2 < "$symlink_target")"
+ then
+ symlink_target=../valgrind.sh
+ fi
+ case "$base" in
+ *.sh|*.perl)
+ symlink_target=../unprocessed-script
+ esac
+ # create the link, or replace it if it is out of date
+ make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
+ }
+
+ # override all git executables in TEST_DIRECTORY/..
+ GIT_VALGRIND=$TEST_DIRECTORY/valgrind
+ mkdir -p "$GIT_VALGRIND"/bin
+ for file in $TEST_DIRECTORY/../git* $TEST_DIRECTORY/../test-*
+ do
+ make_valgrind_symlink $file
+ done
+ OLDIFS=$IFS
+ IFS=:
+ for path in $PATH
+ do
+ ls "$path"/git-* 2> /dev/null |
+ while read file
+ do
+ make_valgrind_symlink "$file"
+ done
+ done
+ IFS=$OLDIFS
+ PATH=$GIT_VALGRIND/bin:$PATH
+ GIT_EXEC_PATH=$GIT_VALGRIND/bin
+ export GIT_VALGRIND
+elif test -n "$GIT_TEST_INSTALLED" ; then
+ GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path) ||
+ error "Cannot run git from $GIT_TEST_INSTALLED."
+ PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
+ GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
+else # normal case, use ../bin-wrappers only unless $with_dashes:
+ git_bin_dir="$TEST_DIRECTORY/../bin-wrappers"
+ if ! test -x "$git_bin_dir/git" ; then
+ if test -z "$with_dashes" ; then
+ say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
+ fi
+ with_dashes=t
+ fi
+ PATH="$git_bin_dir:$PATH"
+ GIT_EXEC_PATH=$TEST_DIRECTORY/..
+ if test -n "$with_dashes" ; then
+ PATH="$TEST_DIRECTORY/..:$PATH"
+ fi
+fi
+GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
+unset GIT_CONFIG
+GIT_CONFIG_NOSYSTEM=1
+GIT_CONFIG_NOGLOBAL=1
+export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL
+
+. ../GIT-BUILD-OPTIONS
+
+if test -z "$GIT_TEST_CMP"
+then
+ if test -n "$GIT_TEST_CMP_USE_COPIED_CONTEXT"
+ then
+ GIT_TEST_CMP="$DIFF -c"
+ else
+ GIT_TEST_CMP="$DIFF -u"
+ fi
+fi
+
+GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
+export GITPERLLIB
+test -d ../templates/blt || {
+ error "You haven't built things yet, have you?"
+}
+
+if test -z "$GIT_TEST_INSTALLED" && test -z "$NO_PYTHON"
+then
+ GITPYTHONLIB="$(pwd)/../git_remote_helpers/build/lib"
+ export GITPYTHONLIB
+ test -d ../git_remote_helpers/build || {
+ error "You haven't built git_remote_helpers yet, have you?"
+ }
+fi
+
+if ! test -x ../test-chmtime; then
+ echo >&2 'You need to build test-chmtime:'
+ echo >&2 'Run "make test-chmtime" in the source (toplevel) directory'
+ exit 1
+fi
+
+# Test repository
+test="trash directory.$(basename "$0" .sh)"
+test -n "$root" && test="$root/$test"
+case "$test" in
+/*) TRASH_DIRECTORY="$test" ;;
+ *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;;
+esac
+test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
+rm -fr "$test" || {
+ GIT_EXIT_OK=t
+ echo >&5 "FATAL: Cannot prepare test area"
+ exit 1
+}
+
+test_create_repo "$test"
+# Use -P to resolve symlinks in our working directory so that the cwd
+# in subprocesses like git equals our $PWD (for pathname comparisons).
+cd -P "$test" || exit 1
+
+this_test=${0##*/}
+this_test=${this_test%%-*}
+for skp in $GIT_SKIP_TESTS
+do
+ case "$this_test" in
+ $skp)
+ say_color skip >&3 "skipping test $this_test altogether"
+ skip_all="skip all tests in $this_test"
+ test_done
+ esac
+done
+
+# Provide an implementation of the 'yes' utility
+yes () {
+ if test $# = 0
+ then
+ y=y
+ else
+ y="$*"
+ fi
+
+ while echo "$y"
+ do
+ :
+ done
+}
+
+# Fix some commands on Windows
+case $(uname -s) in
+*MINGW*)
+ # Windows has its own (incompatible) sort and find
+ sort () {
+ /usr/bin/sort "$@"
+ }
+ find () {
+ /usr/bin/find "$@"
+ }
+ sum () {
+ md5sum "$@"
+ }
+ # git sees Windows-style pwd
+ pwd () {
+ builtin pwd -W
+ }
+ # no POSIX permissions
+ # backslashes in pathspec are converted to '/'
+ # exec does not inherit the PID
+ ;;
+*)
+ test_set_prereq POSIXPERM
+ test_set_prereq BSLASHPSPEC
+ test_set_prereq EXECKEEPSPID
+ ;;
+esac
+
+test -z "$NO_PERL" && test_set_prereq PERL
+test -z "$NO_PYTHON" && test_set_prereq PYTHON
+
+# test whether the filesystem supports symbolic links
+ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
+rm -f y
diff --git a/t/test4012.png b/t/test4012.png
new file mode 100644
index 0000000..7b181d1
--- /dev/null
+++ b/t/test4012.png
Binary files differ
diff --git a/t/test9200a.png b/t/test9200a.png
new file mode 100644
index 0000000..7b181d1
--- /dev/null
+++ b/t/test9200a.png
Binary files differ
diff --git a/t/test9200b.png b/t/test9200b.png
new file mode 100644
index 0000000..ac22ccb
--- /dev/null
+++ b/t/test9200b.png
Binary files differ
diff --git a/t/valgrind/.gitignore b/t/valgrind/.gitignore
new file mode 100644
index 0000000..d4ae667
--- /dev/null
+++ b/t/valgrind/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/templates
diff --git a/t/valgrind/analyze.sh b/t/valgrind/analyze.sh
new file mode 100755
index 0000000..d8105d9
--- /dev/null
+++ b/t/valgrind/analyze.sh
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+out_prefix=$(dirname "$0")/../test-results/valgrind.out
+output=
+count=0
+total_count=0
+missing_message=
+new_line='
+'
+
+# start outputting the current valgrind error in $out_prefix.++$count,
+# and the test case which failed in the corresponding .message file
+start_output () {
+ test -z "$output" || return
+
+ # progress
+ total_count=$(($total_count+1))
+ test -t 2 && printf "\rFound %d errors" $total_count >&2
+
+ count=$(($count+1))
+ output=$out_prefix.$count
+ : > $output
+
+ echo "*** $1 ***" > $output.message
+}
+
+finish_output () {
+ test ! -z "$output" || return
+ output=
+
+ # if a test case has more than one valgrind error, we need to
+ # copy the last .message file to the previous errors
+ test -z "$missing_message" || {
+ while test $missing_message -lt $count
+ do
+ cp $out_prefix.$count.message \
+ $out_prefix.$missing_message.message
+ missing_message=$(($missing_message+1))
+ done
+ missing_message=
+ }
+}
+
+# group the valgrind errors by backtrace
+output_all () {
+ last_line=
+ j=0
+ i=1
+ while test $i -le $count
+ do
+ # output <number> <backtrace-in-one-line>
+ echo "$i $(tr '\n' ' ' < $out_prefix.$i)"
+ i=$(($i+1))
+ done |
+ sort -t ' ' -k 2 | # order by <backtrace-in-one-line>
+ while read number line
+ do
+ # find duplicates, do not output backtrace twice
+ if test "$line" != "$last_line"
+ then
+ last_line=$line
+ j=$(($j+1))
+ printf "\nValgrind error $j:\n\n"
+ cat $out_prefix.$number
+ printf "\nfound in:\n"
+ fi
+ # print the test case where this came from
+ printf "\n"
+ cat $out_prefix.$number.message
+ done
+}
+
+handle_one () {
+ OLDIFS=$IFS
+ IFS="$new_line"
+ while read line
+ do
+ case "$line" in
+ # backtrace, possibly a new one
+ ==[0-9]*)
+
+ # Does the current valgrind error have a message yet?
+ case "$output" in
+ *.message)
+ test -z "$missing_message" &&
+ missing_message=$count
+ output=
+ esac
+
+ start_output $(basename $1)
+ echo "$line" |
+ sed 's/==[0-9]*==/==valgrind==/' >> $output
+ ;;
+ # end of backtrace
+ '}')
+ test -z "$output" || {
+ echo "$line" >> $output
+ test $output = ${output%.message} &&
+ output=$output.message
+ }
+ ;;
+ # end of test case
+ '')
+ finish_output
+ ;;
+ # normal line; if $output is set, print the line
+ *)
+ test -z "$output" || echo "$line" >> $output
+ ;;
+ esac
+ done < $1
+ IFS=$OLDIFS
+
+ # just to be safe
+ finish_output
+}
+
+for test_script in "$(dirname "$0")"/../test-results/*.out
+do
+ handle_one $test_script
+done
+
+output_all
diff --git a/t/valgrind/default.supp b/t/valgrind/default.supp
new file mode 100644
index 0000000..9e013fa
--- /dev/null
+++ b/t/valgrind/default.supp
@@ -0,0 +1,45 @@
+{
+ ignore-zlib-errors-cond
+ Memcheck:Cond
+ obj:*libz.so*
+}
+
+{
+ ignore-zlib-errors-value8
+ Memcheck:Value8
+ obj:*libz.so*
+}
+
+{
+ ignore-zlib-errors-value4
+ Memcheck:Value4
+ obj:*libz.so*
+}
+
+{
+ ignore-ldso-cond
+ Memcheck:Cond
+ obj:*ld-*.so
+}
+
+{
+ ignore-ldso-addr8
+ Memcheck:Addr8
+ obj:*ld-*.so
+}
+
+{
+ ignore-ldso-addr4
+ Memcheck:Addr4
+ obj:*ld-*.so
+}
+
+{
+ writing-data-from-zlib-triggers-even-more-errors
+ Memcheck:Param
+ write(buf)
+ obj:/lib/ld-*.so
+ fun:write_in_full
+ fun:write_buffer
+ fun:write_loose_object
+}
diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh
new file mode 100755
index 0000000..582b4dc
--- /dev/null
+++ b/t/valgrind/valgrind.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+base=$(basename "$0")
+
+TRACK_ORIGINS=
+
+VALGRIND_VERSION=$(valgrind --version)
+VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
+VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
+test 3 -gt "$VALGRIND_MAJOR" ||
+test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+TRACK_ORIGINS=--track-origins=yes
+
+exec valgrind -q --error-exitcode=126 \
+ --leak-check=no \
+ --suppressions="$GIT_VALGRIND/default.supp" \
+ --gen-suppressions=all \
+ $TRACK_ORIGINS \
+ --log-fd=4 \
+ --input-fd=4 \
+ $GIT_VALGRIND_OPTIONS \
+ "$GIT_VALGRIND"/../../"$base" "$@"